diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 103ea86efb..0000000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,7 +0,0 @@ -engines: - fixme: - enabled: true -ratings: - paths: [] -exclude_paths: -- "README.md" diff --git a/.editorconfig b/.editorconfig index 50339b8e81..b103fa6e4f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,3 +12,4 @@ insert_final_newline = true [*.md] trim_trailing_whitespace = false + diff --git a/.gitee/ISSUE_TEMPLATE.md b/.gitee/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..cdae693d35 --- /dev/null +++ b/.gitee/ISSUE_TEMPLATE.md @@ -0,0 +1,19 @@ +强烈建议大家到 `github` 相关页面提交问题,方便统一查询管理,具体页面地址:https://github.com/Wechat-Group/WxJava/issues + +当然如果必须在这里提问,请务必按以下格式填写,谢谢配合~ + +# 提问前,请确保阅读过项目首页说明以及wiki开发文档相关内容,尤其是常见问题部分。完成内容后,请务必移除包括本句在内的无用内容,以免影响阅读,谢谢合作~ +# 另外如果确认属于bug,而且已明确如何修复,请参考贡献指南直接提交PR,省的浪费时间在这里描述问题,非常感谢配合 + +### 简要描述 +__简单概括描述下你所遇到的问题。__ + +### 模块版本情况 +* `WxJava` 模块名: +* `WxJava` 版本号: + +### 详细描述 +__尽量详细描述。请不要使用截图,尽量使用文字描述,代码直接贴上来,日志则请附在后面所示区域。__ + +### 日志 +__将日志放在 [`Pastebin`](https://paste.ubuntu.com/) 或者其他地方,并将其url地址贴在这里__ diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..af87f48020 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [binarywang] +custom: https://github.com/Wechat-Group/WxJava/blob/master/images/qrcodes/wepay.jpg?raw=true diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..ca05962438 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug报告 +about: 如果发现Bug,请告诉我们,我们会尽快修复 +title: '' +labels: '' +assignees: '' + +--- + +# 提问前,请确保阅读过项目首页说明以及wiki开发文档相关内容,尤其是常见问题部分。完成内容后,请务必移除包括本句在内的无用内容,以免影响阅读,否则直接关闭,谢谢合作~ +# 另外如果确认属于bug,而且已明确如何修复,请参考贡献指南直接提交PR,省的浪费时间在这里描述问题,非常感谢配合 + +### 简要描述 +__简单概括描述下你所遇到的问题。__ + +### 模块版本情况 +* WxJava 模块名: +* WxJava 版本号: + +### 详细描述 +__尽量详细描述。请不要使用截图,尽量使用文字描述,代码直接贴上来,日志则请附在后面所示区域。__ + +### 日志 +__将日志放在 [pastebin](https://paste.ubuntu.com/) 或者其他地方,并将其url地址贴在这里__ diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..0834778f3d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: 请求添加新功能 +about: 如果有什么新功能需要添加,请告诉我们 +title: '' +labels: '' +assignees: '' + +--- + +# 提问前,请确保阅读过项目首页说明以及wiki开发文档相关内容,完成内容后,请务必移除包括本句在内的无用内容,以免影响他人阅读,否则直接关闭,谢谢合作~ + +### 简要描述 + + +### 官方文档地址 +__请提供所需功能对应的微信官方文档地址以便进行确认。__ diff --git a/.gitignore b/.gitignore index 4ee3979b32..db0804163d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ *.class test-output -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - # Package Files # *.jar *.war @@ -28,10 +25,6 @@ test-config.xml /gradlew **/build/ -# OSX -# Icon must end with two \r -Icon - ._* .DS_Store @@ -42,6 +35,7 @@ Icon .Spotlight-V100 .TemporaryItems .Trashes +.vscode .VolumeIcon.icns .AppleDB .AppleDesktop @@ -50,3 +44,9 @@ Temporary Items .apdisk /.sonar/ sonar-project.properties + +!/.mvn/wrapper/maven-wrapper.jar +*.versionsBackup + +# STS +.factorypath diff --git a/.travis.yml b/.travis.yml index 904ee9a47c..2b128c8a08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,18 @@ language: java -sudo: false -install: true -addons: - sonarqube: - token: - secure: "834110c7191f97ecb226970c46dcaff8e681da5a" + jdk: - - oraclejdk8 -#script: "mvn clean package -Dmaven.test.skip=true" - -script: - - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar - + - openjdk8 +script: "mvn clean package -DskipTests=true -Dcheckstyle.skip=true" + branches: only: - develop - + cache: directories: - '$HOME/.m2/repository' - - '$HOME/.sonar/cache' notifications: email: - - binaryw@qq.com + - binarywang@vip.qq.com diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..75db09403b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# 代码贡献指南 +1. 首先非常欢迎和感谢对本项目发起Pull Request的同学。 +1. **特别提示:请务必在develop分支提交PR,master分支目前仅是正式版的代码,即发布正式版本后才会从develop分支进行合并。** +1. 本项目代码风格为使用2个空格代表一个Tab,因此在提交代码时请注意一下,否则很容易在IDE格式化代码后与原代码产生大量diff,这样会给其他人阅读代码带来极大的困扰。 +1. 为了便于设置,本项目引入editorconfig支持,请使用Eclipse的同学在贡献代码前安装相关插件,而IntelliJ IDEA新版本自带支持,如果没有可自行安装插件。 +1. **提交代码前,请检查代码是否已经格式化,并且保证新增加或者修改的方法都有完整的参数说明,而public方法必须拥有相应的单元测试并通过测试。** +1. 本项目可以采用两种方式接受代码贡献: + - 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文,推荐使用此种方式贡献代码。 + - (***暂停此种方式,请使用第一种***)另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题。 + +### PR方式贡献代码步骤 +* 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/WxJava`,然后 `clone` 到本地,并设置用户信息。 + +```bash +$ git clone git@github.com:{your-github-username}/WxJava.git +$ cd WxJava +$ git config user.name "yourname" +$ git config user.email "your email" +``` +* 修改代码后提交,并推送到自己的仓库。 + +```bash +$ #do some change on the content +$ git commit -am "Fix issue #1: change something" +$ git push +``` +* 在 GitHub 网站上提交 Pull Request。 +* 定期使用项目仓库内容更新自己仓库内容。 + +```bash +$ git remote add upstream https://github.com/Wechat-Group/WxJava +$ git fetch upstream +$ git checkout develop +$ git rebase upstream/develop +$ git push -f origin develop +``` diff --git a/LICENSE b/LICENSE index 5c304d1a4a..0c8a80022e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,53 @@ Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000000..c5f82fc0e7 --- /dev/null +++ b/README.md @@ -0,0 +1,174 @@ +## WxJava - 微信开发 Java SDK(开发工具包) [![LICENSE](https://img.shields.io/badge/License-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) [![Badge](https://img.shields.io/badge/Link-996.icu-red.svg)](https://996.icu/#/zh_CN) + +[![码云Gitee](https://gitee.com/binary/weixin-java-tools/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools) +[![Github](http://github-svg-buttons.herokuapp.com/star.svg?user=Wechat-Group&repo=WxJava&style=flat&background=1081C1)](https://github.com/Wechat-Group/WxJava) +[![GitHub release](https://img.shields.io/github/release/Wechat-Group/WxJava.svg)](https://github.com/Wechat-Group/WxJava/releases) +[![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java) +[![Build Status](https://travis-ci.org/Wechat-Group/WxJava.svg?branch=develop)](https://travis-ci.org/Wechat-Group/WxJava) +[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=WxJava-weixin-java-tools) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +#### 支持包括微信支付、开放平台、公众号、企业微信/企业号、小程序等微信功能的后端开发。 + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +### 重要信息 +1. **2020-08-24 发布 [【3.9.0正式版】](https://mp.weixin.qq.com/s/xkT7P79SVwkpk85d-2fCUw)**! +1. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用 `maven` 或 `gradle` 引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;另外微信开发新手请务必阅读[【开发文档 Wiki 首页】](https://github.com/Wechat-Group/WxJava/wiki)的常见问题部分,可以少走很多弯路,节省不少时间。 +1. 技术交流群:想获得QQ群/微信群/钉钉企业群等信息的同学,请使用微信扫描上面的微信公众号二维码关注 `WxJava` 后点击相关菜单即可获取加入方式,同时也可以在微信中搜索 `weixin-java-tools` 或 `WxJava` 后选择正确的公众号进行关注,该公众号会及时通知SDK相关更新信息,并不定期分享微信Java开发相关技术知识; +1. 付费QQ群:(**注意:刚入群会有5分钟禁言,稍等片刻即可正常发言**) [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=731dc3e7ea31ebe25376cc1a791445468612c63fd0e9e05399b088ec81fd9e15) 或 [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://jq.qq.com/?_wv=1027&k=40lRskK),或者请自行搜索群号`343954419`进行添加;当然由于某种原因无法入群的,可关注公众号后获取其他群的加入方式; +1. 钉钉技术交流群: `30294972`(技术交流群),`35724728`(通知群,实时通知Github项目变更记录)。 +1. 微信开发新手或者Java开发新手在群内提问或新开Issue提问前,请先阅读[【提问的智慧】](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md),并确保已查阅过 [【开发文档Wiki】](https://github.com/wechat-group/WxJava/wiki) ,避免浪费大家的宝贵时间; +1. 寻求帮助时需贴代码或大长串异常信息的,请利用 http://paste.ubuntu.com + +-------------------------------- +### 其他说明 +1. **阅读源码的同学请注意,本SDK为简化代码编译时加入了`lombok`支持,如果不了解`lombok`的话,请先学习下相关知识,比如可以阅读[此文章](https://mp.weixin.qq.com/s/cUc-bUcprycADfNepnSwZQ);** +1. 如有新功能需求,发现BUG,或者由于微信官方接口调整导致的代码问题,可以直接在[【Issues】](https://github.com/Wechat-Group/WxJava/issues)页提出issue,便于讨论追踪问题; +1. 如果需要贡献代码,请务必在提交PR之前先仔细阅读[【代码贡献指南】](CONTRIBUTING.md),谢谢理解配合; +1. 目前本`SDK`最新版本要求的`JDK`最低版本是`8`,使用`7`的同学可以使用`WxJava` `3.8.0`及以前版本,而还在使用`JDK`6的用户请参考[【此项目】]( https://github.com/binarywang/weixin-java-tools-for-jdk6) ,而其他更早的JDK版本则需要自己改造实现。 +1. [本项目在开源中国的页面](https://www.oschina.net/p/weixin-java-tools-new),欢迎大家积极留言评分 🙂 +1. SDK开发文档请查阅 [【开发文档Wiki】](https://github.com/wechat-group/WxJava/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。 +1. **如果本开发工具包对您有所帮助,欢迎对我们的努力进行肯定,可以直接前往[【托管于码云的项目首页】](http://gitee.com/binary/weixin-java-tools),在页尾部分找到“捐助”按钮进行打赏,多多益善 😄。非常感谢各位打赏和捐助的同学!** +1. 各个模块的Javadoc可以在线查看:[weixin-java-miniapp](http://binary.ac.cn/weixin-java-miniapp-javadoc/)、[weixin-java-pay](http://binary.ac.cn/weixin-java-pay-javadoc/)、[weixin-java-mp](http://binary.ac.cn/weixin-java-mp-javadoc/)、[weixin-java-common](http://binary.ac.cn/weixin-java-common-javadoc/)、[weixin-java-cp](http://binary.ac.cn/weixin-java-cp-javadoc/)、[weixin-java-open](http://binary.ac.cn/weixin-java-open-javadoc/) +1. 本SDK项目在以下代码托管网站同步更新: +* 码云:https://gitee.com/binary/weixin-java-tools +* GitHub:https://github.com/wechat-group/WxJava + +--------------------------------- +### Maven 引用方式 +注意:最新版本(包括测试版)为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java),以下为最新正式版。 + +```xml + + com.github.binarywang + (不同模块参考下文) + 3.9.0 + +``` + + - 微信小程序:`weixin-java-miniapp` + - 微信支付:`weixin-java-pay` + - 微信开放平台:`weixin-java-open` + - 公众号(包括订阅号和服务号):`weixin-java-mp` + - 企业号/企业微信:`weixin-java-cp` + + +--------------------------------- +### 版本说明 + +
+点此展开查看 + +1. 本项目定为大约每两个月发布一次正式版(同时 `develop` 分支代码合并进入 `master` 分支),版本号格式为 `X.X.0`(如`2.1.0`,`2.2.0`等),遇到重大问题需修复会及时提交新版本,欢迎大家随时提交Pull Request; +1. BUG修复和新特性一般会先发布成小版本作为临时测试版本(如`3.6.8.B`,即尾号不为0,并添加B,以区别于正式版),代码仅存在于 `develop` 分支中; +1. 目前最新版本号为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java) ,也可以通过访问链接 [【微信支付】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-pay%22) 、[【微信小程序】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-miniapp%22) 、[【公众号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-mp%22) 、[【企业微信】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22)、[【开放平台】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-open%22) +分别查看所有最新的版本。 + +
+ +---------------------------------- +### 使用案例 +完整案例登记列表,请[【访问这里】](https://github.com/Wechat-Group/weixin-java-tools/issues/729)查看,欢迎登记更多的案例。 + +以下为部分案例列表: + +#### 开源项目: +- 基于微信公众号的签到、抽奖、发送弹幕程序:https://github.com/workcheng/weiya +- XxPay聚合支付:https://github.com/jmdhappy/xxpay-master +- 微同商城:https://gitee.com/fuyang_lipengjun/platform +- 微信点餐系统:https://github.com/sqmax/springboot-project +- 专注批量推送的小而美的工具:https://github.com/rememberber/WePush +- yshop意象商城系统:https://gitee.com/guchengwuyue/yshopmall +- wx-manage(微信公众号管理项目):https://github.com/niefy/wx-manage +- 基于若依开发的微信公众号管理系统:https://gitee.com/joolun/JooLun-wx + +#### 小程序: +- (京东)友家铺子,友家铺子店长版,京粉精选 +- [喵星人贴吧助手(扫码关注)](http://p98ahz3tg.bkt.clouddn.com/miniappqrcode.jpg) +- 树懒揽书+ +- 广廉快线,鹏城巴士等 +- 当燃挑战、sportlight轻灵运动 +- 360考试宝典 +- 民医台 +- 来一团商家版 +- 史必达(史丹利) +- 嘀嗒云印 +- 维沃吼吼 +- 王朝社区(比亚迪新能源社区) +- 极吼吼手机上门回收换新 + + +#### 公众号: +- 中国电信上海网厅(sh_189) +- E答平台 +- 宁夏生鲜365 +- 通服货滴 +- 神龙养车 +- 沃音乐商务智能 +- 光环云社群 +- 手机排队 +- [全民约跑健身便利店](http://www.oneminsport.com/) +- 民医台 +- YshopMall +- 好行景区直通车以及全国40多个公众号 +- 我奥篮球公众号 + +#### 企业号/企业微信: +- HTC企业微信 +- 掌上史丹利 + +#### 其他: +- 高善人力资源 +- 小猪餐餐 +- 餐饮系统 +- 微信公众号管理系统:http://demo.joolun.com +- 锐捷网络:Saleslink + +---------------------------------- +### 贡献者列表 +特别感谢参与贡献的所有同学,所有贡献者列表请在[此处](https://github.com/Wechat-Group/WxJava/graphs/contributors)查看,欢迎大家继续踊跃贡献代码! +
+点击此处展开查看贡献次数最多的几位同学 + +1. [chanjarster (Daniel Qian)](https://github.com/chanjarster) +1. [binarywang (Binary Wang)](https://github.com/binarywang) +1. [007gzs](https://github.com/007gzs) +1. [Silloy](https://github.com/silloy) +1. [mgcnrx11](https://github.com/mgcnrx11) +1. [yuanqixun](https://github.com/yuanqixun) +1. [kakotor](https://github.com/kakotor) +1. [aimilin6688 (Jonk)](https://github.com/aimilin6688) +1. [lkqm (Mario Luo)](https://github.com/lkqm) +1. [kareanyi (MillerLin)](https://github.com/kareanyi) + +
+ +### GitHub Stargazers over time + +[![Stargazers over time](https://starchart.cc/Wechat-Group/WxJava.svg)](https://starchart.cc/Wechat-Group/WxJava) diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 23ec2c52c9..0000000000 --- a/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -allprojects { - apply plugin: 'maven' - - group = 'com.github.binarywang' - version = '2.6.0' -} - -subprojects { - apply plugin: 'java' - sourceCompatibility = 1.7 - targetCompatibility = 1.7 - - - repositories { - mavenLocal() - maven { url "http://central.maven.org/maven2" } - //maven { url "http://maven.aliyun.com/nexus/content/groups/public" } - } - - - dependencies { - compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.10' - compile group: 'org.apache.httpcomponents', name: 'httpmime', version:'4.5' - compile group: 'org.apache.httpcomponents', name: 'httpclient', version:'4.5' - compile group: 'com.google.code.gson', name: 'gson', version:'2.7' - compile group: 'com.google.guava', name: 'guava', version:'19.0' - compile group: 'commons-codec', name: 'commons-codec', version:'1.10' - compile group: 'commons-io', name: 'commons-io', version:'2.5' - compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.4' - compile group: 'redis.clients', name: 'jedis', version:'2.9.0' - testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2' - } -} diff --git a/contribution.md b/contribution.md deleted file mode 100644 index cbcbb7dbb3..0000000000 --- a/contribution.md +++ /dev/null @@ -1,33 +0,0 @@ -# 代码贡献指南 -1. 非常欢迎和感谢对本项目发起Pull Request的同学,本项目代码风格为使用2个空格代表一个Tab,因此在提交代码时请注意一下,否则很容易在IDE格式化代码后与原代码产生大量diff,这样会给其他人阅读代码带来极大的困扰。为了便于设置,本项目引入editorconfig插件,请使用eclipse的同学在贡献代码前安装相关插件,IntelliJ IDEA新版本自带支持,如果没有可自行安装插件。 -1. 本项目可以采用两种方式接受代码贡献: - * 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。 - * 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员[![点击这里给我发消息](http://wpa.qq.com/pa?p=2:1211415707:51)](http://wpa.qq.com/msgrd?v=3&uin=1211415707&site=qq&menu=yes)。 - - -### PR方式贡献代码步骤 -* 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/weixin-java-tools`,然后 `clone` 到本地,并设置用户信息。 - -```bash -$ git clone git@github.com:my_user/weixin-java-tools.git -$ cd weixin-java-tools -$ git config user.name "yourname" -$ git config user.email "your email" -``` -* 修改代码后提交,并推送到自己的仓库。 - -```bash -$ #do some change on the content -$ git commit -am "Fix issue #1: change something" -$ git push -``` -* 在 GitHub 网站上提交 Pull Request。 -* 定期使用项目仓库内容更新自己仓库内容。 - -```bash -$ git remote add upstream https://github.com/wechat-group/weixin-java-tools -$ git fetch upstream -$ git checkout develop -$ git rebase upstream/develop -$ git push -f origin develop -``` diff --git a/demo.md b/demo.md new file mode 100644 index 0000000000..b1953e077c --- /dev/null +++ b/demo.md @@ -0,0 +1,22 @@ +## `Demo` 项目 + +### 说明 +1. 在码云和 `GitHub` 上均可访问,会尽量保持同步,请根据自己情况选用。 +1. 一般来说,`Github`上的版本应该是最新的,但也有可能没及时同步,此种情况下请以 `Github` 上的版本为准,有问题也请在 `Github` 对应项目 `Issues` 页面提问)。 +1. 欢迎提供更多的 demo 实现。 + +### `Spring Boot Starter` 实现 +- 微信支付:[点击查看使用方法](https://github.com/Wechat-Group/WxJava/tree/master/spring-boot-starters/wx-java-pay-spring-boot-starter) +- 微信公众号:[点击查看使用方法](https://github.com/Wechat-Group/WxJava/tree/master/spring-boot-starters/wx-java-mp-spring-boot-starter) ,[使用该 starter 实现的公众号 Demo](https://github.com/binarywang/wx-java-mp-demo) [![Build Status](https://travis-ci.org/binarywang/wx-java-mp-demo.svg?branch=master)](https://travis-ci.org/binarywang/wx-java-mp-demo) +- 微信小程序:[点击查看使用方法](https://github.com/Wechat-Group/WxJava/tree/master/spring-boot-starters/wx-java-miniapp-spring-boot-starter) + +### Demo 列表 +1. 微信支付 Demo:[GitHub](http://github.com/binarywang/weixin-java-pay-demo)、[码云](http://gitee.com/binary/weixin-java-pay-demo) [![Build Status](https://travis-ci.org/binarywang/weixin-java-pay-demo.svg?branch=master)](https://travis-ci.org/binarywang/weixin-java-pay-demo) +1. 企业号/企业微信 Demo:[GitHub](http://github.com/binarywang/weixin-java-cp-demo)、[码云](http://gitee.com/binary/weixin-java-cp-demo) [![Build Status](https://travis-ci.org/binarywang/weixin-java-cp-demo.svg?branch=master)](https://travis-ci.org/binarywang/weixin-java-cp-demo) +1. 微信小程序 Demo:[GitHub](http://github.com/binarywang/weixin-java-miniapp-demo)、[码云](http://gitee.com/binary/weixin-java-miniapp-demo) [![Build Status](https://travis-ci.org/binarywang/weixin-java-miniapp-demo.svg?branch=master)](https://travis-ci.org/binarywang/weixin-java-miniapp-demo) +1. 开放平台 Demo:[GitHub](http://github.com/Wechat-Group/weixin-java-open-demo)、[码云](http://gitee.com/binary/weixin-java-open-demo) [![Build Status](https://travis-ci.org/Wechat-Group/weixin-java-open-demo.svg?branch=master)](https://travis-ci.org/Wechat-Group/weixin-java-open-demo) +1. 公众号 Demo: + - 使用 `Spring MVC` 实现的公众号 Demo:[GitHub](http://github.com/binarywang/weixin-java-mp-demo-springmvc)、[码云](https://gitee.com/binary/weixin-java-mp-demo) [![Build Status](https://travis-ci.org/binarywang/weixin-java-mp-demo-springmvc.svg?branch=master)](https://travis-ci.org/binarywang/weixin-java-mp-demo-springmvc) + - 使用 `Spring Boot` 实现的公众号 Demo(支持多公众号):[GitHub](http://github.com/binarywang/weixin-java-mp-demo-springboot)、[码云](http://gitee.com/binary/weixin-java-mp-demo-springboot) [![Build Status](https://travis-ci.org/binarywang/weixin-java-mp-demo-springboot.svg?branch=master)](https://travis-ci.org/binarywang/weixin-java-mp-demo-springboot) + - 含公众号和部分微信支付代码的 Demo:[GitHub](http://github.com/Wechat-Group/weixin-java-demo-springmvc)、[码云](http://gitee.com/binary/weixin-java-tools-springmvc) [![Build Status](https://travis-ci.org/Wechat-Group/weixin-java-demo-springmvc.svg?branch=master)](https://travis-ci.org/Wechat-Group/weixin-java-demo-springmvc) + diff --git a/images/banners/aliyun.jpg b/images/banners/aliyun.jpg new file mode 100644 index 0000000000..c4b0ba0ebe Binary files /dev/null and b/images/banners/aliyun.jpg differ diff --git a/images/banners/coding.jpg b/images/banners/coding.jpg new file mode 100644 index 0000000000..23e788e7ba Binary files /dev/null and b/images/banners/coding.jpg differ diff --git a/images/banners/planB.jpg b/images/banners/planB.jpg new file mode 100644 index 0000000000..139957fbef Binary files /dev/null and b/images/banners/planB.jpg differ diff --git a/images/banners/tcloud.jpg b/images/banners/tcloud.jpg new file mode 100644 index 0000000000..341032cd41 Binary files /dev/null and b/images/banners/tcloud.jpg differ diff --git a/images/banners/vultr.jpg b/images/banners/vultr.jpg new file mode 100644 index 0000000000..80cf3c2b5e Binary files /dev/null and b/images/banners/vultr.jpg differ diff --git a/images/banners/wiki.jpg b/images/banners/wiki.jpg new file mode 100644 index 0000000000..d36d641934 Binary files /dev/null and b/images/banners/wiki.jpg differ diff --git a/images/qrcodes/alipay.jpg b/images/qrcodes/alipay.jpg new file mode 100644 index 0000000000..09f6854274 Binary files /dev/null and b/images/qrcodes/alipay.jpg differ diff --git a/images/qrcodes/cp.png b/images/qrcodes/cp.png new file mode 100644 index 0000000000..69e716ec29 Binary files /dev/null and b/images/qrcodes/cp.png differ diff --git a/images/qrcodes/ding.jpg b/images/qrcodes/ding.jpg new file mode 100644 index 0000000000..2ed374a937 Binary files /dev/null and b/images/qrcodes/ding.jpg differ diff --git a/images/qrcodes/mp.png b/images/qrcodes/mp.png new file mode 100644 index 0000000000..815857229e Binary files /dev/null and b/images/qrcodes/mp.png differ diff --git a/images/qrcodes/wepay.jpg b/images/qrcodes/wepay.jpg new file mode 100644 index 0000000000..98c77031e4 Binary files /dev/null and b/images/qrcodes/wepay.jpg differ diff --git a/others/.mvn/wrapper/maven-wrapper.properties b/others/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..a447c9fa81 --- /dev/null +++ b/others/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip \ No newline at end of file diff --git a/others/check-dependency-updates.sh b/others/check-dependency-updates.sh new file mode 100644 index 0000000000..13e8f0ef30 --- /dev/null +++ b/others/check-dependency-updates.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +./mvnw org.codehaus.mojo:versions-maven-plugin:display-dependency-updates diff --git a/others/check-plugin-updates.sh b/others/check-plugin-updates.sh new file mode 100644 index 0000000000..0ce88b4df6 --- /dev/null +++ b/others/check-plugin-updates.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +./mvnw org.codehaus.mojo:versions-maven-plugin:display-plugin-updates diff --git a/others/check-property-updates.sh b/others/check-property-updates.sh new file mode 100644 index 0000000000..4dd46a17d1 --- /dev/null +++ b/others/check-property-updates.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +./mvnw org.codehaus.mojo:versions-maven-plugin:display-property-updates diff --git a/others/mvnw b/others/mvnw new file mode 100644 index 0000000000..6ecc150ae0 --- /dev/null +++ b/others/mvnw @@ -0,0 +1,236 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/others/mvnw.cmd b/others/mvnw.cmd new file mode 100644 index 0000000000..8bb8275416 --- /dev/null +++ b/others/mvnw.cmd @@ -0,0 +1,146 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/weixin-java-osgi/pom.xml b/others/weixin-java-osgi/pom.xml similarity index 92% rename from weixin-java-osgi/pom.xml rename to others/weixin-java-osgi/pom.xml index b44ef85d7e..039e32e734 100644 --- a/weixin-java-osgi/pom.xml +++ b/others/weixin-java-osgi/pom.xml @@ -1,18 +1,18 @@ - 4.0.0 com.github.binarywang - weixin-java-parent - 2.5.2.BETA + wx-java + 2.6.0 weixin-java-osgi bundle - WeiXin Java Tools - OSGI - 微信公众号Java SDK OSGI + WxJava - OSGI + 微信Java开发SDK OSGI模块 @@ -28,7 +28,7 @@ com.thoughtworks.xstream xstream - 1.4.7 + 1.4.10-java7 provided @@ -119,7 +119,8 @@ ,org.joda.time.format;version="[1.6,2)";resolution:=optional ,org.kxml2.io;resolution:=optional ,org.w3c.dom;resolution:=optional - ,* + ,* + diff --git a/pom.xml b/pom.xml index 2447fd71d4..5b23cb58c7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,15 +1,16 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> 4.0.0 com.github.binarywang - weixin-java-parent - 2.6.0 + wx-java + 3.9.0 pom - WeiXin Java Tools - Parent - 微信公众号、企业号上级POM - https://github.com/wechat-group/weixin-java-tools + WxJava - Weixin/Wechat Java SDK + 微信开发Java SDK + https://github.com/Wechat-Group/WxJava @@ -74,113 +75,150 @@ aimilin@yeah.net https://github.com/aimilin6688 + + ecoolper + crskyp@gmail.com + https://github.com/crskyp + + + 007 + 007gzs@gmail.com + https://github.com/007gzs + + + Howard Liu + liuxinghao1988@gmail.com + https://github.com/howardliu-cn + + + huangxiaoming + huangxm129@163.com + https://github.com/huangxm129 + - scm:git:https://github.com/wechat-group/weixin-java-tools.git - scm:git:git@github.com:wechat-group/weixin-java-tools.git - https://github.com/wechat-group/weixin-java-tools + scm:git:https://github.com/Wechat-Group/WxJava.git + scm:git:git@github.com:Wechat-Group/WxJava.git + https://github.com/Wechat-Group/WxJava + weixin-graal weixin-java-common weixin-java-cp weixin-java-mp weixin-java-pay + weixin-java-miniapp + weixin-java-open + spring-boot-starters - 1.7 - 1.7 + 1.8 + 1.8 UTF-8 - true - true 4.5 - 1.7.10 - 1.1.2 - 2.7 - 19.0 - 3.5 - 2.5 - 1.10 - 9.3.0.RC0 - 2.9.0 + 9.4.31.v20200723 - - - org.slf4j - slf4j-api - ${slf4j.version} - - - com.thoughtworks.xstream - xstream - 1.4.9 - - - ch.qos.logback - logback-classic - ${logback.version} - test - - - org.apache.httpcomponents - httpclient - ${httpclient.version} - - - org.apache.httpcomponents - httpmime - ${httpclient.version} - - - com.google.code.gson - gson - ${gson.version} - - - commons-codec - commons-codec - ${commons-codec.version} - - - commons-io - commons-io - ${commons-io.version} - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - - com.google.guava - guava - ${guava.version} - - - + + com.github.binarywang + qrcode-utils + 1.1 + + + org.jodd + jodd-http + 5.1.6 + provided + + + com.squareup.okhttp3 + okhttp + 4.5.0 + provided + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + org.apache.httpcomponents + httpmime + ${httpclient.version} + + + commons-codec + commons-codec + 1.10 + + + commons-io + commons-io + 2.5 + + + org.apache.commons + commons-lang3 + 3.10 + + + org.slf4j + slf4j-api + 1.7.24 + + + com.thoughtworks.xstream + xstream + 1.4.11.1 + + + com.google.guava + guava + 29.0-android + + + com.google.code.gson + gson + 2.8.0 + + + + + joda-time + joda-time + 2.10.6 + test + + + ch.qos.logback + logback-classic + 1.2.3 + test + com.google.inject guice - 3.0 + 4.2.3 test org.testng testng - 6.8.7 + 7.1.0 test org.mockito mockito-all - 1.9.5 + 1.10.19 test @@ -195,10 +233,44 @@ ${jetty.version} test + + org.assertj + assertj-guava + 3.0.0 + test + + redis.clients jedis - ${jedis.version} + 3.3.0 + provided + + + com.github.jedis-lock + jedis-lock + 1.0.0 + provided + + + org.redisson + redisson + 3.12.0 + true + provided + + + org.springframework.data + spring-data-redis + 1.8.23.RELEASE + true + provided + + + org.projectlombok + lombok + 1.18.8 + provided @@ -263,7 +335,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 1.6 sign-artifacts @@ -277,6 +349,14 @@ + + + native-image + + false + + + @@ -318,11 +398,23 @@ org.apache.maven.plugins - maven-compiler-plugin - 3.6.0 + maven-checkstyle-plugin + 2.17 - UTF-8 + true + quality-checks/google_checks.xml + true + true + true + + + verify + + check + + + diff --git a/quality-checks/google_checks.xml b/quality-checks/google_checks.xml new file mode 100644 index 0000000000..1f76bfaf07 --- /dev/null +++ b/quality-checks/google_checks.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/readme.md b/readme.md deleted file mode 100644 index b55333188b..0000000000 --- a/readme.md +++ /dev/null @@ -1,98 +0,0 @@ -微信支付、公众号&企业号开发Java SDK ---------------------------------- -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent) -[![Build Status](https://travis-ci.org/Wechat-Group/weixin-java-tools.svg?branch=develop)](https://travis-ci.org/Wechat-Group/weixin-java-tools) - -### 注意事项: -1. 声明: ***本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。*** -1. **新手请注意,本项目仅是一个开发工具包(即SDK),未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考下文中提到的Demo项目或本项目中的部分单元测试代码;如果没有贡献代码的意愿,不建议下载项目的源码自行编译,因为如果想看源码使用maven也是可以下载源码的**; -1. 最新更新:**2017-4-13 发布[【2.6.0正式版】](https://github.com/Wechat-Group/weixin-java-tools/releases)**! -1. 开源中国网站的本项目介绍的首页链接地址:https://www.oschina.net/p/weixin-java-tools-new -1. 自2.0.0版本以来,公众号的接口调整比较大,主要是为了解决主接口类过于庞大不方便管理的问题,将接口实现代码按模块进行拆分。 -1. 自2.6.0版本开始,微信支付相关功能抽出独立为一个模块,详细使用方式请参考相关demo; -1. SDK详细开发文档请查阅 [【Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。***另外微信开发新手请务必阅读wiki首页的常见问题部分,可以少走很多弯路,节省不少时间。*** -1. 各个模块的Javadoc可以在线查看(有可能是最新的测试版本的,请注意观察版本号):[weixin-java-pay](https://binarywang.github.io/weixin-java-pay-javadoc/)、[weixin-java-mp](https://binarywang.github.io/weixin-java-mp-javadoc/)、[weixin-java-common](https://binarywang.github.io/weixin-java-common-javadoc/)、[weixin-java-cp](https://binarywang.github.io/weixin-java-cp-javadoc/) -1. 本SDK要求的最低JDK版本是7,还在使用JDK6的用户请参考[【此项目】]( https://github.com/binarywang/weixin-java-tools-for-jdk6) ,而其他更早的JDK版本则需要自己改造实现。 -1. 如有新功能需求,发现BUG,或者由于微信官方接口调整导致的代码问题,可以直接在[【Issues】](https://github.com/Wechat-Group/weixin-java-tools/issues)页提出issue,便于讨论追踪问题; -1. 如果想贡献代码,请阅读[【代码贡献指南】](contribution.md); -1. 捐助渠道已开通,如有意向请前往托管于码云的项目首页(具体地址见下文)的页面评论区上方,可以找到“捐助”按钮,非常感谢各位捐助的同学! - ---------------------------------- -## SDK使用交流方式说明: -1. QQ群: [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=731dc3e7ea31ebe25376cc1a791445468612c63fd0e9e05399b088ec81fd9e15) 或 [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://jq.qq.com/?_wv=1027&k=40lRskK),推荐点击按钮入群,当然如果无法成功操作,请自行搜索群号343954419进行添加 ) -1. 由于群容量有限,即将爆满,故开启付费入群模式以保证只有真实交流需求的人进入,如果确实因为各种原因无法付费入群的,请联系群主说明原因即可入群;并为保证群的活跃度,本群将不定期清理长时间不活跃的同学; -1. 微信群: 因微信群已达到100人限制,故如有想加入微信群的,请入QQ群后联系管理员,提供微信号以便邀请加入; -1. 新手提问前,请先阅读此文章:http://t.cn/RV93MRB -1. 寻求帮助时需贴代码或大长串异常信息的,请利用http://paste.ubuntu.com - ---------------------------------- -## 版本说明 -1. 本项目定为大约每两个月发布一次正式版,版本号格式为X.X.0(如2.1.0,2.2.0等),遇到重大问题需修复会及时提交新版本,欢迎大家随时提交Pull Request; -1. BUG修复和新特性一般会先发布成小版本作为临时测试版本(如2.4.5.BETA,2.4.6.BETA等,即尾号不为0,并添加BETA字样,以区别于正式版); -1. 目前最新版本号为 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent) ,也可以通过访问链接 [【微信支付】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-pay%22) 、[【公众号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-mp%22) 、[【企业号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22) -分别查看所有最新的版本。 - ---------------------------------- -#### 本项目在几个著名的代码托管网站同步更新,地址分别是: -* 码云:http://git.oschina.net/binary/weixin-java-tools -* GitHub: https://github.com/wechat-group/weixin-java-tools -* Bitbucket:https://bitbucket.org/binarywang/weixin-java-tools -* Coding: https://git.coding.net/binarywang/weixin-java-tools.git - ---------------------------------- -## 可参考的Demo项目 -#### 欢迎提供更多的Demo供新手参考: -* https://github.com/wechat-group/weixin-java-mp-demo (公众号Demo,使用Spring MVC实现) -* https://github.com/wechat-group/weixin-java-mp-multi-demo (支持多公众号) -* https://github.com/wechat-group/weixin-java-tools-springmvc (公众号Demo,内含部分微信支付代码) -* https://github.com/wechat-group/weixin-java-mp-demo-springboot (公众号Demo,使用Spring Boot实现) -* https://github.com/wechat-group/weixin-java-pay-demo (微信支付demo) -* https://github.com/wechat-group/weixin-java-cp-demo (企业号demo,筹备中) - ---------------------------------- -## Maven & Gradle 最新正式版本 - -* 微信支付: - -maven: -```xml - - com.github.binarywang - weixin-java-pay - 2.6.0 - -``` -gradle: -```groovy -compile 'com.github.binarywang:weixin-java-pay:2.6.0' -``` - -* 公众号(订阅号及服务号): - -maven: -```xml - - com.github.binarywang - weixin-java-mp - 2.6.0 - -``` -gradle: -```groovy -compile 'com.github.binarywang:weixin-java-mp:2.6.0' -``` - -* 企业号: - -maven: -```xml - - com.github.binarywang - weixin-java-cp - 2.6.0 - -``` -gradle: -```groovy -compile 'com.github.binarywang:weixin-java-cp:2.6.0' -``` diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 1c0e0214c4..0000000000 --- a/settings.gradle +++ /dev/null @@ -1,8 +0,0 @@ -rootProject.name = 'weixin-java-parent' -include ':weixin-java-common' -include ':weixin-java-cp' -include ':weixin-java-mp' - -project(':weixin-java-common').projectDir = "$rootDir/weixin-java-common" as File -project(':weixin-java-cp').projectDir = "$rootDir/weixin-java-cp" as File -project(':weixin-java-mp').projectDir = "$rootDir/weixin-java-mp" as File \ No newline at end of file diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml new file mode 100644 index 0000000000..3ec16a2274 --- /dev/null +++ b/spring-boot-starters/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.github.binarywang + wx-java + 3.9.0 + + pom + wx-java-spring-boot-starters + WxJava - Spring Boot Starters + WxJava 各个模块的 Spring Boot Starter + + + 2.1.4.RELEASE + + + + wx-java-miniapp-spring-boot-starter + wx-java-mp-spring-boot-starter + wx-java-pay-spring-boot-starter + wx-java-open-spring-boot-starter + + + + + org.springframework.boot + spring-boot-autoconfigure + ${spring.boot.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring.boot.version} + true + + + org.projectlombok + lombok + provided + + + diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md new file mode 100644 index 0000000000..82f6bdd8b1 --- /dev/null +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md @@ -0,0 +1,35 @@ +# wx-java-miniapp-spring-boot-starter +## 快速开始 +1. 引入依赖 + ```xml + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + ${version} + + ``` +2. 添加配置(application.properties) + ```properties + # 公众号配置(必填) + wx.miniapp.appid = appId + wx.miniapp.secret = @secret + wx.miniapp.token = @token + wx.miniapp.aesKey = @aesKey + wx.miniapp.msgDataFormat = @msgDataFormat # 消息格式,XML或者JSON. + # 存储配置redis(可选) + # 注意: 指定redis.host值后不会使用容器注入的redis连接(JedisPool) + wx.miniapp.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate + wx.miniapp.config-storage.key-prefix = wa # 相关redis前缀配置: wa(默认) + wx.miniapp.config-storage.redis.host = 127.0.0.1 + wx.miniapp.config-storage.redis.port = 6379 + # http客户端配置 + wx.miniapp.config-storage.http-client-type=HttpClient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp + wx.miniapp.config-storage.http-proxy-host= + wx.miniapp.config-storage.http-proxy-port= + wx.miniapp.config-storage.http-proxy-username= + wx.miniapp.config-storage.http-proxy-password= + ``` +3. 自动注入的类型 +- `WxMaService` +- `WxMaConfig` + diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml new file mode 100644 index 0000000000..b6e5904acd --- /dev/null +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml @@ -0,0 +1,56 @@ + + + + wx-java-spring-boot-starters + com.github.binarywang + 3.9.0 + + 4.0.0 + + wx-java-miniapp-spring-boot-starter + WxJava - Spring Boot Starter for MiniApp + 微信小程序开发的 Spring Boot Starter + + + + com.github.binarywang + weixin-java-miniapp + ${project.version} + + + redis.clients + jedis + + + org.springframework.data + spring-data-redis + ${spring.boot.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaAutoConfiguration.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaAutoConfiguration.java new file mode 100644 index 0000000000..7c727c9687 --- /dev/null +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaAutoConfiguration.java @@ -0,0 +1,138 @@ +package com.binarywang.spring.starter.wxjava.miniapp.config; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; +import com.binarywang.spring.starter.wxjava.miniapp.enums.HttpClientType; +import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.redis.JedisWxRedisOps; +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +/** + * 自动配置. + * + * @author Binary Wang + * @date 2019-08-10 + */ +@AllArgsConstructor +@Configuration +@ConditionalOnClass(WxMaService.class) +@EnableConfigurationProperties(WxMaProperties.class) +@ConditionalOnProperty(prefix = "wx.miniapp", value = "enabled", matchIfMissing = true) +public class WxMaAutoConfiguration { + + private final WxMaProperties wxMaProperties; + private final ApplicationContext applicationContext; + + /** + * 小程序service. + * + * @return 小程序service + */ + @Bean + @ConditionalOnMissingBean(WxMaService.class) + public WxMaService service(WxMaConfig wxMaConfig) { + HttpClientType httpClientType = wxMaProperties.getConfigStorage().getHttpClientType(); + WxMaService wxMaService; + if (httpClientType == HttpClientType.OkHttp) { + wxMaService = new WxMaServiceOkHttpImpl(); + } else if (httpClientType == HttpClientType.JoddHttp) { + wxMaService = new WxMaServiceJoddHttpImpl(); + } else if (httpClientType == HttpClientType.HttpClient) { + wxMaService = new WxMaServiceHttpClientImpl(); + } else { + wxMaService = new WxMaServiceImpl(); + } + wxMaService.setWxMaConfig(wxMaConfig); + return wxMaService; + } + + @Bean + @ConditionalOnMissingBean(WxMaConfig.class) + public WxMaConfig wxMaConfig() { + WxMaDefaultConfigImpl config; + switch (wxMaProperties.getConfigStorage().getType()) { + case Jedis: + config = wxMaJedisConfigStorage(); + break; + case RedisTemplate: + config = wxMaRedisTemplateConfigStorage(); + break; + default: + config = wxMaDefaultConfigStorage(); + break; + } + + config.setAppid(StringUtils.trimToNull(this.wxMaProperties.getAppid())); + config.setSecret(StringUtils.trimToNull(this.wxMaProperties.getSecret())); + config.setToken(StringUtils.trimToNull(this.wxMaProperties.getToken())); + config.setAesKey(StringUtils.trimToNull(this.wxMaProperties.getAesKey())); + config.setMsgDataFormat(StringUtils.trimToNull(this.wxMaProperties.getMsgDataFormat())); + + WxMaProperties.ConfigStorage configStorageProperties = wxMaProperties.getConfigStorage(); + config.setHttpProxyHost(configStorageProperties.getHttpProxyHost()); + config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername()); + config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword()); + if (configStorageProperties.getHttpProxyPort() != null) { + config.setHttpProxyPort(configStorageProperties.getHttpProxyPort()); + } + return config; + } + + private WxMaDefaultConfigImpl wxMaDefaultConfigStorage() { + return new WxMaDefaultConfigImpl(); + } + + private WxMaDefaultConfigImpl wxMaJedisConfigStorage() { + WxMaProperties.RedisProperties redisProperties = wxMaProperties.getConfigStorage().getRedis(); + JedisPool jedisPool; + if (StringUtils.isNotEmpty(redisProperties.getHost())) { + JedisPoolConfig config = new JedisPoolConfig(); + if (redisProperties.getMaxActive() != null) { + config.setMaxTotal(redisProperties.getMaxActive()); + } + if (redisProperties.getMaxIdle() != null) { + config.setMaxIdle(redisProperties.getMaxIdle()); + } + if (redisProperties.getMaxWaitMillis() != null) { + config.setMaxWaitMillis(redisProperties.getMaxWaitMillis()); + } + if (redisProperties.getMinIdle() != null) { + config.setMinIdle(redisProperties.getMinIdle()); + } + config.setTestOnBorrow(true); + config.setTestWhileIdle(true); + + jedisPool = new JedisPool(config, redisProperties.getHost(), redisProperties.getPort(), + redisProperties.getTimeout(), redisProperties.getPassword(), redisProperties.getDatabase()); + } else { + jedisPool = applicationContext.getBean(JedisPool.class); + } + WxRedisOps redisOps = new JedisWxRedisOps(jedisPool); + return new WxMaRedisBetterConfigImpl(redisOps, wxMaProperties.getConfigStorage().getKeyPrefix()); + } + + private WxMaDefaultConfigImpl wxMaRedisTemplateConfigStorage() { + StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class); + WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); + return new WxMaRedisBetterConfigImpl(redisOps, wxMaProperties.getConfigStorage().getKeyPrefix()); + } +} diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/HttpClientType.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/HttpClientType.java new file mode 100644 index 0000000000..52a53debdc --- /dev/null +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/HttpClientType.java @@ -0,0 +1,22 @@ +package com.binarywang.spring.starter.wxjava.miniapp.enums; + +/** + * httpclient类型. + * + * @author Binary Wang + * @date 2020-05-25 + */ +public enum HttpClientType { + /** + * HttpClient. + */ + HttpClient, + /** + * OkHttp. + */ + OkHttp, + /** + * JoddHttp. + */ + JoddHttp, +} diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/StorageType.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/StorageType.java new file mode 100644 index 0000000000..9328980bb2 --- /dev/null +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/StorageType.java @@ -0,0 +1,22 @@ +package com.binarywang.spring.starter.wxjava.miniapp.enums; + +/** + * storage类型. + * + * @author Binary Wang + * @date 2020-05-25 + */ +public enum StorageType { + /** + * 内存. + */ + Memory, + /** + * redis(JedisClient). + */ + Jedis, + /** + * redis(RedisTemplate). + */ + RedisTemplate +} diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java new file mode 100644 index 0000000000..0139215ea7 --- /dev/null +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java @@ -0,0 +1,124 @@ +package com.binarywang.spring.starter.wxjava.miniapp.properties; + +import com.binarywang.spring.starter.wxjava.miniapp.enums.HttpClientType; +import com.binarywang.spring.starter.wxjava.miniapp.enums.StorageType; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 属性配置类. + * + * @author Binary Wang + * @date 2019-08-10 + */ +@Data +@ConfigurationProperties(prefix = "wx.miniapp") +public class WxMaProperties { + /** + * 设置微信小程序的appid. + */ + private String appid; + + /** + * 设置微信小程序的Secret. + */ + private String secret; + + /** + * 设置微信小程序消息服务器配置的token. + */ + private String token; + + /** + * 设置微信小程序消息服务器配置的EncodingAESKey. + */ + private String aesKey; + + /** + * 消息格式,XML或者JSON. + */ + private String msgDataFormat; + + /** + * 存储策略 + */ + private final ConfigStorage configStorage = new ConfigStorage(); + + @Data + public static class ConfigStorage { + + /** + * 存储类型. + */ + private StorageType type = StorageType.Memory; + + /** + * 指定key前缀. + */ + private String keyPrefix = "wa"; + + /** + * redis连接配置. + */ + private final RedisProperties redis = new RedisProperties(); + + /** + * http客户端类型. + */ + private HttpClientType httpClientType = HttpClientType.HttpClient; + + /** + * http代理主机. + */ + private String httpProxyHost; + + /** + * http代理端口. + */ + private Integer httpProxyPort; + + /** + * http代理用户名. + */ + private String httpProxyUsername; + + /** + * http代理密码. + */ + private String httpProxyPassword; + } + + @Data + public static class RedisProperties { + + /** + * 主机地址.不填则从spring容器内获取JedisPool + */ + private String host; + + /** + * 端口号. + */ + private int port = 6379; + + /** + * 密码. + */ + private String password; + + /** + * 超时. + */ + private int timeout = 2000; + + /** + * 数据库. + */ + private int database = 0; + + private Integer maxActive; + private Integer maxIdle; + private Integer maxWaitMillis; + private Integer minIdle; + } +} diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..4bb1c3d604 --- /dev/null +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.miniapp.config.WxMaAutoConfiguration diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md b/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md new file mode 100644 index 0000000000..adc3a31f46 --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md @@ -0,0 +1,40 @@ +# wx-java-mp-spring-boot-starter +## 快速开始 +1. 引入依赖 + ```xml + + com.github.binarywang + wx-java-mp-spring-boot-starter + ${version} + + ``` +2. 添加配置(application.properties) + ```properties + # 公众号配置(必填) + wx.mp.appId = appId + wx.mp.secret = @secret + wx.mp.token = @token + wx.mp.aesKey = @aesKey + # 存储配置redis(可选) + wx.mp.config-storage.type = redis # 配置类型: memory(默认), redis, jedis, redistemplate + wx.mp.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认) + wx.mp.config-storage.redis.host = 127.0.0.1 + wx.mp.config-storage.redis.port = 6379 + # http客户端配置 + wx.mp.config-storage.http-client-type=httpclient # http客户端类型: httpclient(默认), okhttp, joddhttp + wx.mp.config-storage.http-proxy-host= + wx.mp.config-storage.http-proxy-port= + wx.mp.config-storage.http-proxy-username= + wx.mp.config-storage.http-proxy-password= + ``` +3. 自动注入的类型 +- `WxMpService` +- `WxMpConfigStorage` + +4、参考demo: +https://github.com/binarywang/wx-java-mp-demo + + + + + diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml new file mode 100644 index 0000000000..7e59fc8c83 --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml @@ -0,0 +1,58 @@ + + + + wx-java-spring-boot-starters + com.github.binarywang + 3.9.0 + + 4.0.0 + + wx-java-mp-spring-boot-starter + WxJava - Spring Boot Starter for MP + 微信公众号开发的 Spring Boot Starter + + + + com.github.binarywang + weixin-java-mp + ${project.version} + + + redis.clients + jedis + compile + + + org.springframework.data + spring-data-redis + ${spring.boot.version} + provided + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpAutoConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpAutoConfiguration.java new file mode 100644 index 0000000000..e2b0a60d2b --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpAutoConfiguration.java @@ -0,0 +1,17 @@ +package com.binarywang.spring.starter.wxjava.mp.config; + +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * . + * + * @author someone + */ +@Configuration +@EnableConfigurationProperties(WxMpProperties.class) +@Import({WxMpStorageAutoConfiguration.class, WxMpServiceAutoConfiguration.class}) +public class WxMpAutoConfiguration { +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java new file mode 100644 index 0000000000..1c942bbfa2 --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java @@ -0,0 +1,58 @@ +package com.binarywang.spring.starter.wxjava.mp.config; + +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import me.chanjar.weixin.common.api.WxOcrService; +import me.chanjar.weixin.mp.api.*; +import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 微信公众号相关服务自动注册. + * + * @author someone + */ +@Configuration +public class WxMpServiceAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpProperties wxMpProperties) { + WxMpProperties.HttpClientType httpClientType = wxMpProperties.getConfigStorage().getHttpClientType(); + WxMpService wxMpService; + if (httpClientType == WxMpProperties.HttpClientType.okhttp) { + wxMpService = newWxMpServiceOkHttpImpl(); + } else if (httpClientType == WxMpProperties.HttpClientType.joddhttp) { + wxMpService = newWxMpServiceJoddHttpImpl(); + } else if (httpClientType == WxMpProperties.HttpClientType.httpclient) { + wxMpService = newWxMpServiceHttpClientImpl(); + } else { + wxMpService = newWxMpServiceImpl(); + } + + wxMpService.setWxMpConfigStorage(configStorage); + return wxMpService; + } + + private WxMpService newWxMpServiceImpl() { + return new WxMpServiceImpl(); + } + + private WxMpService newWxMpServiceHttpClientImpl() { + return new WxMpServiceHttpClientImpl(); + } + + private WxMpService newWxMpServiceOkHttpImpl() { + return new WxMpServiceOkHttpImpl(); + } + + private WxMpService newWxMpServiceJoddHttpImpl() { + return new WxMpServiceJoddHttpImpl(); + } + +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java new file mode 100644 index 0000000000..fc15e7605e --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java @@ -0,0 +1,121 @@ +package com.binarywang.spring.starter.wxjava.mp.config; + +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.redis.JedisWxRedisOps; +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; +import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +/** + * 微信公众号存储策略自动配置. + * + * @author someone + */ +@Configuration +@RequiredArgsConstructor +public class WxMpStorageAutoConfiguration { + + private final ApplicationContext applicationContext; + + private final WxMpProperties wxMpProperties; + + @Value("${wx.mp.config-storage.redis.host:") + private String redisHost; + + @Value("${wx.mp.configStorage.redis.host:") + private String redisHost2; + + @Bean + @ConditionalOnMissingBean(WxMpConfigStorage.class) + public WxMpConfigStorage wxMpConfigStorage() { + WxMpProperties.StorageType type = wxMpProperties.getConfigStorage().getType(); + WxMpConfigStorage config; + if (type == WxMpProperties.StorageType.redis || type == WxMpProperties.StorageType.jedis) { + config = wxMpInJedisConfigStorage(); + } else if (type == WxMpProperties.StorageType.redistemplate) { + config = wxMpInRedisTemplateConfigStorage(); + } else { + config = wxMpInMemoryConfigStorage(); + } + return config; + } + + private WxMpConfigStorage wxMpInMemoryConfigStorage() { + WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); + setWxMpInfo(config); + return config; + } + + private WxMpConfigStorage wxMpInJedisConfigStorage() { + JedisPool jedisPool; + if (StringUtils.isNotEmpty(redisHost) || StringUtils.isNotEmpty(redisHost2)) { + jedisPool = getJedisPool(); + } else { + jedisPool = applicationContext.getBean(JedisPool.class); + } + WxRedisOps redisOps = new JedisWxRedisOps(jedisPool); + WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps, wxMpProperties.getConfigStorage().getKeyPrefix()); + setWxMpInfo(wxMpRedisConfig); + return wxMpRedisConfig; + } + + private WxMpConfigStorage wxMpInRedisTemplateConfigStorage() { + StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class); + WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); + WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps, wxMpProperties.getConfigStorage().getKeyPrefix()); + setWxMpInfo(wxMpRedisConfig); + return wxMpRedisConfig; + } + + private void setWxMpInfo(WxMpDefaultConfigImpl config) { + WxMpProperties properties = wxMpProperties; + WxMpProperties.ConfigStorage configStorageProperties = properties.getConfigStorage(); + config.setAppId(properties.getAppId()); + config.setSecret(properties.getSecret()); + config.setToken(properties.getToken()); + config.setAesKey(properties.getAesKey()); + + config.setHttpProxyHost(configStorageProperties.getHttpProxyHost()); + config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername()); + config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword()); + if (configStorageProperties.getHttpProxyPort() != null) { + config.setHttpProxyPort(configStorageProperties.getHttpProxyPort()); + } + } + + private JedisPool getJedisPool() { + WxMpProperties.ConfigStorage storage = wxMpProperties.getConfigStorage(); + WxMpProperties.RedisProperties redis = storage.getRedis(); + + JedisPoolConfig config = new JedisPoolConfig(); + if (redis.getMaxActive() != null) { + config.setMaxTotal(redis.getMaxActive()); + } + if (redis.getMaxIdle() != null) { + config.setMaxIdle(redis.getMaxIdle()); + } + if (redis.getMaxWaitMillis() != null) { + config.setMaxWaitMillis(redis.getMaxWaitMillis()); + } + if (redis.getMinIdle() != null) { + config.setMinIdle(redis.getMinIdle()); + } + config.setTestOnBorrow(true); + config.setTestWhileIdle(true); + + return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), + redis.getDatabase()); + } +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java new file mode 100644 index 0000000000..60b39d9cdc --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java @@ -0,0 +1,162 @@ +package com.binarywang.spring.starter.wxjava.mp.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.io.Serializable; + +import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.PREFIX; +import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.StorageType.memory; + + +/** + * 微信接入相关配置属性. + * + * @author someone + */ +@Data +@ConfigurationProperties(PREFIX) +public class WxMpProperties { + public static final String PREFIX = "wx.mp"; + + /** + * 设置微信公众号的appid. + */ + private String appId; + + /** + * 设置微信公众号的app secret. + */ + private String secret; + + /** + * 设置微信公众号的token. + */ + private String token; + + /** + * 设置微信公众号的EncodingAESKey. + */ + private String aesKey; + + /** + * 存储策略 + */ + private ConfigStorage configStorage = new ConfigStorage(); + + @Data + public static class ConfigStorage implements Serializable { + private static final long serialVersionUID = 4815731027000065434L; + + /** + * 存储类型. + */ + private StorageType type = memory; + + /** + * 指定key前缀. + */ + private String keyPrefix = "wx"; + + /** + * redis连接配置. + */ + private RedisProperties redis = new RedisProperties(); + + /** + * http客户端类型. + */ + private HttpClientType httpClientType = HttpClientType.httpclient; + + /** + * http代理主机. + */ + private String httpProxyHost; + + /** + * http代理端口. + */ + private Integer httpProxyPort; + + /** + * http代理用户名. + */ + private String httpProxyUsername; + + /** + * http代理密码. + */ + private String httpProxyPassword; + + } + + public enum StorageType { + /** + * 内存. + */ + memory, + /** + * jedis. + */ + redis, + /** + * redis(JedisClient). + */ + jedis, + /** + * redis(RedisTemplate). + */ + redistemplate + } + + public enum HttpClientType { + /** + * HttpClient. + */ + httpclient, + /** + * OkHttp. + */ + okhttp, + /** + * JoddHttp. + */ + joddhttp + } + + @Data + public static class RedisProperties implements Serializable { + private static final long serialVersionUID = -5924815351660074401L; + + /** + * 主机地址. + */ + private String host = "127.0.0.1"; + + /** + * 端口号. + */ + private int port = 6379; + + /** + * 密码. + */ + private String password; + + /** + * 超时. + */ + private int timeout = 2000; + + /** + * 数据库. + */ + private int database = 0; + + private Integer maxActive; + private Integer maxIdle; + private Integer maxWaitMillis; + private Integer minIdle; + } + +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..6634593ff2 --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.mp.config.WxMpAutoConfiguration diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/README.md b/spring-boot-starters/wx-java-open-spring-boot-starter/README.md new file mode 100644 index 0000000000..44333f8e4f --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/README.md @@ -0,0 +1,35 @@ +# wx-java-open-spring-boot-starter +## 快速开始 +1. 引入依赖 + ```xml + + com.github.binarywang + wx-java-open-spring-boot-starter + ${version} + + ``` +2. 添加配置(application.properties) + ```properties + # 公众号配置(必填) + wx.open.appId = appId + wx.open.secret = @secret + wx.open.token = @token + wx.open.aesKey = @aesKey + # 存储配置redis(可选) + # 优先注入容器的(JedisPool, RedissonClient), 当配置了wx.open.config-storage.redis.host, 不会使用容器注入redis连接配置 + wx.open.config-storage.type = redis # 配置类型: memory(默认), redis(jedis), jedis, redisson, redistemplate + wx.open.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认) + wx.open.config-storage.redis.host = 127.0.0.1 + wx.open.config-storage.redis.port = 6379 + # http客户端配置 + wx.open.config-storage.http-client-type=httpclient # http客户端类型: httpclient(默认) + wx.open.config-storage.http-proxy-host= + wx.open.config-storage.http-proxy-port= + wx.open.config-storage.http-proxy-username= + wx.open.config-storage.http-proxy-password= + ``` +3. 支持自动注入的类型: `WxOpenService, WxOpenMessageRouter, WxOpenComponentService` + +4. 覆盖自动配置: 自定义注入的bean会覆盖自动注入的 + - WxOpenConfigStorage + - WxOpenService diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml new file mode 100644 index 0000000000..5a98c83a40 --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml @@ -0,0 +1,63 @@ + + + + wx-java-spring-boot-starters + com.github.binarywang + 3.9.0 + + 4.0.0 + + wx-java-open-spring-boot-starter + WxJava - Spring Boot Starter for WxOpen + 微信开放平台开发的 Spring Boot Starter + + + + com.github.binarywang + weixin-java-open + ${project.version} + + + redis.clients + jedis + provided + + + org.redisson + redisson + provided + + + org.springframework.data + spring-data-redis + ${spring.boot.version} + provided + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenAutoConfiguration.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenAutoConfiguration.java new file mode 100644 index 0000000000..0f7ecf3e8c --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenAutoConfiguration.java @@ -0,0 +1,17 @@ +package com.binarywang.spring.starter.wxjava.open.config; + +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * . + * + * @author someone + */ +@Configuration +@EnableConfigurationProperties(WxOpenProperties.class) +@Import({WxOpenStorageAutoConfiguration.class, WxOpenServiceAutoConfiguration.class}) +public class WxOpenAutoConfiguration { +} diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenServiceAutoConfiguration.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenServiceAutoConfiguration.java new file mode 100644 index 0000000000..a211486840 --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenServiceAutoConfiguration.java @@ -0,0 +1,39 @@ +package com.binarywang.spring.starter.wxjava.open.config; + +import me.chanjar.weixin.open.api.WxOpenComponentService; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.api.WxOpenService; +import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter; +import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 微信开放平台相关服务自动注册. + * + * @author someone + */ +@Configuration +public class WxOpenServiceAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public WxOpenService wxOpenService(WxOpenConfigStorage configStorage) { + WxOpenService wxOpenService = new WxOpenServiceImpl(); + wxOpenService.setWxOpenConfigStorage(configStorage); + return wxOpenService; + } + + @Bean + public WxOpenMessageRouter wxOpenMessageRouter(WxOpenService wxOpenService) { + return new WxOpenMessageRouter(wxOpenService); + } + + @Bean + public WxOpenComponentService wxOpenComponentService(WxOpenService wxOpenService) { + return wxOpenService.getWxOpenComponentService(); + } + + +} diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenStorageAutoConfiguration.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenStorageAutoConfiguration.java new file mode 100644 index 0000000000..c97f00451d --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenStorageAutoConfiguration.java @@ -0,0 +1,140 @@ +package com.binarywang.spring.starter.wxjava.open.config; + +import com.binarywang.spring.starter.wxjava.open.properties.RedisProperties; +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.redis.JedisWxRedisOps; +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; +import me.chanjar.weixin.common.redis.RedissonWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage; +import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.TransportMode; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +/** + * 微信公众号存储策略自动配置. + * + * @author someone + */ +@Configuration +@RequiredArgsConstructor +public class WxOpenStorageAutoConfiguration { + private final WxOpenProperties properties; + private final ApplicationContext applicationContext; + + @Bean + @ConditionalOnMissingBean(WxOpenConfigStorage.class) + public WxOpenConfigStorage wxOpenConfigStorage() { + WxOpenProperties.ConfigStorage storage = properties.getConfigStorage(); + WxOpenProperties.StorageType type = storage.getType(); + + WxOpenInMemoryConfigStorage config; + if (type == WxOpenProperties.StorageType.redis || type == WxOpenProperties.StorageType.jedis) { + config = getWxOpenInRedisConfigStorage(); + } else if (type == WxOpenProperties.StorageType.redisson) { + config = getWxOpenInRedissonConfigStorage(); + } else if (type == WxOpenProperties.StorageType.redistemplate) { + config = getWxOpenInRedisTemplateConfigStorage(); + } else { + config = getWxOpenInMemoryConfigStorage(); + } + + WxOpenProperties.ConfigStorage configStorageProperties = properties.getConfigStorage(); + config.setWxOpenInfo(properties.getAppId(), properties.getSecret(), properties.getToken(), properties.getAesKey()); + config.setHttpProxyHost(configStorageProperties.getHttpProxyHost()); + config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername()); + config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword()); + if (configStorageProperties.getHttpProxyPort() != null) { + config.setHttpProxyPort(configStorageProperties.getHttpProxyPort()); + } + return config; + } + + private WxOpenInMemoryConfigStorage getWxOpenInMemoryConfigStorage() { + WxOpenInMemoryConfigStorage config = new WxOpenInMemoryConfigStorage(); + return config; + } + + private WxOpenInRedisConfigStorage getWxOpenInRedisConfigStorage() { + RedisProperties redisProperties = properties.getConfigStorage().getRedis(); + JedisPool jedisPool; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + jedisPool = getJedisPool(); + } else { + jedisPool = applicationContext.getBean(JedisPool.class); + } + WxRedisOps redisOps = new JedisWxRedisOps(jedisPool); + WxOpenInRedisConfigStorage config = new WxOpenInRedisConfigStorage(redisOps, properties.getConfigStorage().getKeyPrefix()); + return config; + } + + private WxOpenInRedisConfigStorage getWxOpenInRedissonConfigStorage() { + RedisProperties redisProperties = properties.getConfigStorage().getRedis(); + RedissonClient redissonClient; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + redissonClient = getRedissonClient(); + } else { + redissonClient = applicationContext.getBean(RedissonClient.class); + } + WxRedisOps redisOps = new RedissonWxRedisOps(redissonClient); + WxOpenInRedisConfigStorage config = new WxOpenInRedisConfigStorage(redisOps, properties.getConfigStorage().getKeyPrefix()); + return config; + } + + private WxOpenInRedisConfigStorage getWxOpenInRedisTemplateConfigStorage() { + StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class); + WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); + WxOpenInRedisConfigStorage config = new WxOpenInRedisConfigStorage(redisOps, properties.getConfigStorage().getKeyPrefix()); + return config; + } + + + private JedisPool getJedisPool() { + WxOpenProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + JedisPoolConfig config = new JedisPoolConfig(); + if (redis.getMaxActive() != null) { + config.setMaxTotal(redis.getMaxActive()); + } + if (redis.getMaxIdle() != null) { + config.setMaxIdle(redis.getMaxIdle()); + } + if (redis.getMaxWaitMillis() != null) { + config.setMaxWaitMillis(redis.getMaxWaitMillis()); + } + if (redis.getMinIdle() != null) { + config.setMinIdle(redis.getMinIdle()); + } + config.setTestOnBorrow(true); + config.setTestWhileIdle(true); + + JedisPool pool = new JedisPool(config, redis.getHost(), redis.getPort(), + redis.getTimeout(), redis.getPassword(), redis.getDatabase()); + return pool; + } + + private RedissonClient getRedissonClient() { + WxOpenProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + Config config = new Config(); + config.useSingleServer() + .setAddress("redis://" + redis.getHost() + ":" + redis.getPort()) + .setPassword(redis.getPassword()); + config.setTransportMode(TransportMode.NIO); + return Redisson.create(config); + } +} diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/RedisProperties.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/RedisProperties.java new file mode 100644 index 0000000000..a03d3a47f6 --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/RedisProperties.java @@ -0,0 +1,45 @@ +package com.binarywang.spring.starter.wxjava.open.properties; + +import lombok.Data; + +import java.io.Serializable; + +/** + * Redis配置. + * + * @author someone + */ +@Data +public class RedisProperties implements Serializable { + private static final long serialVersionUID = -5924815351660074401L; + + /** + * 主机地址. + */ + private String host; + + /** + * 端口号. + */ + private int port = 6379; + + /** + * 密码. + */ + private String password; + + /** + * 超时. + */ + private int timeout = 2000; + + /** + * 数据库. + */ + private int database = 0; + + private Integer maxActive; + private Integer maxIdle; + private Integer maxWaitMillis; + private Integer minIdle; +} diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenProperties.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenProperties.java new file mode 100644 index 0000000000..9c9986bacc --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenProperties.java @@ -0,0 +1,123 @@ +package com.binarywang.spring.starter.wxjava.open.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.io.Serializable; + +import static com.binarywang.spring.starter.wxjava.open.properties.WxOpenProperties.PREFIX; +import static com.binarywang.spring.starter.wxjava.open.properties.WxOpenProperties.StorageType.memory; + + +/** + * 微信接入相关配置属性. + * + * @author someone + */ +@Data +@ConfigurationProperties(PREFIX) +public class WxOpenProperties { + public static final String PREFIX = "wx.open"; + + /** + * 设置微信开放平台的appid. + */ + private String appId; + + /** + * 设置微信开放平台的app secret. + */ + private String secret; + + /** + * 设置微信开放平台的token. + */ + private String token; + + /** + * 设置微信开放平台的EncodingAESKey. + */ + private String aesKey; + + /** + * 存储策略. + */ + private ConfigStorage configStorage = new ConfigStorage(); + + + @Data + public static class ConfigStorage implements Serializable { + private static final long serialVersionUID = 4815731027000065434L; + + /** + * 存储类型. + */ + private StorageType type = memory; + + /** + * 指定key前缀. + */ + private String keyPrefix = "wx"; + + /** + * redis连接配置. + */ + private RedisProperties redis = new RedisProperties(); + + /** + * http客户端类型. + */ + private HttpClientType httpClientType = HttpClientType.httpclient; + + /** + * http代理主机. + */ + private String httpProxyHost; + + /** + * http代理端口. + */ + private Integer httpProxyPort; + + /** + * http代理用户名. + */ + private String httpProxyUsername; + + /** + * http代理密码. + */ + private String httpProxyPassword; + + } + + public enum StorageType { + /** + * 内存. + */ + memory, + /** + * redis. + */ + redis, + /** + * jedis. + */ + jedis, + /** + * redisson. + */ + redisson, + /** + * redistemplate + */ + redistemplate + } + + public enum HttpClientType { + /** + * HttpClient. + */ + httpclient + } +} diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..d46458f9db --- /dev/null +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.open.config.WxOpenAutoConfiguration diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/README.md b/spring-boot-starters/wx-java-pay-spring-boot-starter/README.md new file mode 100644 index 0000000000..a4d91fade0 --- /dev/null +++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/README.md @@ -0,0 +1,27 @@ +# 使用说明 +1. 在自己的Spring Boot项目里,引入maven依赖 +```xml + + com.github.binarywang + wx-java-pay-spring-boot-starter + ${version} + + ``` +2. 添加配置(application.yml) +```yml +wx: + pay: + appId: + mchId: + mchKey: + subAppId: + subMchId: + keyPath: +``` + + + + + + + diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml new file mode 100644 index 0000000000..1a93eb54f9 --- /dev/null +++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml @@ -0,0 +1,47 @@ + + + + wx-java-spring-boot-starters + com.github.binarywang + 3.9.0 + + 4.0.0 + + wx-java-pay-spring-boot-starter + WxJava - Spring Boot Starter for WxPay + 微信支付开发的 Spring Boot Starter + + + + com.github.binarywang + weixin-java-pay + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java new file mode 100644 index 0000000000..2dd44004a6 --- /dev/null +++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java @@ -0,0 +1,64 @@ +package com.binarywang.spring.starter.wxjava.pay.config; + +import com.binarywang.spring.starter.wxjava.pay.properties.WxPayProperties; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + *
+ *  微信支付自动配置
+ *  Created by BinaryWang on 2019/4/17.
+ * 
+ * + * @author Binary Wang + */ +@Configuration +@EnableConfigurationProperties(WxPayProperties.class) +@ConditionalOnClass(WxPayService.class) +@ConditionalOnProperty(prefix = "wx.pay", value = "enabled", matchIfMissing = true) +public class WxPayAutoConfiguration { + private WxPayProperties properties; + + @Autowired + public WxPayAutoConfiguration(WxPayProperties properties) { + this.properties = properties; + } + + /** + * 构造微信支付服务对象. + * + * @return 微信支付service + */ + @Bean + @ConditionalOnMissingBean(WxPayService.class) + public WxPayService wxPayService() { + final WxPayServiceImpl wxPayService = new WxPayServiceImpl(); + WxPayConfig payConfig = new WxPayConfig(); + payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId())); + payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId())); + payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey())); + payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId())); + payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId())); + payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); + //以下是apiv3以及支付分相关 + payConfig.setServiceId(StringUtils.trimToNull(this.properties.getServiceId())); + payConfig.setPayScoreNotifyUrl(StringUtils.trimToNull(this.properties.getPayScoreNotifyUrl())); + payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath())); + payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath())); + payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getCertSerialNo())); + payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiv3Key())); + + wxPayService.setConfig(payConfig); + return wxPayService; + } + +} diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java new file mode 100644 index 0000000000..940cdf5916 --- /dev/null +++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java @@ -0,0 +1,77 @@ +package com.binarywang.spring.starter.wxjava.pay.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + *
+ *  微信支付属性配置类
+ * Created by Binary Wang on 2019/4/17.
+ * 
+ * + * @author Binary Wang + */ +@Data +@ConfigurationProperties(prefix = "wx.pay") +public class WxPayProperties { + /** + * 设置微信公众号或者小程序等的appid. + */ + private String appId; + + /** + * 微信支付商户号. + */ + private String mchId; + + /** + * 微信支付商户密钥. + */ + private String mchKey; + + /** + * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除. + */ + private String subAppId; + + /** + * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除. + */ + private String subMchId; + + /** + * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定. + */ + private String keyPath; + + /** + * 微信支付分serviceId + */ + private String serviceId; + + /** + * 证书序列号 + */ + private String certSerialNo; + + /** + * apiV3秘钥 + */ + private String apiv3Key; + + /** + * 微信支付分回调地址 + */ + private String payScoreNotifyUrl; + + /** + * apiv3 商户apiclient_key.pem + */ + private String privateKeyPath; + + /** + * apiv3 商户apiclient_cert.pem + */ + private String privateCertPath; + +} diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..37fe6c20e4 --- /dev/null +++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.pay.config.WxPayAutoConfiguration diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml new file mode 100644 index 0000000000..016544bc6c --- /dev/null +++ b/weixin-graal/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.github.binarywang + wx-java + 3.9.0 + + + weixin-graal + WxJava - Graal + 微信开发Java内部配合graal以产生native-image配置的辅助工具, 可以通过项目的 native-image Profile 来启用: mvn -P native-image ... + + + + + + org.projectlombok + lombok + compile + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + none + + + + + + diff --git a/weixin-graal/src/main/java/cn/binarywang/wx/graal/GraalProcessor.java b/weixin-graal/src/main/java/cn/binarywang/wx/graal/GraalProcessor.java new file mode 100644 index 0000000000..a7b02cae99 --- /dev/null +++ b/weixin-graal/src/main/java/cn/binarywang/wx/graal/GraalProcessor.java @@ -0,0 +1,177 @@ +package cn.binarywang.wx.graal; + +import lombok.Data; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import java.io.IOException; +import java.io.Writer; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * 目前仅仅处理@Data,且必须在lombok自己的processor之前执行,千万注意!!!!! + * + * @author outersky + */ +@SupportedAnnotationTypes("lombok.Data") +@SupportedSourceVersion(SourceVersion.RELEASE_7) +public class GraalProcessor extends AbstractProcessor { + private static final String REFLECTION_CONFIG_JSON = "reflection-config.json"; + private static final String NATIVE_IMAGE_PROPERTIES = "native-image.properties"; + + private SortedSet classSet = new TreeSet<>(); + private String shortestPackageName = null; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (TypeElement annotatedClass : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Data.class))) { + registerClass(annotatedClass.getQualifiedName().toString()); + handleSuperClass(annotatedClass); + } + + //只有最后一轮才可以写文件,否则文件会被重复打开,报错! + if (!roundEnv.processingOver()) { + return false; + } + + // 如果没有文件要写,跳过 + if (classSet.isEmpty()) { + return false; + } + + writeFiles(); + + //必须返回false,以便让lombok能继续处理。 + return false; + } + + /** + * 设置当前最短的package名称 + * + * @param packageName 包名 + */ + private void setShortestPackageName(String packageName) { + if (shortestPackageName == null) { + shortestPackageName = packageName; + } else if (packageName.length() < shortestPackageName.length()) { + shortestPackageName = packageName; + } + } + + /** + * 更加完整的类名来获取package名称 + * + * @param fullClassName 完整的类名 + * @return package name + */ + private String getPackageName(String fullClassName) { + int last = fullClassName.lastIndexOf('.'); + if (last == -1) { + return fullClassName; + } + return fullClassName.substring(0, last); + } + + /** + * 保存文件 + * META-INF/native-image/.../reflection-config.json + * META-INF/native-image/.../native-image.properties + */ + private void writeFiles() { + String basePackage = shortestPackageName; + + String module; + if (basePackage.contains(".")) { + final int i = basePackage.lastIndexOf('.'); + module = basePackage.substring(i + 1); + basePackage = basePackage.substring(0, i); + } else { + module = basePackage; + } + + String path = "META-INF/native-image/" + basePackage + "/" + module + "/"; + String reflectFile = path + REFLECTION_CONFIG_JSON; + String propsFile = path + NATIVE_IMAGE_PROPERTIES; + try { + FileObject fileObject = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", propsFile); + try (Writer writer = fileObject.openWriter();) { + writer.append("Args = -H:ReflectionConfigurationResources=${.}/" + REFLECTION_CONFIG_JSON); + } + } catch (IOException e) { + e.printStackTrace(); + } + + try { + FileObject fileObject = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", reflectFile); + try (Writer writer = fileObject.openWriter();) { + writer.write("[\n"); + boolean first = true; + for (String name : classSet) { + if (first) { + first = false; + } else { + writer.write(","); + } + writer.write(assetGraalJsonElement(name)); + writer.append('\n'); + } + writer.write("]"); + } + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private String assetGraalJsonElement(String className) { + return "{\n" + + " \"name\" : \"" + className + "\",\n" + + " \"allDeclaredFields\":true,\n" + + " \"allDeclaredMethods\":true,\n" + + " \"allDeclaredConstructors\":true,\n" + + " \"allPublicMethods\" : true\n" + + "}"; + } + + /** + * 登记一个class + * + * @param className 完整的类名 + */ + private void registerClass(String className) { + classSet.add(className); + setShortestPackageName(getPackageName(className)); + } + + /** + * 获取一个类型的所有的父类,并登记 + * + * @param typeElement 类型元素 + */ + private void handleSuperClass(TypeElement typeElement) { + TypeMirror superclass = typeElement.getSuperclass(); + if (superclass.getKind() == TypeKind.DECLARED) { + TypeElement s = (TypeElement) ((DeclaredType) superclass).asElement(); + String sName = s.toString(); + // ignore java.**/javax.** + if (sName.startsWith("java.") || sName.startsWith("javax.")) { + return; + } + registerClass(sName); + handleSuperClass(s); + } + } + +} diff --git a/weixin-graal/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/weixin-graal/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..fed7c4d9cd --- /dev/null +++ b/weixin-graal/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +cn.binarywang.wx.graal.GraalProcessor diff --git a/weixin-java-cloudbase/README.md b/weixin-java-cloudbase/README.md new file mode 100644 index 0000000000..495b8354e3 --- /dev/null +++ b/weixin-java-cloudbase/README.md @@ -0,0 +1,80 @@ +## 如何使用 `WxJava` 进行小程序云开发 + +[云开发(CloudBase)](https://www.cloudbase.net/)是基于Serverless架构构建的一站式后端云服务,涵盖函数、数据库、存储、CDN等服务,免后端运维,支持小程序、Web和APP开发。 +其中,小程序·云开发是微信和腾讯云联合推出的云端一体化解决方案,基于云开发可以免鉴权调用微信所有开放能力,在微信开发者工具中即可开通使用。 + +### 一、 引入`maven`依赖 + + +``` + + com.github.binarywang + weixin-java-miniapp + 3.7.1.B + +``` + + +### 二、 构造配置类,填入相关参数 + +``` + WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); + config.setAppid(...); // 微信小程序的appid + config.setSecret(...); // 微信小程序的Secret + config.setToken(...); // 微信小程序消息服务器配置的token,如果程序不涉及相关功能,可以忽略 + config.setAesKey(...); // 微信小程序消息服务器配置的EncodingAESKey,同上,如果不涉及,可以忽略 + config.setMsgDataFormat(...); // 消息数据格式,可以为XML或者JSON +``` + + +### 三、 构造service类,关联上述配置 + +``` + WxMaService wxMaService= new WxMaServiceImpl(); + + wxMaService.setWxMaConfig(config); +``` + +### 四、 根据小程序前端需要调用相应的方法: + +目前 `WxJava` 已支持当前所有接口(当然如果官方后续加入新接口,则还未在最新版本中实现,会考虑在之后版本中加入),所有已支持的接口列表可以参考在线`JavaDoc`:http://binary.ac.cn/weixin-java-miniapp-javadoc/cn/binarywang/wx/miniapp/api/WxMaCloudService.html + + +以触发云函数接口为例,可以采用如下方式调用 `invokeCloudFunction` 方法: + + +``` +String result = wxMaService.getCloudService().invokeCloudFunction("rcn", "login", "{}"); // 拿到result之后,可以在后续加入自己的处理逻辑代码 +``` + + +更多方法调用实例可以参考 `WxJava` 源码中的单元测试类:`cn.binarywang.wx.miniapp.api.impl.WxMaCloudServiceImplTest` + + +另外,顺便在此列出当前已支持云开发的方法如下: + +| 接口描述 | 方法名 | +| ---- | ---- | +| 删除文件 | batchDeleteFile(String env, String[] fileIds)| +| 获取文件下载链接 | batchDownloadFile(String env, String[] fileIds, long[] maxAges)| +| 数据库插入记录 | databaseAdd(String env, String query)| +| 数据库聚合记录 | databaseAggregate(String env, String query)| +| 新增集合 | databaseCollectionAdd(String env, String collectionName)| +| 删除集合 | databaseCollectionDelete(String env, String collectionName)| +| 获取特定云环境下集合信息 | databaseCollectionGet(String env, Long limit, Long offset)| +| 统计集合记录数或统计查询语句对应的结果记录数 | databaseCount(String env, String query)| +| 数据库删除记录 | databaseDelete(String env, String query)| +| 数据库导出 | databaseMigrateExport(String env, String filePath, int fileType, String query)| +| 数据库导入 | databaseMigrateImport(String env, String collectionName, String filePath, int fileType, boolean stopOnError, int conflictMode)| +| 数据库迁移状态查询 | databaseMigrateQueryInfo(String env, Long jobId)| +| 数据库查询记录 | databaseQuery(String env, String query)| +| 数据库更新记录 | databaseUpdate(String env, String query)| +| 获取腾讯云API调用凭证 | getQcloudToken(long lifeSpan)| +| 触发云函数 | invokeCloudFunction(String env, String name, String body)| +| 变更数据库索引 | updateIndex(String env, String collectionName, List createIndexes, List dropIndexNames)| +| 获取文件上传链接 | uploadFile(String env, String path)| + +### 五、 `Spring` 框架整合: + +可以参考https://github.com/binarywang/weixin-java-miniapp-demo 此项目整合 `Spring` 开发。 +此项目基于 `Spring Boot` ,如果需要支持`Spring MVC`项目,适当改造即可。 diff --git a/weixin-java-common/build.gradle b/weixin-java-common/build.gradle deleted file mode 100644 index 718004931c..0000000000 --- a/weixin-java-common/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ - -description = 'WeiXin Java Tools - Common' -dependencies { - compile group: 'com.thoughtworks.xstream', name: 'xstream', version:'1.4.7' - testCompile group: 'junit', name: 'junit', version:'4.11' - testCompile group: 'org.testng', name: 'testng', version:'6.8.7' - testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' - testCompile group: 'com.google.inject', name: 'guice', version:'3.0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' -} -test.useTestNG() diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 946c65c24e..8786e0458d 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -1,19 +1,88 @@ - + 4.0.0 com.github.binarywang - weixin-java-parent - 2.6.0 + wx-java + 3.9.0 weixin-java-common - WeiXin Java Tools - Common - 微信公众号、企业号Java SDK Common + WxJava - Common Java SDK + 微信开发Java SDK公共模块 + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + + + + org.slf4j + slf4j-api + + + com.thoughtworks.xstream + xstream + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpmime + + + org.slf4j + jcl-over-slf4j + 1.7.24 + + + + com.google.code.gson + gson + + + commons-codec + commons-codec + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + org.projectlombok + lombok + + + + ch.qos.logback + logback-classic + test + org.testng testng @@ -39,6 +108,32 @@ jetty-servlet test + + org.assertj + assertj-guava + test + + + org.dom4j + dom4j + 2.1.3 + + + redis.clients + jedis + + + com.github.jedis-lock + jedis-lock + + + org.redisson + redisson + + + org.springframework.data + spring-data-redis + @@ -55,4 +150,36 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + + diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java index 9ed249da6b..e18be69e96 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java @@ -6,14 +6,15 @@ import java.lang.annotation.Target; /** + *
  * 标识某个字段是否是必填的
- * 
  * Created by Binary Wang on 2016/9/25.
- * @author binarywang (https://github.com/binarywang)
+ * 
* + * @author binarywang (https://github.com/binarywang) */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Required { -} \ No newline at end of file +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 435ba8e0fb..99acd4f867 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -1,228 +1,451 @@ package me.chanjar.weixin.common.api; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; -public class WxConsts { - - /////////////////////// - // 微信推送过来的消息的类型,和发送给微信xml格式消息的消息类型 - /////////////////////// - public static final String XML_MSG_TEXT = "text"; - public static final String XML_MSG_IMAGE = "image"; - public static final String XML_MSG_VOICE = "voice"; - public static final String XML_MSG_SHORTVIDEO = "shortvideo"; - public static final String XML_MSG_VIDEO = "video"; - public static final String XML_MSG_NEWS = "news"; - public static final String XML_MSG_MUSIC = "music"; - public static final String XML_MSG_LOCATION = "location"; - public static final String XML_MSG_LINK = "link"; - public static final String XML_MSG_EVENT = "event"; - public static final String XML_MSG_DEVICE_TEXT = "device_text"; - public static final String XML_MSG_DEVICE_EVENT = "device_event"; - public static final String XML_MSG_DEVICE_STATUS = "device_status"; - public static final String XML_MSG_HARDWARE = "hardware"; - public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; - - - /////////////////////// - // 主动发送消息(即客服消息)的消息类型 - /////////////////////// - public static final String CUSTOM_MSG_TEXT = "text";//文本消息 - public static final String CUSTOM_MSG_IMAGE = "image";//图片消息 - public static final String CUSTOM_MSG_VOICE = "voice";//语音消息 - public static final String CUSTOM_MSG_VIDEO = "video";//视频消息 - public static final String CUSTOM_MSG_MUSIC = "music";//音乐消息 - public static final String CUSTOM_MSG_NEWS = "news";//图文消息(点击跳转到外链) - public static final String CUSTOM_MSG_MPNEWS = "mpnews";//图文消息(点击跳转到图文消息页面) - public static final String CUSTOM_MSG_FILE = "file";//发送文件(CP专用) - public static final String CUSTOM_MSG_WXCARD = "wxcard";//卡券消息 - public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; - public static final String CUSTOM_MSG_SAFE_NO = "0"; - public static final String CUSTOM_MSG_SAFE_YES = "1"; - - /////////////////////// - // 群发消息的消息类型 - /////////////////////// - public static final String MASS_MSG_NEWS = "mpnews"; - public static final String MASS_MSG_TEXT = "text"; - public static final String MASS_MSG_VOICE = "voice"; - public static final String MASS_MSG_IMAGE = "image"; - public static final String MASS_MSG_VIDEO = "mpvideo"; - - /////////////////////// - // 群发消息后微信端推送给服务器的反馈消息 - /////////////////////// - public static final String MASS_ST_SUCCESS = "send success"; - public static final String MASS_ST_FAIL = "send fail"; - public static final String MASS_ST_10001 = "err(10001)"; - public static final String MASS_ST_20001 = "err(20001)"; - public static final String MASS_ST_20004 = "err(20004)"; - public static final String MASS_ST_20002 = "err(20002)"; - public static final String MASS_ST_20006 = "err(20006)"; - public static final String MASS_ST_20008 = "err(20008)"; - public static final String MASS_ST_20013 = "err(20013)"; - public static final String MASS_ST_22000 = "err(22000)"; - public static final String MASS_ST_21000 = "err(21000)"; +import static me.chanjar.weixin.common.error.WxMpErrorMsgEnum.*; +/** + * 微信开发所使用到的常量类. + * + * @author Daniel Qian & binarywang + */ +public class WxConsts { /** - * 群发反馈消息代码所对应的文字描述 - */ - public static final Map MASS_ST_2_DESC = new HashMap<>(); - - /////////////////////// - // 微信端推送过来的事件类型 - /////////////////////// - public static final String EVT_SUBSCRIBE = "subscribe"; - public static final String EVT_UNSUBSCRIBE = "unsubscribe"; - public static final String EVT_SCAN = "SCAN"; - public static final String EVT_LOCATION = "LOCATION"; - public static final String EVT_CLICK = "CLICK"; - public static final String EVT_VIEW = "VIEW"; - public static final String EVT_MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; - public static final String EVT_SCANCODE_PUSH = "scancode_push"; - public static final String EVT_SCANCODE_WAITMSG = "scancode_waitmsg"; - public static final String EVT_PIC_SYSPHOTO = "pic_sysphoto"; - public static final String EVT_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; - public static final String EVT_PIC_WEIXIN = "pic_weixin"; - public static final String EVT_LOCATION_SELECT = "location_select"; - public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH"; - public static final String EVT_ENTER_AGENT = "enter_agent"; - public static final String EVT_CARD_PASS_CHECK = "card_pass_check"; - public static final String EVT_CARD_NOT_PASS_CHECK = "card_not_pass_check"; - public static final String EVT_USER_GET_CARD = "user_get_card"; - public static final String EVT_USER_DEL_CARD = "user_del_card"; - public static final String EVT_USER_CONSUME_CARD = "user_consume_card"; - public static final String EVT_USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell"; - public static final String EVT_USER_VIEW_CARD = "user_view_card"; - public static final String EVT_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; - public static final String EVT_CARD_SKU_REMIND = "card_sku_remind"; // 库存报警 - public static final String EVT_KF_CREATE_SESSION = "kf_create_session"; // 客服接入会话 - public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话 - public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 - public static final String EVT_POI_CHECK_NOTIFY = "poi_check_notify"; //门店审核事件推送 - public static final String EVN_SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; //接收会员信息事件推送 - //以下为微信认证事件 - /** - * 资质认证成功 - */ - public static final String EVT_QUALIFICATION_VERIFY_SUCCESS = "qualification_verify_success"; - /** - * 资质认证失败 - */ - public static final String EVT_QUALIFICATION_VERIFY_FAIL = "qualification_verify_fail"; - /** - * 名称认证成功 - */ - public static final String EVT_NAMING_VERIFY_SUCCESS = "naming_verify_success"; - /** - * 名称认证失败 + * access_token 相关错误代码 + *
+   * 发生以下情况时尝试刷新access_token
+   * 40001 获取access_token时AppSecret错误,或者access_token无效
+   * 42001 access_token超时
+   * 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口
+   * 
*/ - public static final String EVT_NAMING_VERIFY_FAIL = "naming_verify_fail"; + public static final List ACCESS_TOKEN_ERROR_CODES = Arrays.asList(CODE_40001.getCode(), + CODE_40014.getCode(), CODE_42001.getCode()); + /** - * 年审通知 + * 微信推送过来的消息的类型,和发送给微信xml格式消息的消息类型. */ - public static final String EVT_ANNUAL_RENEW = "annual_renew"; + public static class XmlMsgType { + public static final String TEXT = "text"; + public static final String IMAGE = "image"; + public static final String VOICE = "voice"; + public static final String SHORTVIDEO = "shortvideo"; + public static final String VIDEO = "video"; + public static final String NEWS = "news"; + public static final String MUSIC = "music"; + public static final String LOCATION = "location"; + public static final String LINK = "link"; + public static final String EVENT = "event"; + public static final String DEVICE_TEXT = "device_text"; + public static final String DEVICE_EVENT = "device_event"; + public static final String DEVICE_STATUS = "device_status"; + public static final String HARDWARE = "hardware"; + public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; + } + /** - * 认证过期失效通知 + * 主动发送消息(即客服消息)的消息类型. */ - public static final String EVT_VERIFY_EXPIRED = "verify_expired"; + public static class KefuMsgType { + /** + * 文本消息. + */ + public static final String TEXT = "text"; + /** + * 图片消息. + */ + public static final String IMAGE = "image"; + /** + * 语音消息. + */ + public static final String VOICE = "voice"; + /** + * 视频消息. + */ + public static final String VIDEO = "video"; + /** + * 音乐消息. + */ + public static final String MUSIC = "music"; + /** + * 图文消息(点击跳转到外链). + */ + public static final String NEWS = "news"; + /** + * 图文消息(点击跳转到图文消息页面). + */ + public static final String MPNEWS = "mpnews"; + /** + * markdown消息. + * (目前仅支持markdown语法的子集,微工作台(原企业号)不支持展示markdown消息) + */ + public static final String MARKDOWN = "markdown"; + /** + * 发送文件(CP专用). + */ + public static final String FILE = "file"; + /** + * 文本卡片消息(CP专用). + */ + public static final String TEXTCARD = "textcard"; + /** + * 卡券消息. + */ + public static final String WXCARD = "wxcard"; + /** + * 转发到客服的消息. + */ + public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; - /////////////////////// - // 上传多媒体文件的类型 - /////////////////////// - public static final String MEDIA_IMAGE = "image"; - public static final String MEDIA_VOICE = "voice"; - public static final String MEDIA_VIDEO = "video"; - public static final String MEDIA_THUMB = "thumb"; - public static final String MEDIA_FILE = "file"; + /** + * 小程序卡片(要求小程序与公众号已关联). + */ + public static final String MINIPROGRAMPAGE = "miniprogrampage"; + /** + * 任务卡片消息. + */ + public static final String TASKCARD = "taskcard"; + + /** + * 菜单消息. + */ + public static final String MSGMENU = "msgmenu"; + + /** + * 小程序通知消息. + */ + public static final String MINIPROGRAM_NOTICE = "miniprogram_notice"; + } - /////////////////////// - // 自定义菜单的按钮类型 - /////////////////////// - /** - * 点击推事件 - */ - public static final String BUTTON_CLICK = "click"; - /** - * 跳转URL - */ - public static final String BUTTON_VIEW = "view"; /** - * 跳转到小程序 + * 群机器人的消息类型. */ - public static final String BUTTON_MINIPROGRAM = "miniprogram"; + public static class GroupRobotMsgType { + /** + * 文本消息. + */ + public static final String TEXT = "text"; + + /** + * 图片消息. + */ + public static final String IMAGE = "image"; + + /** + * markdown消息. + */ + public static final String MARKDOWN = "markdown"; + + /** + * 图文消息(点击跳转到外链). + */ + public static final String NEWS = "news"; + } + /** - * 扫码推事件 + * 表示是否是保密消息,0表示否,1表示是,默认0. */ - public static final String BUTTON_SCANCODE_PUSH = "scancode_push"; + public static class KefuMsgSafe { + public static final String NO = "0"; + public static final String YES = "1"; + } + /** - * 扫码推事件且弹出“消息接收中”提示框 + * 群发消息的消息类型. */ - public static final String BUTTON_SCANCODE_WAITMSG = "scancode_waitmsg"; + public static class MassMsgType { + public static final String MPNEWS = "mpnews"; + public static final String TEXT = "text"; + public static final String VOICE = "voice"; + public static final String IMAGE = "image"; + public static final String MPVIDEO = "mpvideo"; + } + /** - * 弹出系统拍照发图 + * 群发消息后微信端推送给服务器的反馈消息. */ - public static final String BUTTON_PIC_SYSPHOTO = "pic_sysphoto"; + public static class MassMsgStatus { + public static final String SEND_SUCCESS = "send success"; + public static final String SEND_FAIL = "send fail"; + public static final String ERR_10001 = "err(10001)"; + public static final String ERR_20001 = "err(20001)"; + public static final String ERR_20004 = "err(20004)"; + public static final String ERR_20002 = "err(20002)"; + public static final String ERR_20006 = "err(20006)"; + public static final String ERR_20008 = "err(20008)"; + public static final String ERR_20013 = "err(20013)"; + public static final String ERR_22000 = "err(22000)"; + public static final String ERR_21000 = "err(21000)"; + + /** + * 群发反馈消息代码所对应的文字描述. + */ + public static final Map STATUS_DESC = new HashMap<>(); + + static { + STATUS_DESC.put(SEND_SUCCESS, "发送成功"); + STATUS_DESC.put(SEND_FAIL, "发送失败"); + STATUS_DESC.put(ERR_10001, "涉嫌广告"); + STATUS_DESC.put(ERR_20001, "涉嫌政治"); + STATUS_DESC.put(ERR_20004, "涉嫌社会"); + STATUS_DESC.put(ERR_20002, "涉嫌色情"); + STATUS_DESC.put(ERR_20006, "涉嫌违法犯罪"); + STATUS_DESC.put(ERR_20008, "涉嫌欺诈"); + STATUS_DESC.put(ERR_20013, "涉嫌版权"); + STATUS_DESC.put(ERR_22000, "涉嫌互推_互相宣传"); + STATUS_DESC.put(ERR_21000, "涉嫌其他"); + } + } + /** - * 弹出拍照或者相册发图 + * 微信端推送过来的事件类型. */ - public static final String BUTTON_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; + public static class EventType { + public static final String SUBSCRIBE = "subscribe"; + public static final String UNSUBSCRIBE = "unsubscribe"; + public static final String SCAN = "SCAN"; + public static final String LOCATION = "LOCATION"; + public static final String CLICK = "CLICK"; + public static final String VIEW = "VIEW"; + public static final String MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; + /** + * 扫码推事件的事件推送 + */ + public static final String SCANCODE_PUSH = "scancode_push"; + /** + * 扫码推事件且弹出“消息接收中”提示框的事件推送. + */ + public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; + /** + * 弹出系统拍照发图的事件推送. + */ + public static final String PIC_SYSPHOTO = "pic_sysphoto"; + /** + * 弹出拍照或者相册发图的事件推送. + */ + public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; + /** + * 弹出微信相册发图器的事件推送. + */ + public static final String PIC_WEIXIN = "pic_weixin"; + /** + * 弹出地理位置选择器的事件推送. + */ + public static final String LOCATION_SELECT = "location_select"; + + public static final String TEMPLATE_SEND_JOB_FINISH = "TEMPLATESENDJOBFINISH"; + /** + * 微信小店 订单付款通知. + */ + public static final String MERCHANT_ORDER = "merchant_order"; + + /** + * 卡券事件:卡券通过审核 + */ + public static final String CARD_PASS_CHECK = "card_pass_check"; + + /** + * 卡券事件:卡券未通过审核 + */ + public static final String CARD_NOT_PASS_CHECK = "card_not_pass_check"; + + /** + * 卡券事件:用户领取卡券 + */ + public static final String CARD_USER_GET_CARD = "user_get_card"; + + /** + * 卡券事件:用户转赠卡券 + */ + public static final String CARD_USER_GIFTING_CARD = "user_gifting_card"; + + + /** + * 卡券事件:用户核销卡券 + */ + public static final String CARD_USER_CONSUME_CARD = "user_consume_card"; + + + /** + * 卡券事件:用户通过卡券的微信买单完成时推送 + */ + public static final String CARD_USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell"; + + + /** + * 卡券事件:用户提交会员卡开卡信息 + */ + public static final String CARD_SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; + + /** + * 卡券事件:用户打开查看卡券 + */ + public static final String CARD_USER_VIEW_CARD = "user_view_card"; + + /** + * 卡券事件:用户删除卡券 + */ + public static final String CARD_USER_DEL_CARD = "user_del_card"; + + /** + * 卡券事件:用户在卡券里点击查看公众号进入会话时(需要用户已经关注公众号) + */ + public static final String CARD_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; + + /** + * 卡券事件:当用户的会员卡积分余额发生变动时 + */ + public static final String CARD_UPDATE_MEMBER_CARD = "update_member_card"; + + /** + * 卡券事件:当某个card_id的初始库存数大于200且当前库存小于等于100时,用户尝试领券会触发发送事件给商户,事件每隔12h发送一次 + */ + public static final String CARD_SKU_REMIND = "card_sku_remind"; + + /** + * 卡券事件:当商户朋友的券券点发生变动时 + */ + public static final String CARD_PAY_ORDER = "card_pay_order"; + + /** + * 小程序审核事件:审核通过 + */ + public static final String WEAPP_AUDIT_SUCCESS = "weapp_audit_success"; + + /** + * 小程序审核事件:审核不通过 + */ + public static final String WEAPP_AUDIT_FAIL = "weapp_audit_fail"; + + } + /** - * 弹出微信相册发图器 + * 上传多媒体(临时素材)文件的类型. */ - public static final String BUTTON_PIC_WEIXIN = "pic_weixin"; + public static class MediaFileType { + public static final String IMAGE = "image"; + public static final String VOICE = "voice"; + public static final String VIDEO = "video"; + public static final String THUMB = "thumb"; + public static final String FILE = "file"; + } + /** - * 弹出地理位置选择器 + * 自定义菜单的按钮类型. */ - public static final String BUTTON_LOCATION_SELECT = "location_select"; + public static class MenuButtonType { + /** + * 点击推事件. + */ + public static final String CLICK = "click"; + /** + * 跳转URL. + */ + public static final String VIEW = "view"; + /** + * 跳转到小程序. + */ + public static final String MINIPROGRAM = "miniprogram"; + /** + * 扫码推事件. + */ + public static final String SCANCODE_PUSH = "scancode_push"; + /** + * 扫码推事件且弹出“消息接收中”提示框. + */ + public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; + /** + * 弹出系统拍照发图. + */ + public static final String PIC_SYSPHOTO = "pic_sysphoto"; + /** + * 弹出拍照或者相册发图. + */ + public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; + /** + * 弹出微信相册发图器. + */ + public static final String PIC_WEIXIN = "pic_weixin"; + /** + * 弹出地理位置选择器. + */ + public static final String LOCATION_SELECT = "location_select"; + /** + * 下发消息(除文本消息). + */ + public static final String MEDIA_ID = "media_id"; + /** + * 跳转图文消息URL. + */ + public static final String VIEW_LIMITED = "view_limited"; + } + /** - * 下发消息(除文本消息) + * oauth2网页授权的scope. */ - public static final String BUTTON_MEDIA_ID = "media_id"; + public static class OAuth2Scope { + /** + * 不弹出授权页面,直接跳转,只能获取用户openid. + */ + public static final String SNSAPI_BASE = "snsapi_base"; + + /** + * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息. + */ + public static final String SNSAPI_USERINFO = "snsapi_userinfo"; + + /** + * 手动授权,可获取成员的详细信息,包含手机、邮箱。只适用于企业微信或企业号. + */ + public static final String SNSAPI_PRIVATEINFO = "snsapi_privateinfo"; + } + /** - * 跳转图文消息URL + * 网页应用登录授权作用域. */ - public static final String BUTTON_VIEW_LIMITED = "view_limited"; - + public static class QrConnectScope { + public static final String SNSAPI_LOGIN = "snsapi_login"; + } + /** - * 不弹出授权页面,直接跳转,只能获取用户openid + * 永久素材类型. */ - public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; + public static class MaterialType { + public static final String NEWS = "news"; + public static final String VOICE = "voice"; + public static final String IMAGE = "image"; + public static final String VIDEO = "video"; + } + - /////////////////////// - // oauth2网页授权的scope - /////////////////////// /** - * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 + * 网络检测入参. */ - public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; + public static class NetCheckArgs { + public static final String ACTIONDNS = "dns"; + public static final String ACTIONPING = "ping"; + public static final String ACTIONALL = "all"; + public static final String OPERATORUNICOM = "UNICOM"; + public static final String OPERATORCHINANET = "CHINANET"; + public static final String OPERATORCAP = "CAP"; + public static final String OPERATORDEFAULT = "DEFAULT"; + } /** - * 网页应用登录授权作用域 snsapi_login + * appId 类型 */ - public static final String QRCONNECT_SCOPE_SNSAPI_LOGIN = "snsapi_login"; - - /////////////////////// - // 永久素材类型 - /////////////////////// - public static final String MATERIAL_NEWS = "news"; - public static final String MATERIAL_VOICE = "voice"; - public static final String MATERIAL_IMAGE = "image"; - public static final String MATERIAL_VIDEO = "video"; - - static { - MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); - MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); - MASS_ST_2_DESC.put(MASS_ST_10001, "涉嫌广告"); - MASS_ST_2_DESC.put(MASS_ST_20001, "涉嫌政治"); - MASS_ST_2_DESC.put(MASS_ST_20004, "涉嫌社会"); - MASS_ST_2_DESC.put(MASS_ST_20002, "涉嫌色情"); - MASS_ST_2_DESC.put(MASS_ST_20006, "涉嫌违法犯罪"); - MASS_ST_2_DESC.put(MASS_ST_20008, "涉嫌欺诈"); - MASS_ST_2_DESC.put(MASS_ST_20013, "涉嫌版权"); - MASS_ST_2_DESC.put(MASS_ST_22000, "涉嫌互推_互相宣传"); - MASS_ST_2_DESC.put(MASS_ST_21000, "涉嫌其他"); + public static class AppIdType { + /** + * 公众号appId类型 + */ + public static final String MP_TYPE = "mp"; + /** + * 小程序appId类型 + */ + public static final String MINI_TYPE = "mini"; } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxErrorExceptionHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxErrorExceptionHandler.java index d6acf55b52..83242e2f7a 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxErrorExceptionHandler.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxErrorExceptionHandler.java @@ -1,9 +1,11 @@ package me.chanjar.weixin.common.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; /** - * WxErrorException处理器 + * WxErrorException处理器. + * + * @author Daniel Qian */ public interface WxErrorExceptionHandler { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxImgProcService.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxImgProcService.java new file mode 100644 index 0000000000..c7d1bcfc14 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxImgProcService.java @@ -0,0 +1,121 @@ +package me.chanjar.weixin.common.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcAiCropResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcQrCodeResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcSuperResolutionResult; + +import java.io.File; + +/** + * 多项图像处理能力相关的API. + * https://developers.weixin.qq.com/doc/offiaccount/Intelligent_Interface/Img_Proc.html + * + * @author Theo Nie + */ +public interface WxImgProcService { + + /** + * 二维码/条码识别接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * 3.支持条码、二维码、DataMatrix和PDF417的识别。 + * 4.二维码、DataMatrix会返回位置坐标,条码和PDF417暂不返回位置坐标。 + * + * @param imgUrl 图片url地址 + * @return WxMpImgProcQrCodeResult + * @throws WxErrorException . + */ + WxImgProcQrCodeResult qrCode(String imgUrl) throws WxErrorException; + + /** + * 二维码/条码识别接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * 3.支持条码、二维码、DataMatrix和PDF417的识别。 + * 4.二维码、DataMatrix会返回位置坐标,条码和PDF417暂不返回位置坐标。 + * + * @param imgFile 图片文件对象 + * @return WxMpImgProcQrCodeResult + * @throws WxErrorException . + */ + WxImgProcQrCodeResult qrCode(File imgFile) throws WxErrorException; + + /** + * 图片高清化接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * 3.目前支持将图片超分辨率高清化2倍,即生成图片分辨率为原图2倍大小 + * 返回的media_id有效期为3天,期间可以通过“获取临时素材”接口获取图片二进制 + * + * @param imgUrl 图片url地址 + * @return WxMpImgProcSuperResolutionResult + * @throws WxErrorException . + */ + WxImgProcSuperResolutionResult superResolution(String imgUrl) throws WxErrorException; + + /** + * 图片高清化接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * 3.目前支持将图片超分辨率高清化2倍,即生成图片分辨率为原图2倍大小 + * 返回的media_id有效期为3天,期间可以通过“获取临时素材”接口获取图片二进制 + * + * @param imgFile 图片文件对象 + * @return WxMpImgProcSuperResolutionResult + * @throws WxErrorException . + */ + WxImgProcSuperResolutionResult superResolution(File imgFile) throws WxErrorException; + + /** + * 图片智能裁剪接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * 3.该接口默认使用最佳宽高比 + * @param imgUrl 图片url地址 + * @return WxMpImgProcAiCropResult + * @throws WxErrorException . + */ + WxImgProcAiCropResult aiCrop(String imgUrl) throws WxErrorException; + + /** + * 图片智能裁剪接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * @param imgUrl 图片url地址 + * @param ratios 宽高比,最多支持5个,请以英文逗号分隔 + * @return WxMpImgProcAiCropResult + * @throws WxErrorException . + */ + WxImgProcAiCropResult aiCrop(String imgUrl, String ratios) throws WxErrorException; + + /** + * 图片智能裁剪接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * 3.该接口默认使用最佳宽高比 + * @param imgFile 图片文件对象 + * @return WxMpImgProcAiCropResult + * @throws WxErrorException . + */ + WxImgProcAiCropResult aiCrop(File imgFile) throws WxErrorException; + + /** + * 图片智能裁剪接口 + * 说明: + * 1.图片支持使用img参数实时上传,也支持使用img_url参数传送图片地址,由微信后台下载图片进行识别 + * 2.文件大小限制:小于2M + * @param imgFile 图片文件对象 + * @param ratios 宽高比,最多支持5个,请以英文逗号分隔 + * @return WxMpImgProcAiCropResult + * @throws WxErrorException . + */ + WxImgProcAiCropResult aiCrop(File imgFile, String ratios) throws WxErrorException; +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageDuplicateChecker.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageDuplicateChecker.java index 831458a443..3993dab548 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageDuplicateChecker.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageDuplicateChecker.java @@ -2,22 +2,23 @@ /** *
- * 消息重复检查器
+ * 消息重复检查器.
  * 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次
  * 
+ * + * @author Daniel Qian */ public interface WxMessageDuplicateChecker { /** + * 判断消息是否重复. *

公众号的排重方式

- *

+ * *

普通消息:关于重试的消息排重,推荐使用msgid排重。文档参考

*

事件消息:关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。文档参考

- *

+ * *

企业号的排重方式

- *

- * 官方文档完全没有写,参照公众号的方式排重。 - *

+ *

官方文档完全没有写,参照公众号的方式排重。

*

或者可以采取更简单的方式,如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重

* * @param messageId messageId需要根据上面讲的方式构造 diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java index 1281e26012..d7ac36c7c6 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java @@ -6,46 +6,48 @@ /** *
- * 默认消息重复检查器
+ * 默认消息重复检查器.
  * 将每个消息id保存在内存里,每隔5秒清理已经过期的消息id,每个消息id的过期时间是15秒
  * 
+ * + * @author Daniel Qian */ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChecker { /** - * 一个消息ID在内存的过期时间:15秒 + * 一个消息ID在内存的过期时间:15秒. */ private final Long timeToLive; /** - * 每隔多少周期检查消息ID是否过期:5秒 + * 每隔多少周期检查消息ID是否过期:5秒. */ private final Long clearPeriod; /** - * 消息id->消息时间戳的map + * 消息id->消息时间戳的map. */ private final ConcurrentHashMap msgId2Timestamp = new ConcurrentHashMap<>(); /** - * 后台清理线程是否已经开启 + * 后台清理线程是否已经开启. */ private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false); /** - * WxMsgIdInMemoryDuplicateChecker构造函数 + * 无参构造方法. *
    * 一个消息ID在内存的过期时间:15秒
    * 每隔多少周期检查消息ID是否过期:5秒
    * 
*/ public WxMessageInMemoryDuplicateChecker() { - this.timeToLive = 15 * 1000l; - this.clearPeriod = 5 * 1000l; + this.timeToLive = 15 * 1000L; + this.clearPeriod = 5 * 1000L; } /** - * WxMsgIdInMemoryDuplicateChecker构造函数 + * 构造方法. * * @param timeToLive 一个消息ID在内存的过期时间:毫秒 * @param clearPeriod 每隔多少周期检查消息ID是否过期:毫秒 @@ -66,14 +68,15 @@ public void run() { while (true) { Thread.sleep(WxMessageInMemoryDuplicateChecker.this.clearPeriod); Long now = System.currentTimeMillis(); - for (Map.Entry entry : WxMessageInMemoryDuplicateChecker.this.msgId2Timestamp.entrySet()) { + for (Map.Entry entry : + WxMessageInMemoryDuplicateChecker.this.msgId2Timestamp.entrySet()) { if (now - entry.getValue() > WxMessageInMemoryDuplicateChecker.this.timeToLive) { WxMessageInMemoryDuplicateChecker.this.msgId2Timestamp.entrySet().remove(entry); } } } } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); } } }); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxOcrService.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxOcrService.java new file mode 100644 index 0000000000..480ed3e95b --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxOcrService.java @@ -0,0 +1,131 @@ +package me.chanjar.weixin.common.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.bean.ocr.WxOcrBankCardResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrBizLicenseResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrCommResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrDrivingLicenseResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrDrivingResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrIdCardResult; + +import java.io.File; + +/** + * 基于小程序或 H5 的身份证、银行卡、行驶证 OCR 识别. + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21516712284rHWMX + * + * @author Binary Wang + * @date 2019-06-22 + */ +public interface WxOcrService { + + /** + * 身份证OCR识别接口. + * + * @param imgUrl 图片url地址 + * @return WxMpOcrIdCardResult + * @throws WxErrorException . + */ + WxOcrIdCardResult idCard(String imgUrl) throws WxErrorException; + + /** + * 身份证OCR识别接口. + * + * @param imgFile 图片文件对象 + * @return WxMpOcrIdCardResult + * @throws WxErrorException . + */ + WxOcrIdCardResult idCard(File imgFile) throws WxErrorException; + + /** + * 银行卡OCR识别接口 + * 文件大小限制:小于2M + * @param imgUrl 图片url地址 + * @return WxMpOcrBankCardResult + * @throws WxErrorException . + */ + WxOcrBankCardResult bankCard(String imgUrl) throws WxErrorException; + + /** + * 银行卡OCR识别接口 + * 文件大小限制:小于2M + * @param imgFile 图片文件对象 + * @return WxMpOcrBankCardResult + * @throws WxErrorException . + */ + WxOcrBankCardResult bankCard(File imgFile) throws WxErrorException; + + /** + * 行驶证OCR识别接口 + * 文件大小限制:小于2M + * @param imgUrl 图片url地址 + * @return WxMpOcrDrivingResult + * @throws WxErrorException . + */ + WxOcrDrivingResult driving(String imgUrl) throws WxErrorException; + + /** + * 行驶证OCR识别接口 + * 文件大小限制:小于2M + * @param imgFile 图片文件对象 + * @return WxMpOcrDrivingResult + * @throws WxErrorException . + */ + WxOcrDrivingResult driving(File imgFile) throws WxErrorException; + + /** + * 驾驶证OCR识别接口 + * 文件大小限制:小于2M + * @param imgUrl 图片url地址 + * @return WxMpOcrDrivingLicenseResult + * @throws WxErrorException . + */ + WxOcrDrivingLicenseResult drivingLicense(String imgUrl) throws WxErrorException; + + /** + * 驾驶证OCR识别接口 + * 文件大小限制:小于2M + * @param imgFile 图片文件对象 + * @return WxMpOcrDrivingLicenseResult + * @throws WxErrorException . + */ + WxOcrDrivingLicenseResult drivingLicense(File imgFile) throws WxErrorException; + + /** + * 营业执照OCR识别接口 + * 文件大小限制:小于2M + * @param imgUrl 图片url地址 + * @return WxMpOcrBizLicenseResult + * @throws WxErrorException . + */ + WxOcrBizLicenseResult bizLicense(String imgUrl) throws WxErrorException; + + /** + * 营业执照OCR识别接口 + * 文件大小限制:小于2M + * @param imgFile 图片文件对象 + * @return WxMpOcrBizLicenseResult + * @throws WxErrorException . + */ + WxOcrBizLicenseResult bizLicense(File imgFile) throws WxErrorException; + + /** + * 通用印刷体OCR识别接口 + * 文件大小限制:小于2M + * 适用于屏幕截图、印刷体照片等场景 + * @param imgUrl 图片url地址 + * @return WxMpOcrCommResult + * @throws WxErrorException . + */ + WxOcrCommResult comm(String imgUrl) throws WxErrorException; + + /** + * 通用印刷体OCR识别接口 + * 文件大小限制:小于2M + * 适用于屏幕截图、印刷体照片等场景 + * @param imgFile 图片文件对象 + * @return WxMpOcrCommResult + * @throws WxErrorException . + */ + WxOcrCommResult comm(File imgFile) throws WxErrorException; +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxAccessToken.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxAccessToken.java index 6476205a54..3935e5f55d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxAccessToken.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxAccessToken.java @@ -1,9 +1,16 @@ package me.chanjar.weixin.common.bean; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - import java.io.Serializable; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * access token. + * + * @author Daniel Qian + */ +@Data public class WxAccessToken implements Serializable { private static final long serialVersionUID = 8709719312922168909L; @@ -15,20 +22,4 @@ public static WxAccessToken fromJson(String json) { return WxGsonBuilder.create().fromJson(json, WxAccessToken.class); } - public String getAccessToken() { - return this.accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public int getExpiresIn() { - return this.expiresIn; - } - - public void setExpiresIn(int expiresIn) { - this.expiresIn = expiresIn; - } - } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java index df272318e7..0b9f689bdc 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxCardApiSignature.java @@ -1,17 +1,18 @@ package me.chanjar.weixin.common.bean; -import me.chanjar.weixin.common.util.ToStringUtils; - import java.io.Serializable; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + /** - * 卡券Api签名 + * 卡券Api签名. * * @author YuJian * @version 15/11/8 */ +@Data public class WxCardApiSignature implements Serializable { - private static final long serialVersionUID = 158176707226975979L; private String appId; @@ -34,78 +35,6 @@ public class WxCardApiSignature implements Serializable { @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getAppId() { - return this.appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getCardId() { - return this.cardId; - } - - public void setCardId(String cardId) { - this.cardId = cardId; - } - - public String getCardType() { - return this.cardType; - } - - public void setCardType(String cardType) { - this.cardType = cardType; - } - - public String getLocationId() { - return this.locationId; - } - - public void setLocationId(String locationId) { - this.locationId = locationId; - } - - public String getCode() { - return this.code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getOpenId() { - return this.openId; - } - - public void setOpenId(String openId) { - this.openId = openId; - } - - public Long getTimestamp() { - return this.timestamp; - } - - public void setTimestamp(Long timestamp) { - this.timestamp = timestamp; - } - - public String getNonceStr() { - return this.nonceStr; - } - - public void setNonceStr(String nonceStr) { - this.nonceStr = nonceStr; - } - - public String getSignature() { - return this.signature; - } - - public void setSignature(String signature) { - this.signature = signature; + return WxGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxJsapiSignature.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxJsapiSignature.java index 7837158361..759f5e12fe 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxJsapiSignature.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxJsapiSignature.java @@ -2,9 +2,20 @@ import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + /** - * jspai signature + * jspai signature. + * + * @author Daniel Qian */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor public class WxJsapiSignature implements Serializable { private static final long serialVersionUID = -1116808193154384804L; @@ -18,43 +29,4 @@ public class WxJsapiSignature implements Serializable { private String signature; - public String getSignature() { - return this.signature; - } - - public void setSignature(String signature) { - this.signature = signature; - } - - public String getNonceStr() { - return nonceStr; - } - - public void setNonceStr(String nonceStr) { - this.nonceStr = nonceStr; - } - - public long getTimestamp() { - return this.timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxNetCheckResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxNetCheckResult.java new file mode 100644 index 0000000000..b5f5762cb3 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxNetCheckResult.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.common.bean; + +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 网络检测. + * @author billytomato + */ +@Data +public class WxNetCheckResult implements Serializable { + private static final long serialVersionUID = 6918924418847404172L; + + private List dnsInfos = new ArrayList<>(); + private List pingInfos = new ArrayList<>(); + + public static WxNetCheckResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxNetCheckResult.class); + } + + @Data + public static class WxNetCheckDnsInfo implements Serializable{ + private static final long serialVersionUID = 82631178029516008L; + private String ip; + private String realOperator; + } + + @Data + public static class WxNetCheckPingInfo implements Serializable{ + private static final long serialVersionUID = -1871970825359178319L; + private String ip; + private String fromOperator; + private String packageLoss; + private String time; + } +} + + diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcAiCropResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcAiCropResult.java new file mode 100644 index 0000000000..4cfc514d70 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcAiCropResult.java @@ -0,0 +1,69 @@ +package me.chanjar.weixin.common.bean.imgproc; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Theo Nie + */ +@Data +public class WxImgProcAiCropResult implements Serializable { + private static final long serialVersionUID = -6470673963772979463L; + + @SerializedName("img_size") + private ImgSize imgSize; + + @SerializedName("results") + private List results; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + public static WxImgProcAiCropResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxImgProcAiCropResult.class); + } + + @Data + public static class ImgSize implements Serializable { + private static final long serialVersionUID = -6470673963772979463L; + + @SerializedName("w") + private int w; + + @SerializedName("h") + private int h; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } + + @Data + public static class Results implements Serializable { + private static final long serialVersionUID = -6470673963772979463L; + + @SerializedName("crop_left") + private int cropLeft; + + @SerializedName("crop_top") + private int cropTop; + + @SerializedName("crop_right") + private int cropRight; + + @SerializedName("crop_bottom") + private int cropBottom; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcQrCodeResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcQrCodeResult.java new file mode 100644 index 0000000000..c257146092 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcQrCodeResult.java @@ -0,0 +1,103 @@ +package me.chanjar.weixin.common.bean.imgproc; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 二维码/条码识别返回结果 + * + * @author Theo Nie + */ +@Data +public class WxImgProcQrCodeResult implements Serializable { + private static final long serialVersionUID = -1194154790100866123L; + + @SerializedName("img_size") + private ImgSize imgSize; + + @SerializedName("code_results") + private List codeResults; + + @Data + public static class ImgSize implements Serializable { + private static final long serialVersionUID = -8847603245514017839L; + + @SerializedName("w") + private int w; + @SerializedName("h") + private int h; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } + + @Data + public static class CodeResults implements Serializable { + private static final long serialVersionUID = -6138135951229076759L; + + @SerializedName("type_name") + private String typeName; + + @SerializedName("data") + private String data; + + @SerializedName("pos") + private Pos pos; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + @Data + public static class Pos implements Serializable { + private static final long serialVersionUID = 7754894061212819602L; + @SerializedName("left_top") + private Coordinate leftTop; + + @SerializedName("right_top") + private Coordinate rightTop; + + @SerializedName("right_bottom") + private Coordinate rightBottom; + + @SerializedName("left_bottom") + private Coordinate leftBottom; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + @Data + public static class Coordinate implements Serializable { + private static final long serialVersionUID = 8930443668927359677L; + @SerializedName("x") + private int x; + + @SerializedName("y") + private int y; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } + } + } + + public static WxImgProcQrCodeResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxImgProcQrCodeResult.class); + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcSuperResolutionResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcSuperResolutionResult.java new file mode 100644 index 0000000000..2ce5d3829f --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/imgproc/WxImgProcSuperResolutionResult.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.common.bean.imgproc; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * 图片高清化返回结果 + * @author Theo Nie + */ +@Data +public class WxImgProcSuperResolutionResult implements Serializable { + private static final long serialVersionUID = 8007440280170407021L; + + @SerializedName("media_id") + private String mediaId; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + public static WxImgProcSuperResolutionResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxImgProcSuperResolutionResult.class); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java index 4e61a46591..ab00073378 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java @@ -1,8 +1,5 @@ package me.chanjar.weixin.common.bean.menu; -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - import java.io.InputStream; import java.io.InputStreamReader; import java.io.Serializable; @@ -10,13 +7,16 @@ import java.util.ArrayList; import java.util.List; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + /** - * 菜单(公众号和企业号共用的) + * 菜单(公众号和企业号共用的). * * @author Daniel Qian */ +@Data public class WxMenu implements Serializable { - private static final long serialVersionUID = -7083914585539687746L; private List buttons = new ArrayList<>(); @@ -36,23 +36,8 @@ public static WxMenu fromJson(String json) { * 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu */ public static WxMenu fromJson(InputStream is) { - return WxGsonBuilder.create().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); - } - - public List getButtons() { - return this.buttons; - } - - public void setButtons(List buttons) { - this.buttons = buttons; - } - - public WxMenuRule getMatchRule() { - return this.matchRule; - } - - public void setMatchRule(WxMenuRule matchRule) { - this.matchRule = matchRule; + return WxGsonBuilder.create() + .fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); } public String toJson() { @@ -61,7 +46,7 @@ public String toJson() { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return this.toJson(); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java index aa6ea4cfb2..114e267d41 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java @@ -1,18 +1,25 @@ package me.chanjar.weixin.common.bean.menu; -import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; - import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * menu button. + * + * @author Daniel Qian + */ +@Data public class WxMenuButton implements Serializable { private static final long serialVersionUID = -1070939403109776555L; /** *
-   * 菜单的响应动作类型:
+   * 菜单的响应动作类型.
    * view表示网页类型,
    * click表示点击类型,
    * miniprogram表示小程序类型
@@ -21,13 +28,13 @@ public class WxMenuButton implements Serializable {
   private String type;
 
   /**
-   * 菜单标题,不超过16个字节,子菜单不超过60个字节
+   * 菜单标题,不超过16个字节,子菜单不超过60个字节.
    */
   private String name;
 
   /**
    * 
-   * 菜单KEY值,用于消息接口推送,不超过128字节
+   * 菜单KEY值,用于消息接口推送,不超过128字节.
    * click等点击类型必须
    * 
*/ @@ -35,7 +42,8 @@ public class WxMenuButton implements Serializable { /** *
-   * 网页链接,用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。
+   * 网页链接.
+   * 用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。
    * view、miniprogram类型必须
    * 
*/ @@ -43,7 +51,7 @@ public class WxMenuButton implements Serializable { /** *
-   * 调用新增永久素材接口返回的合法media_id
+   * 调用新增永久素材接口返回的合法media_id.
    * media_id类型和view_limited类型必须
    * 
*/ @@ -52,7 +60,7 @@ public class WxMenuButton implements Serializable { /** *
-   * 小程序的appid
+   * 小程序的appid.
    * miniprogram类型必须
    * 
*/ @@ -61,7 +69,7 @@ public class WxMenuButton implements Serializable { /** *
-   * 小程序的页面路径
+   * 小程序的页面路径.
    * miniprogram类型必须
    * 
*/ @@ -73,70 +81,7 @@ public class WxMenuButton implements Serializable { @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getType() { - return this.type; - } - - public void setType(String type) { - this.type = type; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getKey() { - return this.key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getUrl() { - return this.url; + return WxGsonBuilder.create().toJson(this); } - public void setUrl(String url) { - this.url = url; - } - - public List getSubButtons() { - return this.subButtons; - } - - public void setSubButtons(List subButtons) { - this.subButtons = subButtons; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getPagePath() { - return pagePath; - } - - public void setPagePath(String pagePath) { - this.pagePath = pagePath; - } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuRule.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuRule.java index e0182c9678..49d4e891c4 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuRule.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuRule.java @@ -1,12 +1,24 @@ package me.chanjar.weixin.common.bean.menu; -import me.chanjar.weixin.common.util.ToStringUtils; - import java.io.Serializable; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * menu rule. + * + * @author Daniel Qian + */ +@Data public class WxMenuRule implements Serializable { private static final long serialVersionUID = -4587181819499286670L; + /** + * 变态的微信接口,反序列化时这里反人类的使用和序列化时不一样的名字. + */ + @SerializedName(value = "tag_id", alternate = "group_id") private String tagId; private String sex; private String country; @@ -15,64 +27,8 @@ public class WxMenuRule implements Serializable { private String clientPlatformType; private String language; - public String getTagId() { - return this.tagId; - } - - public void setTagId(String tagId) { - this.tagId = tagId; - } - - public String getSex() { - return this.sex; - } - - public void setSex(String sex) { - this.sex = sex; - } - - public String getCountry() { - return this.country; - } - - public void setCountry(String country) { - this.country = country; - } - - public String getProvince() { - return this.province; - } - - public void setProvince(String province) { - this.province = province; - } - - public String getCity() { - return this.city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getClientPlatformType() { - return this.clientPlatformType; - } - - public void setClientPlatformType(String clientPlatformType) { - this.clientPlatformType = clientPlatformType; - } - - public String getLanguage() { - return this.language; - } - - public void setLanguage(String language) { - this.language = language; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrBankCardResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrBankCardResult.java new file mode 100644 index 0000000000..d5ff0b901d --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrBankCardResult.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * 银行卡OCR识别结果 + * + * @author Theo Nie + */ +@Data +public class WxOcrBankCardResult implements Serializable { + + private static final long serialVersionUID = 554136620394204143L; + @SerializedName("number") + private String number; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + public static WxOcrBankCardResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOcrBankCardResult.class); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrBizLicenseResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrBizLicenseResult.java new file mode 100644 index 0000000000..2e83443e95 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrBizLicenseResult.java @@ -0,0 +1,108 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * @author Theo Nie + */ +@Data +public class WxOcrBizLicenseResult implements Serializable { + private static final long serialVersionUID = -5007671093920178291L; + + /** + * 注册号 + */ + @SerializedName("reg_num") + private String regNum; + /** + * 编号 + */ + @SerializedName("serial") + private String serial; + /** + * 法定代表人姓名 + */ + @SerializedName("legal_representative") + private String legalRepresentative; + /** + * 企业名称 + */ + @SerializedName("enterprise_name") + private String enterpriseName; + /** + * 组成形式 + */ + @SerializedName("type_of_organization") + private String typeOfOrganization; + /** + * 经营场所/企业住所 + */ + @SerializedName("address") + private String address; + /** + * 公司类型 + */ + @SerializedName("type_of_enterprise") + private String typeOfEnterprise; + /** + * 经营范围 + */ + @SerializedName("business_scope") + private String businessScope; + /** + * 注册资本 + */ + @SerializedName("registered_capital") + private String registeredCapital; + /** + * 实收资本 + */ + @SerializedName("paid_in_capital") + private String paidInCapital; + /** + * 营业期限 + */ + @SerializedName("valid_period") + private String validPeriod; + /** + * 注册日期/成立日期 + */ + @SerializedName("registered_date") + private String registeredDate; + /** + * 营业执照位置 + */ + @SerializedName("cert_position") + private CertPosition certPosition; + /** + * 图片大小 + */ + @SerializedName("img_size") + private WxOcrImgSize imgSize; + + public static WxOcrBizLicenseResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOcrBizLicenseResult.class); + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + @Data + public static class CertPosition implements Serializable { + private static final long serialVersionUID = 290286813344131863L; + + @SerializedName("pos") + private WxOcrPos pos; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrCommResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrCommResult.java new file mode 100644 index 0000000000..5f56d16e3c --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrCommResult.java @@ -0,0 +1,45 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Theo Nie + */ +@Data +public class WxOcrCommResult implements Serializable { + private static final long serialVersionUID = 455833771627756440L; + + @SerializedName("img_size") + private WxOcrImgSize imgSize; + @SerializedName("items") + private List items; + + public static WxOcrCommResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOcrCommResult.class); + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + @Data + public static class Items implements Serializable { + private static final long serialVersionUID = 3066181677009102791L; + + @SerializedName("text") + private String text; + @SerializedName("pos") + private WxOcrPos pos; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrDrivingLicenseResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrDrivingLicenseResult.java new file mode 100644 index 0000000000..c9306200de --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrDrivingLicenseResult.java @@ -0,0 +1,80 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * @author Theo Nie + */ +@Data +public class WxOcrDrivingLicenseResult implements Serializable { + private static final long serialVersionUID = -6984670645802585738L; + + /** + * 证号 + */ + @SerializedName("id_num") + private String idNum; + /** + * 姓名 + */ + @SerializedName("name") + private String name; + /** + * 性别 + */ + @SerializedName("sex") + private String sex; + /** + * 国籍 + */ + @SerializedName("nationality") + private String nationality; + /** + * 住址 + */ + @SerializedName("address") + private String address; + /** + * 出生日期 + */ + @SerializedName("birth_date") + private String birthDate; + /** + * 初次领证日期 + */ + @SerializedName("issue_date") + private String issueDate; + /** + * 准驾车型 + */ + @SerializedName("car_class") + private String carClass; + /** + * 有效期限起始日 + */ + @SerializedName("valid_from") + private String validFrom; + /** + * 有效期限终止日 + */ + @SerializedName("valid_to") + private String validTo; + /** + * 印章文字 + */ + @SerializedName("official_seal") + private String officialSeal; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + public static WxOcrDrivingLicenseResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOcrDrivingLicenseResult.class); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrDrivingResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrDrivingResult.java new file mode 100644 index 0000000000..b486baf1c4 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrDrivingResult.java @@ -0,0 +1,133 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * @author Theo Nie + */ +@Data +public class WxOcrDrivingResult implements Serializable { + private static final long serialVersionUID = -7477484374200211303L; + + /** + * 车牌号码 + */ + @SerializedName("plate_num") + private String plateNum; + /** + * 车辆类型 + */ + @SerializedName("vehicle_type") + private String vehicleType; + /** + * 所有人 + */ + @SerializedName("owner") + private String owner; + /** + * 住址 + */ + @SerializedName("addr") + private String addr; + /** + * 使用性质 + */ + @SerializedName("use_character") + private String useCharacter; + /** + * 品牌型号 + */ + @SerializedName("model") + private String model; + /** + * 车辆识别代码 + */ + @SerializedName("vin") + private String vin; + /** + * 发动机号码 + */ + @SerializedName("engine_num") + private String engineNum; + /** + * 注册日期 + */ + @SerializedName("register_date") + private String registerDate; + /** + * 发证日期 + */ + @SerializedName("issue_date") + private String issueDate; + /** + * 车牌号码 + */ + @SerializedName("plate_num_b") + private String plateNumB; + /** + * 号牌 + */ + @SerializedName("record") + private String record; + /** + * 核定载人数 + */ + @SerializedName("passengers_num") + private String passengersNum; + /** + * 总质量 + */ + @SerializedName("total_quality") + private String totalQuality; + /** + * 整备质量 + */ + @SerializedName("prepare_quality") + private String prepareQuality; + /** + * 外廓尺寸 + */ + @SerializedName("overall_size") + private String overallSize; + /** + * 卡片正面位置(检测到卡片正面才会返回) + */ + @SerializedName("card_position_front") + private CardPosition cardPositionFront; + /** + * 卡片反面位置(检测到卡片反面才会返回) + */ + @SerializedName("card_position_back") + private CardPosition cardPositionBack; + /** + * 图片大小 + */ + @SerializedName("img_size") + private WxOcrImgSize imgSize; + + @Data + public static class CardPosition implements Serializable { + private static final long serialVersionUID = 2884515165228160517L; + + @SerializedName("pos") + private WxOcrPos pos; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } + + public static WxOcrDrivingResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOcrDrivingResult.class); + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrIdCardResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrIdCardResult.java new file mode 100644 index 0000000000..0b1e0ff838 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrIdCardResult.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * OCR身份证识别结果. + * + * @author Binary Wang + * @date 2019-06-23 + */ +@Data +public class WxOcrIdCardResult implements Serializable { + private static final long serialVersionUID = 8184352486986729980L; + + @SerializedName("type") + private String type; + @SerializedName("name") + private String name; + @SerializedName("id") + private String id; + @SerializedName("valid_date") + private String validDate; + + public static WxOcrIdCardResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOcrIdCardResult.class); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrImgSize.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrImgSize.java new file mode 100644 index 0000000000..f5446ab405 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrImgSize.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * @author Theo Nie + */ +@Data +public class WxOcrImgSize implements Serializable { + private static final long serialVersionUID = 5234409123551074168L; + + @SerializedName("w") + private int w; + @SerializedName("h") + private int h; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrPos.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrPos.java new file mode 100644 index 0000000000..54089f3235 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/ocr/WxOcrPos.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.common.bean.ocr; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * @author Theo Nie + */ +@Data +public class WxOcrPos implements Serializable { + private static final long serialVersionUID = 4204160206873907920L; + + @SerializedName("left_top") + private Coordinate leftTop; + @SerializedName("right_top") + private Coordinate rightTop; + @SerializedName("right_bottom") + private Coordinate rightBottom; + @SerializedName("left_bottom") + private Coordinate leftBottom; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + @Data + public static class Coordinate implements Serializable { + private static final long serialVersionUID = 8675059935386304399L; + @SerializedName("x") + private int x; + @SerializedName("y") + private int y; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxError.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxError.java deleted file mode 100644 index 46c0ae89be..0000000000 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxError.java +++ /dev/null @@ -1,84 +0,0 @@ -package me.chanjar.weixin.common.bean.result; - -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - -import java.io.Serializable; - -/** - * 微信错误码说明,请阅读: 全局返回码说明 - * - * @author Daniel Qian - */ -public class WxError implements Serializable { - - private static final long serialVersionUID = 7869786563361406291L; - - private int errorCode; - - private String errorMsg; - - private String json; - - public static WxError fromJson(String json) { - return WxGsonBuilder.create().fromJson(json, WxError.class); - } - - public static Builder newBuilder() { - return new Builder(); - } - - public int getErrorCode() { - return this.errorCode; - } - - public void setErrorCode(int errorCode) { - this.errorCode = errorCode; - } - - public String getErrorMsg() { - return this.errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } - - public String getJson() { - return this.json; - } - - public void setJson(String json) { - this.json = json; - } - - @Override - public String toString() { - if (this.json != null) { - return this.json; - } - return "错误: Code=" + this.errorCode + ", Msg=" + this.errorMsg; - } - - public static class Builder { - private int errorCode; - private String errorMsg; - - public Builder setErrorCode(int errorCode) { - this.errorCode = errorCode; - return this; - } - - public Builder setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - return this; - } - - public WxError build() { - WxError wxError = new WxError(); - wxError.setErrorCode(this.errorCode); - wxError.setErrorMsg(this.errorMsg); - return wxError; - } - - } -} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java index 09901c3e0e..a62bf3c605 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java @@ -1,12 +1,19 @@ package me.chanjar.weixin.common.bean.result; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - import java.io.Serializable; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * + * @author Daniel Qian + */ +@Data public class WxMediaUploadResult implements Serializable { private static final long serialVersionUID = 330834334738622341L; + private String url; private String type; private String mediaId; private String thumbMediaId; @@ -16,42 +23,9 @@ public static WxMediaUploadResult fromJson(String json) { return WxGsonBuilder.create().fromJson(json, WxMediaUploadResult.class); } - public String getType() { - return this.type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public long getCreatedAt() { - return this.createdAt; - } - - public void setCreatedAt(long createdAt) { - this.createdAt = createdAt; - } - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - @Override public String toString() { - return "WxUploadResult [type=" + this.type + ", media_id=" + this.mediaId + ", thumb_media_id=" + this.thumbMediaId - + ", created_at=" + this.createdAt + "]"; + return WxGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/enums/TicketType.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/enums/TicketType.java new file mode 100644 index 0000000000..afbd1ec382 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/enums/TicketType.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.common.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + *
+ * ticket类型枚举
+ * Created by Binary Wang on 2018/11/18.
+ * 
+ * + * @author Binary Wang + */ +@Getter +@RequiredArgsConstructor +public enum TicketType { + /** + * jsapi + */ + JSAPI("jsapi"), + /** + * sdk + */ + SDK("2"), + /** + * 微信卡券 + */ + WX_CARD("wx_card"); + + /** + * type代码 + */ + private final String code; + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/enums/WxType.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/enums/WxType.java new file mode 100644 index 0000000000..de047beb1d --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/enums/WxType.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.common.enums; + +/** + *
+ *  微信类型枚举.
+ *  Created by BinaryWang on 2018/5/14.
+ * 
+ * + * @author Binary Wang + */ +public enum WxType { + /** + * 企业微信. + */ + CP, + /** + * 微信公众号. + */ + MP, + /** + * 微信小程序. + */ + MiniApp, + /** + * 微信开放平台. + */ + Open, + /** + * 微信支付. + */ + Pay; +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java new file mode 100644 index 0000000000..c742959bb6 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java @@ -0,0 +1,1137 @@ +package me.chanjar.weixin.common.error; + +import lombok.Getter; + +/** + *
+ * 企业微信全局错误码.
+ * 参考文档:企业微信全局错误码
+ * Created by Binary Wang on 2018/5/13.
+ * 
+ * + * @author Binary Wang + */ +@Getter +public enum WxCpErrorMsgEnum { + /** + * 系统繁忙;服务器暂不可用,建议稍候重试。建议重试次数不超过3次. + */ + CODE_1(-1, "系统繁忙;服务器暂不可用,建议稍候重试。建议重试次数不超过3次。"), + /** + * 请求成功;接口调用成功. + */ + CODE_0(0, "请求成功;接口调用成功"), + /** + * 不合法的secret参数;secret在应用详情/通讯录管理助手可查看. + */ + CODE_40001(40001, "不合法的secret参数;secret在应用详情/通讯录管理助手可查看"), + /** + * 无效的UserID. + */ + CODE_40003(40003, "无效的UserID"), + /** + * 不合法的媒体文件类型;不满足系统文件要求。参考:上传的媒体文件限制. + */ + CODE_40004(40004, "不合法的媒体文件类型;不满足系统文件要求。参考:上传的媒体文件限制"), + /** + * 不合法的type参数;合法的type取值,参考:上传临时素材. + */ + CODE_40005(40005, "不合法的type参数;合法的type取值,参考:上传临时素材"), + /** + * 不合法的文件大小;系统文件要求,参考:上传的媒体文件限制. + */ + CODE_40006(40006, "不合法的文件大小;系统文件要求,参考:上传的媒体文件限制"), + /** + * 不合法的media_id参数. + */ + CODE_40007(40007, "不合法的media_id参数"), + /** + * 不合法的msgtype参数;合法的msgtype取值,参考:消息类型. + */ + CODE_40008(40008, "不合法的msgtype参数;合法的msgtype取值,参考:消息类型"), + /** + * 上传图片大小不是有效值;图片大小的系统限制,参考上传的媒体文件限制. + */ + CODE_40009(40009, "上传图片大小不是有效值;图片大小的系统限制,参考上传的媒体文件限制"), + /** + * 上传视频大小不是有效值;视频大小的系统限制,参考上传的媒体文件限制. + */ + CODE_40011(40011, "上传视频大小不是有效值;视频大小的系统限制,参考上传的媒体文件限制"), + /** + * 不合法的CorpID;需确认CorpID是否填写正确,在 web管理端-设置 可查看. + */ + CODE_40013(40013, "不合法的CorpID;需确认CorpID是否填写正确,在 web管理端-设置 可查看"), + /** + * 不合法的access_token. + */ + CODE_40014(40014, "不合法的access_token"), + /** + * 不合法的按钮个数;菜单按钮1-3个. + */ + CODE_40016(40016, "不合法的按钮个数;菜单按钮1-3个"), + /** + * 不合法的按钮类型;支持的类型,参考:按钮类型. + */ + CODE_40017(40017, "不合法的按钮类型;支持的类型,参考:按钮类型"), + /** + * 不合法的按钮名字长度;长度应不超过16个字节. + */ + CODE_40018(40018, "不合法的按钮名字长度;长度应不超过16个字节"), + /** + * 不合法的按钮KEY长度;长度应不超过128字节. + */ + CODE_40019(40019, "不合法的按钮KEY长度;长度应不超过128字节"), + /** + * 不合法的按钮URL长度;长度应不超过1024字节. + */ + CODE_40020(40020, "不合法的按钮URL长度;长度应不超过1024字节"), + /** + * 不合法的子菜单级数;只能包含一级菜单和二级菜单. + */ + CODE_40022(40022, "不合法的子菜单级数;只能包含一级菜单和二级菜单"), + /** + * 不合法的子菜单按钮个数;子菜单按钮1-5个. + */ + CODE_40023(40023, "不合法的子菜单按钮个数;子菜单按钮1-5个"), + /** + * 不合法的子菜单按钮类型;支持的类型,参考:按钮类型. + */ + CODE_40024(40024, "不合法的子菜单按钮类型;支持的类型,参考:按钮类型"), + /** + * 不合法的子菜单按钮名字长度;支持的类型,参考:按钮类型. + */ + CODE_40025(40025, "不合法的子菜单按钮名字长度;支持的类型,参考:按钮类型"), + /** + * 不合法的子菜单按钮KEY长度;长度应不超过60个字节. + */ + CODE_40026(40026, "不合法的子菜单按钮KEY长度;长度应不超过60个字节"), + /** + * 不合法的子菜单按钮URL长度;长度应不超过1024字节. + */ + CODE_40027(40027, "不合法的子菜单按钮URL长度;长度应不超过1024字节"), + /** + * 不合法的oauth_code. + */ + CODE_40029(40029, "不合法的oauth_code"), + /** + * 不合法的UserID列表;指定的UserID列表,至少存在一个UserID不在通讯录中. + */ + CODE_40031(40031, "不合法的UserID列表;指定的UserID列表,至少存在一个UserID不在通讯录中"), + /** + * 不合法的UserID列表长度. + */ + CODE_40032(40032, "不合法的UserID列表长度"), + /** + * 不合法的请求字符;不能包含\\uxxxx格式的字符. + */ + CODE_40033(40033, "不合法的请求字符;不能包含\\uxxxx格式的字符"), + /** + * 不合法的参数. + */ + CODE_40035(40035, "不合法的参数"), + /** + * chatid不存在;会话需要先创建后,才可修改会话详情或者发起聊天. + */ + CODE_40050(40050, "chatid不存在;会话需要先创建后,才可修改会话详情或者发起聊天"), + /** + * 不合法的子菜单url域名. + */ + CODE_40054(40054, "不合法的子菜单url域名"), + /** + * 不合法的菜单url域名. + */ + CODE_40055(40055, "不合法的菜单url域名"), + /** + * 不合法的agentid. + */ + CODE_40056(40056, "不合法的agentid"), + /** + * 不合法的callbackurl或者callbackurl验证失败;可自助到开发调试工具重现. + */ + CODE_40057(40057, "不合法的callbackurl或者callbackurl验证失败;可自助到开发调试工具重现"), + /** + * 不合法的参数;传递参数不符合系统要求,需要参照具体API接口说明. + */ + CODE_40058(40058, "不合法的参数;传递参数不符合系统要求,需要参照具体API接口说明"), + /** + * 不合法的上报地理位置标志位;开关标志位只能填 0 或者 1. + */ + CODE_40059(40059, "不合法的上报地理位置标志位;开关标志位只能填 0 或者 1"), + /** + * 参数为空. + */ + CODE_40063(40063, "参数为空"), + /** + * 不合法的部门列表;部门列表为空,或者至少存在一个部门ID不存在于通讯录中. + */ + CODE_40066(40066, "不合法的部门列表;部门列表为空,或者至少存在一个部门ID不存在于通讯录中"), + /** + * 不合法的标签ID;标签ID未指定,或者指定的标签ID不存在. + */ + CODE_40068(40068, "不合法的标签ID;标签ID未指定,或者指定的标签ID不存在"), + /** + * 指定的标签范围结点全部无效. + */ + CODE_40070(40070, "指定的标签范围结点全部无效"), + /** + * 不合法的标签名字;标签名字已经存在. + */ + CODE_40071(40071, "不合法的标签名字;标签名字已经存在"), + /** + * 不合法的标签名字长度;不允许为空,最大长度限制为32个字(汉字或英文字母). + */ + CODE_40072(40072, "不合法的标签名字长度;不允许为空,最大长度限制为32个字(汉字或英文字母)"), + /** + * 不合法的openid;openid不存在,需确认获取来源. + */ + CODE_40073(40073, "不合法的openid;openid不存在,需确认获取来源"), + /** + * news消息不支持保密消息类型;图文消息支持保密类型需改用mpnews. + */ + CODE_40074(40074, "news消息不支持保密消息类型;图文消息支持保密类型需改用mpnews"), + /** + * 不合法的pre_auth_code参数;预授权码不存在,参考:获取预授权码. + */ + CODE_40077(40077, "不合法的pre_auth_code参数;预授权码不存在,参考:获取预授权码"), + /** + * 不合法的auth_code参数;需确认获取来源,并且只能消费一次. + */ + CODE_40078(40078, "不合法的auth_code参数;需确认获取来源,并且只能消费一次"), + /** + * 不合法的suite_secret;套件secret可在第三方管理端套件详情查看. + */ + CODE_40080(40080, "不合法的suite_secret;套件secret可在第三方管理端套件详情查看"), + /** + * 不合法的suite_token. + */ + CODE_40082(40082, "不合法的suite_token"), + /** + * 不合法的suite_id;suite_id不存在. + */ + CODE_40083(40083, "不合法的suite_id;suite_id不存在"), + /** + * 不合法的permanent_code参数. + */ + CODE_40084(40084, "不合法的permanent_code参数"), + /** + * 不合法的的suite_ticket参数;suite_ticket不存在或者已失效. + */ + CODE_40085(40085, "不合法的的suite_ticket参数;suite_ticket不存在或者已失效"), + /** + * 不合法的第三方应用appid;至少有一个不存在应用id. + */ + CODE_40086(40086, "不合法的第三方应用appid;至少有一个不存在应用id"), + /** + * jobid不存在;请检查 jobid 来源. + */ + CODE_40088(40088, "jobid不存在;请检查 jobid 来源"), + /** + * 批量任务的结果已清理;系统仅保存最近5次批量任务的结果。可在通讯录查看实际导入情况. + */ + CODE_40089(40089, "批量任务的结果已清理;系统仅保存最近5次批量任务的结果。可在通讯录查看实际导入情况"), + /** + * secret不合法;可能用了别的企业的secret. + */ + CODE_40091(40091, "secret不合法;可能用了别的企业的secret"), + /** + * 导入文件存在不合法的内容. + */ + CODE_40092(40092, "导入文件存在不合法的内容"), + /** + * 不合法的jsapi_ticket参数;ticket已失效,或者拼写错误. + */ + CODE_40093(40093, "不合法的jsapi_ticket参数;ticket已失效,或者拼写错误"), + /** + * 不合法的URL;缺少主页URL参数,或者URL不合法(链接需要带上协议头,以 http:// 或者 https:// 开头). + */ + CODE_40094(40094, "不合法的URL;缺少主页URL参数,或者URL不合法(链接需要带上协议头,以 http:// 或者 https:// 开头)"), + /** + * 缺少access_token参数. + */ + CODE_41001(41001, "缺少access_token参数"), + /** + * 缺少corpid参数. + */ + CODE_41002(41002, "缺少corpid参数"), + /** + * 缺少secret参数. + */ + CODE_41004(41004, "缺少secret参数"), + /** + * 缺少media_id参数;media_id为调用接口必填参数,请确认是否有传递. + */ + CODE_41006(41006, "缺少media_id参数;media_id为调用接口必填参数,请确认是否有传递"), + /** + * 缺少auth code参数. + */ + CODE_41008(41008, "缺少auth code参数"), + /** + * 缺少userid参数. + */ + CODE_41009(41009, "缺少userid参数"), + /** + * 缺少url参数. + */ + CODE_41010(41010, "缺少url参数"), + /** + * 缺少agentid参数. + */ + CODE_41011(41011, "缺少agentid参数"), + /** + * 缺少 description 参数;发送文本卡片消息接口,description 是必填字段. + */ + CODE_41033(41033, "缺少 description 参数;发送文本卡片消息接口,description 是必填字段"), + /** + * 缺少title参数;发送图文消息,标题是必填参数。请确认参数是否有传递. + */ + CODE_41016(41016, "缺少title参数;发送图文消息,标题是必填参数。请确认参数是否有传递。"), + /** + * 缺少 department 参数. + */ + CODE_41019(41019, "缺少 department 参数"), + /** + * 缺少tagid参数. + */ + CODE_41017(41017, "缺少tagid参数"), + /** + * 缺少suite_id参数. + */ + CODE_41021(41021, "缺少suite_id参数"), + /** + * 缺少suite_access_token参数. + */ + CODE_41022(41022, "缺少suite_access_token参数"), + /** + * 缺少suite_ticket参数. + */ + CODE_41023(41023, "缺少suite_ticket参数"), + /** + * 缺少secret参数. + */ + CODE_41024(41024, "缺少secret参数"), + /** + * 缺少permanent_code参数. + */ + CODE_41025(41025, "缺少permanent_code参数"), + /** + * access_token已过期;access_token有时效性,需要重新获取一次. + */ + CODE_42001(42001, "access_token已过期;access_token有时效性,需要重新获取一次"), + /** + * pre_auth_code已过期;pre_auth_code有时效性,需要重新获取一次. + */ + CODE_42007(42007, "pre_auth_code已过期;pre_auth_code有时效性,需要重新获取一次"), + /** + * suite_access_token已过期;suite_access_token有时效性,需要重新获取一次. + */ + CODE_42009(42009, "suite_access_token已过期;suite_access_token有时效性,需要重新获取一次"), + /** + * 指定的userid未绑定微信或未关注微信插件;需要成员使用微信登录企业微信或者关注微信插件才能获取openid. + */ + CODE_43004(43004, "指定的userid未绑定微信或未关注微信插件;需要成员使用微信登录企业微信或者关注微信插件才能获取openid"), + /** + * 多媒体文件为空;上传格式参考:上传临时素材,确认header和body的内容正确. + */ + CODE_44001(44001, "多媒体文件为空;上传格式参考:上传临时素材,确认header和body的内容正确。"), + /** + * 文本消息content参数为空;发文本消息content为必填参数,且不能为空. + */ + CODE_44004(44004, "文本消息content参数为空;发文本消息content为必填参数,且不能为空"), + /** + * 多媒体文件大小超过限制;图片不可超过5M;音频不可超过5M;文件不可超过20M. + */ + CODE_45001(45001, "多媒体文件大小超过限制;图片不可超过5M;音频不可超过5M;文件不可超过20M"), + /** + * 消息内容大小超过限制. + */ + CODE_45002(45002, "消息内容大小超过限制"), + /** + * 应用description参数长度不符合系统限制;设置应用若带有description参数,则长度必须为4至120个字符. + */ + CODE_45004(45004, "应用description参数长度不符合系统限制;设置应用若带有description参数,则长度必须为4至120个字符"), + /** + * 语音播放时间超过限制;语音播放时长不能超过60秒. + */ + CODE_45007(45007, "语音播放时间超过限制;语音播放时长不能超过60秒"), + /** + * 图文消息的文章数量不符合系统限制;图文消息的文章数量不能超过8条. + */ + CODE_45008(45008, "图文消息的文章数量不符合系统限制;图文消息的文章数量不能超过8条"), + /** + * 接口调用超过限制. + */ + CODE_45009(45009, "接口调用超过限制"), + /** + * 应用name参数长度不符合系统限制;设置应用若带有name参数,则不允许为空,且不超过32个字符. + */ + CODE_45022(45022, "应用name参数长度不符合系统限制;设置应用若带有name参数,则不允许为空,且不超过32个字符"), + /** + * 帐号数量超过上限. + */ + CODE_45024(45024, "帐号数量超过上限"), + /** + * 触发删除用户数的保护;限制参考:全量覆盖成员. + */ + CODE_45026(45026, "触发删除用户数的保护;限制参考:全量覆盖成员"), + /** + * 图文消息author参数长度超过限制;最长64个字节. + */ + CODE_45032(45032, "图文消息author参数长度超过限制;最长64个字节"), + /** + * 接口并发调用超过限制. + */ + CODE_45033(45033, "接口并发调用超过限制"), + /** + * 菜单未设置;菜单需发布后才能获取到数据. + */ + CODE_46003(46003, "菜单未设置;菜单需发布后才能获取到数据"), + /** + * 指定的用户不存在;需要确认指定的用户存在于通讯录中. + */ + CODE_46004(46004, "指定的用户不存在;需要确认指定的用户存在于通讯录中"), + /** + * API接口无权限调用. + */ + CODE_48002(48002, "API接口无权限调用"), + /** + * 不合法的suite_id;确认suite_access_token由指定的suite_id生成. + */ + CODE_48003(48003, "不合法的suite_id;确认suite_access_token由指定的suite_id生成"), + /** + * 授权关系无效;可能是无授权或授权已被取消. + */ + CODE_48004(48004, "授权关系无效;可能是无授权或授权已被取消"), + /** + * API接口已废弃;接口已不再支持,建议改用新接口或者新方案. + */ + CODE_48005(48005, "API接口已废弃;接口已不再支持,建议改用新接口或者新方案"), + /** + * redirect_url未登记可信域名. + */ + CODE_50001(50001, "redirect_url未登记可信域名"), + /** + * 成员不在权限范围;请检查应用或管理组的权限范围. + */ + CODE_50002(50002, "成员不在权限范围;请检查应用或管理组的权限范围"), + /** + * 应用已禁用;禁用的应用无法使用API接口。可在”管理端-企业应用”启用应用. + */ + CODE_50003(50003, "应用已禁用;禁用的应用无法使用API接口。可在”管理端-企业应用”启用应用"), + /** + * 部门长度不符合限制;部门名称不能为空且长度不能超过32个字. + */ + CODE_60001(60001, "部门长度不符合限制;部门名称不能为空且长度不能超过32个字"), + /** + * 部门ID不存在;需要确认部门ID是否有带,并且存在通讯录中. + */ + CODE_60003(60003, "部门ID不存在;需要确认部门ID是否有带,并且存在通讯录中"), + /** + * 父部门不存在;需要确认父亲部门ID是否有带,并且存在通讯录中. + */ + CODE_60004(60004, "父部门不存在;需要确认父亲部门ID是否有带,并且存在通讯录中"), + /** + * 部门下存在成员;不允许删除有成员的部门. + */ + CODE_60005(60005, "部门下存在成员;不允许删除有成员的部门"), + /** + * 部门下存在子部门;不允许删除有子部门的部门. + */ + CODE_60006(60006, "部门下存在子部门;不允许删除有子部门的部门"), + /** + * 不允许删除根部门. + */ + CODE_60007(60007, "不允许删除根部门"), + /** + * 部门已存在;部门ID或者部门名称已存在. + */ + CODE_60008(60008, "部门已存在;部门ID或者部门名称已存在"), + /** + * 部门名称含有非法字符;不能含有 \\:?*“< >| 等字符. + */ + CODE_60009(60009, "部门名称含有非法字符;不能含有 \\ :?*“< >| 等字符"), + /** + * 部门存在循环关系. + */ + CODE_60010(60010, "部门存在循环关系"), + /** + * 指定的成员/部门/标签参数无权限. + */ + CODE_60011(60011, "指定的成员/部门/标签参数无权限"), + /** + * 不允许删除默认应用;默认应用的id为0. + */ + CODE_60012(60012, "不允许删除默认应用;默认应用的id为0"), + /** + * 访问ip不在白名单之中;请确认访问ip是否在服务商白名单IP列表. + */ + CODE_60020(60020, "访问ip不在白名单之中;请确认访问ip是否在服务商白名单IP列表"), + /** + * 不允许修改第三方应用的主页 URL;第三方应用类型,不允许通过接口修改该应用的主页 URL. + */ + CODE_60028(60028, "不允许修改第三方应用的主页 URL;第三方应用类型,不允许通过接口修改该应用的主页 URL"), + /** + * UserID已存在. + */ + CODE_60102(60102, "UserID已存在"), + /** + * 手机号码不合法;长度不超过32位,字符仅支持数字,加号和减号. + */ + CODE_60103(60103, "手机号码不合法;长度不超过32位,字符仅支持数字,加号和减号"), + /** + * 手机号码已存在;同一个企业内,成员的手机号不能重复。建议更换手机号,或者更新已有的手机记录. + */ + CODE_60104(60104, "手机号码已存在;同一个企业内,成员的手机号不能重复。建议更换手机号,或者更新已有的手机记录。"), + /** + * 邮箱不合法;长度不超过64位,且为有效的email格式. + */ + CODE_60105(60105, "邮箱不合法;长度不超过64位,且为有效的email格式"), + /** + * 邮箱已存在;同一个企业内,成员的邮箱不能重复。建议更换邮箱,或者更新已有的邮箱记录. + */ + CODE_60106(60106, "邮箱已存在;同一个企业内,成员的邮箱不能重复。建议更换邮箱,或者更新已有的邮箱记录。"), + /** + * 微信号不合法;微信号格式由字母、数字、”-“、”_“组成,长度为 3-20 字节,首字符必须是字母或”-“或”_“. + */ + CODE_60107(60107, "微信号不合法;微信号格式由字母、数字、”-“、”_“组成,长度为 3-20 字节,首字符必须是字母或”-“或”_“"), + /** + * 用户所属部门数量超过限制;用户同时归属部门不超过20个. + */ + CODE_60110(60110, "用户所属部门数量超过限制;用户同时归属部门不超过20个"), + /** + * UserID不存在;UserID参数为空,或者不存在通讯录中. + */ + CODE_60111(60111, "UserID不存在;UserID参数为空,或者不存在通讯录中"), + /** + * 成员name参数不合法;不能为空,且不能超过64字符. + */ + CODE_60112(60112, "成员name参数不合法;不能为空,且不能超过64字符"), + /** + * 无效的部门id;部门不存在通讯录中. + */ + CODE_60123(60123, "无效的部门id;部门不存在通讯录中"), + /** + * 无效的父部门id;父部门不存在通讯录中. + */ + CODE_60124(60124, "无效的父部门id;父部门不存在通讯录中"), + /** + * 非法部门名字;不能为空,且不能超过64字节,且不能含有\\:*?”< >|等字符. + */ + CODE_60125(60125, "非法部门名字;不能为空,且不能超过64字节,且不能含有\\:*?”< >|等字符"), + /** + * 缺少department参数. + */ + CODE_60127(60127, "缺少department参数"), + /** + * 成员手机和邮箱都为空;成员手机和邮箱至少有个非空. + */ + CODE_60129(60129, "成员手机和邮箱都为空;成员手机和邮箱至少有个非空"), + /** + * 发票已被其他公众号锁定. + */ + CODE_72023(72023, "发票已被其他公众号锁定"), + /** + * 发票状态错误;reimburse_status状态错误,参考:更新发票状态. + */ + CODE_72024(72024, "发票状态错误;reimburse_status状态错误,参考:更新发票状态"), + /** + * 存在发票不属于该用户;只能批量更新该openid的发票,参考:批量更新发票状态. + */ + CODE_72037(72037, "存在发票不属于该用户;只能批量更新该openid的发票,参考:批量更新发票状态"), + /** + * 可信域名不正确,或者无ICP备案. + */ + CODE_80001(80001, "可信域名不正确,或者无ICP备案"), + /** + * 部门下的结点数超过限制(3W). + */ + CODE_81001(81001, "部门下的结点数超过限制(3W)"), + /** + * 部门最多15层. + */ + CODE_81002(81002, "部门最多15层"), + /** + * 无权限操作标签. + */ + CODE_81011(81011, "无权限操作标签"), + /** + * UserID、部门ID、标签ID全部非法或无权限. + */ + CODE_81013(81013, "UserID、部门ID、标签ID全部非法或无权限"), + /** + * 标签添加成员,单次添加user或party过多. + */ + CODE_81014(81014, "标签添加成员,单次添加user或party过多"), + /** + * 指定的成员/部门/标签全部无效. + */ + CODE_82001(82001, "指定的成员/部门/标签全部无效"), + /** + * 不合法的PartyID列表长度;发消息,单次不能超过100个部门. + */ + CODE_82002(82002, "不合法的PartyID列表长度;发消息,单次不能超过100个部门"), + /** + * 不合法的TagID列表长度;发消息,单次不能超过100个标签. + */ + CODE_82003(82003, "不合法的TagID列表长度;发消息,单次不能超过100个标签"), + /** + * 成员票据过期. + */ + CODE_84014(84014, "成员票据过期"), + /** + * 成员票据无效;确认user_ticket参数来源是否正确。参考接口:根据code获取成员信息. + */ + CODE_84015(84015, "成员票据无效;确认user_ticket参数来源是否正确。参考接口:根据code获取成员信息"), + /** + * 缺少templateid参数. + */ + CODE_84019(84019, "缺少templateid参数"), + /** + * templateid不存在;确认参数是否有带,并且已创建. + */ + CODE_84020(84020, "templateid不存在;确认参数是否有带,并且已创建"), + /** + * 缺少register_code参数. + */ + CODE_84021(84021, "缺少register_code参数"), + /** + * 无效的register_code参数. + */ + CODE_84022(84022, "无效的register_code参数"), + /** + * 不允许调用设置通讯录同步完成接口. + */ + CODE_84023(84023, "不允许调用设置通讯录同步完成接口"), + /** + * 无注册信息. + */ + CODE_84024(84024, "无注册信息"), + /** + * 不符合的state参数;必须是[a-zA-Z0-9]的参数值,长度不可超过128个字节. + */ + CODE_84025(84025, "不符合的state参数;必须是[a-zA-Z0-9]的参数值,长度不可超过128个字节"), + /** + * 缺少caller参数. + */ + CODE_84052(84052, "缺少caller参数"), + /** + * 缺少callee参数. + */ + CODE_84053(84053, "缺少callee参数"), + /** + * 缺少auth_corpid参数. + */ + CODE_84054(84054, "缺少auth_corpid参数"), + /** + * 超过拨打公费电话频率。排查方法:同一个客服5秒内只能调用api拨打一次公费电话 + */ + CODE_84055(84055, "超过拨打公费电话频率。排查方法:同一个客服5秒内只能调用api拨打一次公费电话"), + /** + * 被拨打用户安装应用时未授权拨打公费电话权限. + */ + CODE_84056(84056, "被拨打用户安装应用时未授权拨打公费电话权限"), + /** + * 公费电话余额不足. + */ + CODE_84057(84057, "公费电话余额不足"), + /** + * caller + */ + CODE_84058(84058, "caller 呼叫号码不支持"), + /** + * 号码非法. + */ + CODE_84059(84059, "号码非法"), + /** + * callee + */ + CODE_84060(84060, "callee 呼叫号码不支持"), + /** + * 不存在外部联系人的关系. + */ + CODE_84061(84061, "不存在外部联系人的关系"), + /** + * 未开启公费电话应用. + */ + CODE_84062(84062, "未开启公费电话应用"), + /** + * caller不存在. + */ + CODE_84063(84063, "caller不存在"), + /** + * callee不存在. + */ + CODE_84064(84064, "callee不存在"), + /** + * caller跟callee电话号码一致。排查方法:不允许自己拨打给自己 + */ + CODE_84065(84065, "caller跟callee电话号码一致。排查方法:不允许自己拨打给自己"), + /** + * 服务商拨打次数超过限制。排查方法:单个企业管理员,在一天(以上午10 + */ + CODE_84066(84066, "服务商拨打次数超过限制。排查方法:单个企业管理员,在一天(以上午10:00为起始时间)内,对应单个服务商,只能被呼叫【4】次。"), + /** + * 管理员收到的服务商公费电话个数超过限制。排查方法:单个企业管理员,在一天(以上午10 + */ + CODE_84067(84067, "管理员收到的服务商公费电话个数超过限制。排查方法:单个企业管理员,在一天(以上午10:00为起始时间)内,一共只能被【3】个服务商成功呼叫。"), + /** + * 拨打方被限制拨打公费电话. + */ + CODE_84069(84069, "拨打方被限制拨打公费电话"), + /** + * 不支持的电话号码。排查方法:拨打方或者被拨打方电话号码不支持 + */ + CODE_84070(84070, "不支持的电话号码。排查方法:拨打方或者被拨打方电话号码不支持"), + /** + * 不合法的外部联系人授权码。排查方法:非法或者已经消费过 + */ + CODE_84071(84071, "不合法的外部联系人授权码。排查方法:非法或者已经消费过"), + /** + * 应用未配置客服. + */ + CODE_84072(84072, "应用未配置客服"), + /** + * 客服userid不在应用配置的客服列表中. + */ + CODE_84073(84073, "客服userid不在应用配置的客服列表中"), + /** + * 没有外部联系人权限. + */ + CODE_84074(84074, "没有外部联系人权限"), + /** + * 不合法或过期的authcode. + */ + CODE_84075(84075, "不合法或过期的authcode"), + /** + * 缺失authcode. + */ + CODE_84076(84076, "缺失authcode"), + /** + * 订单价格过高,无法受理. + */ + CODE_84077(84077, "订单价格过高,无法受理"), + /** + * 购买人数不正确. + */ + CODE_84078(84078, "购买人数不正确"), + /** + * 价格策略不存在. + */ + CODE_84079(84079, "价格策略不存在"), + /** + * 订单不存在. + */ + CODE_84080(84080, "订单不存在"), + /** + * 存在未支付订单. + */ + CODE_84081(84081, "存在未支付订单"), + /** + * 存在申请退款中的订单. + */ + CODE_84082(84082, "存在申请退款中的订单"), + /** + * 非服务人员. + */ + CODE_84083(84083, "非服务人员"), + /** + * 非跟进用户. + */ + CODE_84084(84084, "非跟进用户"), + /** + * 应用已下架. + */ + CODE_84085(84085, "应用已下架"), + /** + * 订单人数超过可购买最大人数. + */ + CODE_84086(84086, "订单人数超过可购买最大人数"), + /** + * 打开订单支付前禁止关闭订单. + */ + CODE_84087(84087, "打开订单支付前禁止关闭订单"), + /** + * 禁止关闭已支付的订单. + */ + CODE_84088(84088, "禁止关闭已支付的订单"), + /** + * 订单已支付. + */ + CODE_84089(84089, "订单已支付"), + /** + * 缺失user_ticket. + */ + CODE_84090(84090, "缺失user_ticket"), + /** + * 订单价格不可低于下限. + */ + CODE_84091(84091, "订单价格不可低于下限"), + /** + * 无法发起代下单操作. + */ + CODE_84092(84092, "无法发起代下单操作"), + /** + * 代理关系已占用,无法代下单. + */ + CODE_84093(84093, "代理关系已占用,无法代下单"), + /** + * 该应用未配置代理分润规则,请先联系应用服务商处理. + */ + CODE_84094(84094, "该应用未配置代理分润规则,请先联系应用服务商处理"), + /** + * 免费试用版,无法扩容. + */ + CODE_84095(84095, "免费试用版,无法扩容"), + /** + * 免费试用版,无法续期. + */ + CODE_84096(84096, "免费试用版,无法续期"), + /** + * 当前企业有未处理订单. + */ + CODE_84097(84097, "当前企业有未处理订单"), + /** + * 固定总量,无法扩容. + */ + CODE_84098(84098, "固定总量,无法扩容"), + /** + * 非购买状态,无法扩容. + */ + CODE_84099(84099, "非购买状态,无法扩容"), + /** + * 未购买过此应用,无法续期. + */ + CODE_84100(84100, "未购买过此应用,无法续期"), + /** + * 企业已试用付费版本,无法全新购买. + */ + CODE_84101(84101, "企业已试用付费版本,无法全新购买"), + /** + * 企业当前应用状态已过期,无法扩容. + */ + CODE_84102(84102, "企业当前应用状态已过期,无法扩容"), + /** + * 仅可修改未支付订单. + */ + CODE_84103(84103, "仅可修改未支付订单"), + /** + * 订单已支付,无法修改. + */ + CODE_84104(84104, "订单已支付,无法修改"), + /** + * 订单已被取消,无法修改. + */ + CODE_84105(84105, "订单已被取消,无法修改"), + /** + * 企业含有该应用的待支付订单,无法代下单. + */ + CODE_84106(84106, "企业含有该应用的待支付订单,无法代下单"), + /** + * 企业含有该应用的退款中订单,无法代下单. + */ + CODE_84107(84107, "企业含有该应用的退款中订单,无法代下单"), + /** + * 企业含有该应用的待生效订单,无法代下单. + */ + CODE_84108(84108, "企业含有该应用的待生效订单,无法代下单"), + /** + * 订单定价不能未0. + */ + CODE_84109(84109, "订单定价不能未0"), + /** + * 新安装应用不在试用状态,无法升级为付费版. + */ + CODE_84110(84110, "新安装应用不在试用状态,无法升级为付费版"), + /** + * 无足够可用优惠券. + */ + CODE_84111(84111, "无足够可用优惠券"), + /** + * 无法关闭未支付订单. + */ + CODE_84112(84112, "无法关闭未支付订单"), + /** + * 无付费信息. + */ + CODE_84113(84113, "无付费信息"), + /** + * 虚拟版本不支持下单. + */ + CODE_84114(84114, "虚拟版本不支持下单"), + /** + * 虚拟版本不支持扩容. + */ + CODE_84115(84115, "虚拟版本不支持扩容"), + /** + * 虚拟版本不支持续期. + */ + CODE_84116(84116, "虚拟版本不支持续期"), + /** + * 在虚拟正式版期内不能扩容. + */ + CODE_84117(84117, "在虚拟正式版期内不能扩容"), + /** + * 虚拟正式版期内不能变更版本. + */ + CODE_84118(84118, "虚拟正式版期内不能变更版本"), + /** + * 当前企业未报备,无法进行代下单. + */ + CODE_84119(84119, "当前企业未报备,无法进行代下单"), + /** + * 当前应用版本已删除. + */ + CODE_84120(84120, "当前应用版本已删除"), + /** + * 应用版本已删除,无法扩容. + */ + CODE_84121(84121, "应用版本已删除,无法扩容"), + /** + * 应用版本已删除,无法续期. + */ + CODE_84122(84122, "应用版本已删除,无法续期"), + /** + * 非虚拟版本,无法升级. + */ + CODE_84123(84123, "非虚拟版本,无法升级"), + /** + * 非行业方案订单,不能添加部分应用版本的订单. + */ + CODE_84124(84124, "非行业方案订单,不能添加部分应用版本的订单"), + /** + * 购买人数不能少于最少购买人数. + */ + CODE_84125(84125, "购买人数不能少于最少购买人数"), + /** + * 购买人数不能多于最大购买人数. + */ + CODE_84126(84126, "购买人数不能多于最大购买人数"), + /** + * 无应用管理权限. + */ + CODE_84127(84127, "无应用管理权限"), + /** + * 无该行业方案下全部应用的管理权限. + */ + CODE_84128(84128, "无该行业方案下全部应用的管理权限"), + /** + * 付费策略已被删除,无法下单. + */ + CODE_84129(84129, "付费策略已被删除,无法下单"), + /** + * 订单生效时间不合法. + */ + CODE_84130(84130, "订单生效时间不合法"), + /** + * 文件转译解析错误。排查方法:只支持utf8文件转译,可能是不支持的文件类型或者格式 + */ + CODE_84200(84200, "文件转译解析错误。排查方法:只支持utf8文件转译,可能是不支持的文件类型或者格式"), + /** + * 包含不合法的词语. + */ + CODE_85002(85002, "包含不合法的词语"), + /** + * 每企业每个月设置的可信域名不可超过20个. + */ + CODE_85004(85004, "每企业每个月设置的可信域名不可超过20个"), + /** + * 可信域名未通过所有权校验. + */ + CODE_85005(85005, "可信域名未通过所有权校验"), + /** + * 参数 chatid 不合法. + */ + CODE_86001(86001, "参数 chatid 不合法"), + /** + * 参数 chatid 不存在. + */ + CODE_86003(86003, "参数 chatid 不存在"), + /** + * 参数 群名不合法. + */ + CODE_86004(86004, "参数 群名不合法"), + /** + * 参数 群主不合法. + */ + CODE_86005(86005, "参数 群主不合法"), + /** + * 群成员数过多或过少. + */ + CODE_86006(86006, "群成员数过多或过少"), + /** + * 不合法的群成员. + */ + CODE_86007(86007, "不合法的群成员"), + /** + * 非法操作非自己创建的群. + */ + CODE_86008(86008, "非法操作非自己创建的群"), + /** + * 存在非法会话成员ID. + */ + CODE_86216(86216, "存在非法会话成员ID"), + /** + * 会话发送者不在会话成员列表中;会话的发送者,必须是会话的成员列表之一. + */ + CODE_86217(86217, "会话发送者不在会话成员列表中;会话的发送者,必须是会话的成员列表之一"), + /** + * 指定的会话参数不合法. + */ + CODE_86220(86220, "指定的会话参数不合法"), + /** + * 未认证摇一摇周边. + */ + CODE_90001(90001, "未认证摇一摇周边"), + /** + * 缺少摇一摇周边ticket参数. + */ + CODE_90002(90002, "缺少摇一摇周边ticket参数"), + /** + * 摇一摇周边ticket参数不合法. + */ + CODE_90003(90003, "摇一摇周边ticket参数不合法"), + /** + * 非法的对外属性类型. + */ + CODE_90100(90100, "非法的对外属性类型"), + /** + * 对外属性:文本类型长度不合法;文本长度不可超过12个UTF8字符. + */ + CODE_90101(90101, "对外属性:文本类型长度不合法;文本长度不可超过12个UTF8字符"), + /** + * 对外属性:网页类型标题长度不合法;标题长度不可超过12个UTF8字符. + */ + CODE_90102(90102, "对外属性:网页类型标题长度不合法;标题长度不可超过12个UTF8字符"), + /** + * 对外属性:网页url不合法. + */ + CODE_90103(90103, "对外属性:网页url不合法"), + /** + * 对外属性:小程序类型标题长度不合法;标题长度不可超过12个UTF8字符. + */ + CODE_90104(90104, "对外属性:小程序类型标题长度不合法;标题长度不可超过12个UTF8字符"), + /** + * 对外属性:小程序类型pagepath不合法. + */ + CODE_90105(90105, "对外属性:小程序类型pagepath不合法"), + /** + * 对外属性:请求参数不合法. + */ + CODE_90106(90106, "对外属性:请求参数不合法"), + /** + * 获取ticket的类型无效. + */ + CODE_91040(91040, "获取ticket的类型无效"), + /** + * 无权限操作指定的应用. + */ + CODE_301002(301002, "无权限操作指定的应用"), + /** + * 不允许删除创建者;创建者不允许从通讯录中删除。如果需要删除该成员,需要先在WEB管理端转移创建者身份. + */ + CODE_301005(301005, "不允许删除创建者;创建者不允许从通讯录中删除。如果需要删除该成员,需要先在WEB管理端转移创建者身份。"), + /** + * 参数 position 不合法;长度不允许超过128个字符. + */ + CODE_301012(301012, "参数 position 不合法;长度不允许超过128个字符"), + /** + * 参数 telephone 不合法;telephone必须由1-32位的纯数字或’-‘号组成. + */ + CODE_301013(301013, "参数 telephone 不合法;telephone必须由1-32位的纯数字或’-‘号组成。"), + /** + * 参数 english_name 不合法;参数如果有传递,不允许为空字符串,同时不能超过64字节,只能是由字母、数字、点(.)、减号(-)、空格或下划线(_)组成. + */ + CODE_301014(301014, "参数 english_name 不合法;参数如果有传递,不允许为空字符串,同时不能超过64字节,只能是由字母、数字、点(.)、减号(-)、空格或下划线(_)组成"), + /** + * 参数 mediaid 不合法;请检查 mediaid 来源,应该通过上传临时素材的图片类型获得mediaid. + */ + CODE_301015(301015, "参数 mediaid 不合法;请检查 mediaid 来源,应该通过上传临时素材的图片类型获得mediaid"), + /** + * 上传语音文件不符合系统要求;语音文件的系统限制,参考上传的媒体文件限制. + */ + CODE_301016(301016, "上传语音文件不符合系统要求;语音文件的系统限制,参考上传的媒体文件限制"), + /** + * 上传语音文件仅支持AMR格式;语音文件的系统限制,参考上传的媒体文件限制. + */ + CODE_301017(301017, "上传语音文件仅支持AMR格式;语音文件的系统限制,参考上传的媒体文件限制"), + /** + * 参数 userid 无效;至少有一个userid不存在于通讯录中. + */ + CODE_301021(301021, "参数 userid 无效;至少有一个userid不存在于通讯录中"), + /** + * 获取打卡数据失败;系统失败,可重试处理. + */ + CODE_301022(301022, "获取打卡数据失败;系统失败,可重试处理"), + /** + * useridlist非法或超过限额;列表数量不能为0且不超过100. + */ + CODE_301023(301023, "useridlist非法或超过限额;列表数量不能为0且不超过100"), + /** + * 获取打卡记录时间间隔超限;保证开始时间大于0 且结束时间大于 0 且结束时间大于开始时间,且间隔少于93天. + */ + CODE_301024(301024, "获取打卡记录时间间隔超限;保证开始时间大于0 且结束时间大于 0 且结束时间大于开始时间,且间隔少于93天"), + /** + * 提交审批单请求参数错误 + */ + CODE_301025(301025,"提交审批单请求参数错误"), + /** + * 不允许更新该用户的userid. + */ + CODE_301036(301036, "不允许更新该用户的userid"), + /** + * 无审批应用权限,或者提单者不在审批应用/自建应用的可见范围 + */ + CODE_301055(301055,"无审批应用权限,或者提单者不在审批应用/自建应用的可见范围"), + /** + * 审批应用已停用 + */ + CODE_301056(301056,"审批应用已停用"), + /** + * 通用错误码,提交审批单内部接口失败 + */ + CODE_301057(301057,"通用错误码,提交审批单内部接口失败"), + /** + * 批量导入任务的文件中userid有重复. + */ + CODE_302003(302003, "批量导入任务的文件中userid有重复"), + /** + * 组织架构不合法(1不是一棵树,2 多个一样的partyid,3 partyid空,4 partyid name 空,5 同一个父节点下有两个子节点 部门名字一样 可能是以上情况,请一一排查). + */ + CODE_302004(302004, "组织架构不合法(1不是一棵树,2 多个一样的partyid,3 partyid空,4 partyid name 空,5 同一个父节点下有两个子节点 部门名字一样 可能是以上情况,请一一排查)"), + /** + * 批量导入系统失败,请重新尝试导入. + */ + CODE_302005(302005, "批量导入系统失败,请重新尝试导入"), + /** + * 批量导入任务的文件中partyid有重复. + */ + CODE_302006(302006, "批量导入任务的文件中partyid有重复"), + /** + * 批量导入任务的文件中,同一个部门下有两个子部门名字一样. + */ + CODE_302007(302007, "批量导入任务的文件中,同一个部门下有两个子部门名字一样"), + /** + * CorpId参数无效;指定的CorpId不存在. + */ + CODE_2000002(2000002, "CorpId参数无效;指定的CorpId不存在"); + + private int code; + private String msg; + + WxCpErrorMsgEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + /** + * 通过错误代码查找其中文含义.. + */ + public static String findMsgByCode(int code) { + for (WxCpErrorMsgEnum value : WxCpErrorMsgEnum.values()) { + if (value.code == code) { + return value.msg; + } + } + + return null; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java new file mode 100644 index 0000000000..f1be843827 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java @@ -0,0 +1,94 @@ +package me.chanjar.weixin.common.error; + +import lombok.Builder; +import lombok.Data; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * 微信错误码. + * 请阅读: + * 公众平台:全局返回码说明 + * 企业微信:全局错误码 + * + * @author Daniel Qian & Binary Wang + */ +@Data +@Builder +public class WxError implements Serializable { + private static final long serialVersionUID = 7869786563361406291L; + + /** + * 微信错误代码. + */ + private int errorCode; + + /** + * 微信错误信息. + * (如果可以翻译为中文,就为中文) + */ + private String errorMsg; + + /** + * 微信接口返回的错误原始信息(英文). + */ + private String errorMsgEn; + + private String json; + + public static WxError fromJson(String json) { + return fromJson(json, null); + } + + public static WxError fromJson(String json, WxType type) { + final WxError wxError = WxGsonBuilder.create().fromJson(json, WxError.class); + if (wxError.getErrorCode() == 0 || type == null) { + return wxError; + } + + if (StringUtils.isNotEmpty(wxError.getErrorMsg())) { + wxError.setErrorMsgEn(wxError.getErrorMsg()); + } + + switch (type) { + case MP: { + final String msg = WxMpErrorMsgEnum.findMsgByCode(wxError.getErrorCode()); + if (msg != null) { + wxError.setErrorMsg(msg); + } + break; + } + case CP: { + final String msg = WxCpErrorMsgEnum.findMsgByCode(wxError.getErrorCode()); + if (msg != null) { + wxError.setErrorMsg(msg); + } + break; + } + case MiniApp: { + final String msg = WxMaErrorMsgEnum.findMsgByCode(wxError.getErrorCode()); + if (msg != null) { + wxError.setErrorMsg(msg); + } + break; + } + default: + return wxError; + } + + return wxError; + } + + @Override + public String toString() { + if (this.json == null) { + return "错误代码:" + this.errorCode + ", 错误信息:" + this.errorMsg; + } + + return "错误代码:" + this.errorCode + ", 错误信息:" + this.errorMsg + ",微信原始报文:" + this.json; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/exception/WxErrorException.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxErrorException.java similarity index 81% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/exception/WxErrorException.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxErrorException.java index 4038e60185..6e9a2c538d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/exception/WxErrorException.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxErrorException.java @@ -1,9 +1,9 @@ -package me.chanjar.weixin.common.exception; - -import me.chanjar.weixin.common.bean.result.WxError; +package me.chanjar.weixin.common.error; +/** + * @author Daniel Qian + */ public class WxErrorException extends Exception { - private static final long serialVersionUID = -6357149550353160810L; private WxError error; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java new file mode 100644 index 0000000000..eced6027e9 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java @@ -0,0 +1,490 @@ +package me.chanjar.weixin.common.error; + +import lombok.Getter; + +/** + * 微信小程序错误码 + * + * @author biggates + */ +@Getter +public enum WxMaErrorMsgEnum { + /** + *
+   * 获取 access_token 时 AppSecret 错误,
+   * 或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的小程序调用接口
+   * 对应操作:sendCustomerMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
+   * 
+ */ + CODE_40001(40001, "access_token 无效或 AppSecret 错误"), + /** + *
+   * 不合法的凭证类型
+   * 对应操作:sendCustomerMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
+   * 
+ */ + CODE_40002(40002, "不合法的凭证类型"), + /** + *
+   * touser不是正确的openid.
+   * 对应操作:sendCustomerMessage, sendUniformMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+   * 
+ */ + CODE_40003(40003, "openid 不正确"), + /** + *
+   * 无效媒体文件类型
+   * 对应操作:uploadTempMedia
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/uploadTempMedia.html
+   * 
+ */ + CODE_40004(40004, "无效媒体文件类型"), + /** + *
+   * 无效媒体文件 ID.
+   * 对应操作:getTempMedia
+   * 对应地址:
+   * GET https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/getTempMedia.html
+   * 
+ */ + CODE_40007(40007, "无效媒体文件 ID"), + /** + *
+   * appid不正确,或者不符合绑定关系要求.
+   * 对应操作:sendUniformMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+   * 
+ */ + CODE_40013(40013, "appid不正确,或者不符合绑定关系要求"), + /** + *
+   * template_id 不正确.
+   * 对应操作:sendUniformMessage, sendTemplateMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
+   * 
+ */ + CODE_40037(40037, "template_id 不正确"), + /** + *
+   * form_id不正确,或者过期.
+   * 对应操作:sendUniformMessage, sendTemplateMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
+   * 
+ */ + CODE_41028(41028, "form_id 不正确,或者过期"), + /** + *
+   * code 或 template_id 不正确.
+   * 对应操作:code2Session, sendUniformMessage, sendTemplateMessage
+   * 对应地址:
+   * GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
+   * 
+ */ + CODE_41029(41029, "请求的参数不正确"), + /** + *
+   * form_id 已被使用,或者所传page页面不存在,或者小程序没有发布
+   * 对应操作:sendUniformMessage, getWXACodeUnlimit
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+   *  https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html
+   * 
+ */ + CODE_41030(41030, "请求的参数不正确"), + /** + *
+   * 调用分钟频率受限.
+   * 对应操作:getWXACodeUnlimit, sendUniformMessage, sendTemplateMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html
+   * 
+ */ + CODE_45009(45009, "调用分钟频率受限"), + /** + *
+   * 频率限制,每个用户每分钟100次.
+   * 对应操作:code2Session
+   * 对应地址:
+   * GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html
+   * 
+ */ + CODE_45011(45011, "频率限制,每个用户每分钟100次"), + /** + *
+   * 回复时间超过限制.
+   * 对应操作:sendCustomerMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
+   * 
+ */ + CODE_45015(45015, "回复时间超过限制"), + /** + *
+   * 接口调用超过限额, 或生成码个数总和到达最大个数限制.
+   * 对应操作:createWXAQRCode, sendTemplateMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACode.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
+   * 
+ */ + CODE_45029(45029, "接口调用超过限额"), + /** + *
+   * 客服接口下行条数超过上限.
+   * 对应操作:sendCustomerMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
+   * 
+ */ + CODE_45047(45047, "客服接口下行条数超过上限"), + /** + *
+   * command字段取值不对
+   * 对应操作:customerTyping
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html
+   * 
+ */ + CODE_45072(45072, "command字段取值不对"), + /** + *
+   * 下发输入状态,需要之前30秒内跟用户有过消息交互.
+   * 对应操作:customerTyping
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html
+   */
+  CODE_45080(45080, "下发输入状态,需要之前30秒内跟用户有过消息交互"),
+  /**
+   * 
+   * 已经在输入状态,不可重复下发.
+   * 对应操作:customerTyping
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html
+   * 
+ */ + CODE_45081(45081, "已经在输入状态,不可重复下发"), + /** + *
+   * API 功能未授权,请确认小程序已获得该接口.
+   * 对应操作:sendCustomerMessage
+   * 对应地址:
+   * POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
+   * 
+ */ + CODE_48001(48001, "API 功能未授权"), + /** + *
+   * 内容含有违法违规内容.
+   * 对应操作:imgSecCheck, msgSecCheck
+   * 对应地址:
+   * POST https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN
+   * POST https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN
+   * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/imgSecCheck.html
+   * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/msgSecCheck.html
+   * 
+ */ + CODE_87014(87014, "内容含有违法违规内容"), + /** + * 系统繁忙,此时请开发者稍候再试. + */ + CODE_MINUS_1(-1, "系统繁忙,此时请开发者稍候再试"), + /** + * code 无效. + */ + CODE_40029(40029, "code 无效"), + /** + * access_token 过期. + */ + CODE_42001(42001, "access_token 过期"), + /** + * post 数据为空. + */ + CODE_44002(44002, "post 数据为空"), + /** + * post 数据中参数缺失. + */ + CODE_47001(47001, "post 数据中参数缺失"), + /** + * 参数 activity_id 错误. + */ + CODE_47501(47501, "参数 activity_id 错误"), + /** + * 参数 target_state 错误. + */ + CODE_47502(47502, "参数 target_state 错误"), + /** + * 参数 version_type 错误. + */ + CODE_47503(47503, "参数 version_type 错误"), + /** + * activity_id 过期. + */ + CODE_47504(47504, "activity_id 过期"), + /** + * 没有绑定开放平台帐号. + */ + CODE_89002(89002, "没有绑定开放平台帐号"), + /** + * 订单无效. + */ + CODE_89300(89300, "订单无效"), + + /** + * 代小程序实现业务的错误码,部分和小程序业务一致 + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Intro.html + */ + CODE_85060(85060, "无效的taskid"), + + CODE_85027(85027, "身份证绑定管理员名额达到上限"), + + CODE_85061(85061, "手机号绑定管理员名额达到上限"), + + CODE_85026(85026, "微信号绑定管理员名额达到上限"), + + CODE_85063(85063, "身份证黑名单"), + + CODE_85062(85062, "手机号黑名单"), + + CODE_85016(85016, "域名数量超过限制"), + + CODE_85017(85017, "没有新增域名,请确认小程序已经添加了域名或该域名是否没有在第三方平台添加"), + + CODE_85018(85018, "域名没有在第三方平台设置"), + + CODE_89019(89019, "业务域名无更改,无需重复设置"), + + CODE_89020(89020, "尚未设置小程序业务域名,请先在第三方平台中设置小程序业务域名后在调用本接口"), + + CODE_89021(89021, "请求保存的域名不是第三方平台中已设置的小程序业务域名或子域名"), + + CODE_89029(89029, "业务域名数量超过限制"), + + CODE_89231(89231, "个人小程序不支持调用 setwebviewdomain 接口"), + + CODE_91001(91001, "不是公众号快速创建的小程序"), + + CODE_91002(91002, "小程序发布后不可改名"), + + CODE_91003(91003, "改名状态不合法"), + + CODE_91004(91004, "昵称不合法"), + + CODE_91005(91005, "昵称 15 天主体保护"), + + CODE_91006(91006, "昵称命中微信号"), + + CODE_91007(91007, "昵称已被占用"), + + CODE_91008(91008, "昵称命中 7 天侵权保护期"), + + CODE_91009(91009, "需要提交材料"), + + CODE_91010(91010, "其他错误"), + + CODE_91011(91011, "查不到昵称修改审核单信息"), + + CODE_91012(91012, "其他错误"), + + CODE_91013(91013, "占用名字过多"), + + CODE_91014(91014, "+号规则 同一类型关联名主体不一致"), + + CODE_91015(91015, "原始名不同类型主体不一致"), + + CODE_91016(91016, "名称占用者 ≥2"), + + CODE_91017(91017, "+号规则 不同类型关联名主体不一致"), + + CODE_40097(40097, "参数错误"), + + CODE_41006(41006, "media_id 不能为空"), + + CODE_46001(46001, "media_id 不存在"), + + CODE_40009(40009, "图片尺寸太大"), + + CODE_53202(53202, "本月头像修改次数已用完"), + + CODE_53200(53200, "本月功能介绍修改次数已用完"), + + CODE_53201(53201, "功能介绍内容命中黑名单关键字"), + + CODE_85083(85083, "搜索标记位被封禁,无法修改"), + + CODE_85084(85084, "非法的 status 值,只能填 0 或者 1"), + + CODE_85013(85013, "无效的自定义配置"), + + CODE_85014(85014, "无效的模版编号"), + + CODE_85043(85043, "模版错误"), + + CODE_85044(85044, "代码包超过大小限制"), + + CODE_85045(85045, "ext_json 有不存在的路径"), + + CODE_85046(85046, "tabBar 中缺少 path"), + + CODE_85047(85047, "pages 字段为空"), + + CODE_85048(85048, "ext_json 解析失败"), + + CODE_80082(80082, "没有权限使用该插件"), + + CODE_80067(80067, "找不到使用的插件"), + + CODE_80066(80066, "非法的插件版本"), + + CODE_86000(86000, "不是由第三方代小程序进行调用"), + + CODE_86001(86001, "不存在第三方的已经提交的代码"), + + CODE_85006(85006, "标签格式错误"), + + CODE_85007(85007, "页面路径错误"), + + CODE_85008(85008, "类目填写错误"), + + CODE_85009(85009, "已经有正在审核的版本"), + + CODE_85010(85010, "item_list 有项目为空"), + + CODE_85011(85011, "标题填写错误"), + + CODE_85023(85023, "审核列表填写的项目数不在 1-5 以内"), + + CODE_85077(85077, "小程序类目信息失效(类目中含有官方下架的类目,请重新选择类目)"), + + CODE_86002(86002, "小程序还未设置昵称、头像、简介。请先设置完后再重新提交"), + + CODE_85085(85085, "近 7 天提交审核的小程序数量过多,请耐心等待审核完毕后再次提交"), + + CODE_85086(85086, "提交代码审核之前需提前上传代码"), + + CODE_85087(85087, "小程序已使用 api navigateToMiniProgram,请声明跳转 appid 列表后再次提交"), + + CODE_85012(85012, "无效的审核 id"), + + CODE_87013(87013, "撤回次数达到上限(每天一次,每个月 10 次)"), + + CODE_85019(85019, "没有审核版本"), + + CODE_85020(85020, "审核状态未满足发布"), + + CODE_87011(87011, "现网已经在灰度发布,不能进行版本回退"), + + CODE_87012(87012, "该版本不能回退,可能的原因:1:无上一个线上版用于回退 2:此版本为已回退版本,不能回退 3:此版本为回退功能上线之前的版本,不能回退"), + + CODE_85079(85079, "小程序没有线上版本,不能进行灰度"), + + CODE_85080(85080, "小程序提交的审核未审核通过"), + + CODE_85081(85081, "无效的发布比例"), + + CODE_85082(85082, "当前的发布比例需要比之前设置的高"), + + CODE_85021(85021, "状态不可变"), + + CODE_85022(85022, "action 非法"), + + CODE_89401(89401, "系统不稳定,请稍后再试,如多次失败请通过社区反馈"), + + CODE_89402(89402, "该审核单不在待审核队列,请检查是否已提交审核或已审完"), + + CODE_89403(89403, "本单属于平台不支持加急种类,请等待正常审核流程"), + + CODE_89404(89404, "本单已加速成功,请勿重复提交"), + + CODE_89405(89405, "本月加急额度不足,请提升提审质量以获取更多额度"), + + CODE_85064(85064, "找不到模版/草稿"), + + CODE_85065(85065, "模版库已满"), + + /** + * 小程序订阅消息错误码 + * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html + */ + CODE_43101(43101, "用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系"), + + CODE_47003(47003, "模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错"), + + /** + * 小程序绑定体验者 + */ + CODE_85001(85001, "微信号不存在或微信号设置为不可搜索"), + + CODE_85002(85002, "小程序绑定的体验者数量达到上限"), + + CODE_85003(85003, "微信号绑定的小程序体验者达到上限"), + + CODE_85004(85004, "微信号已经绑定"), + +// CODE_504002(-504002, "云函数未找到 Function not found"), + ; + + private int code; + private String msg; + + WxMaErrorMsgEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + /** + * 通过错误代码查找其中文含义. + */ + public static String findMsgByCode(int code) { + for (WxMaErrorMsgEnum value : WxMaErrorMsgEnum.values()) { + if (value.code == code) { + return value.msg; + } + } + + return null; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMpErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMpErrorMsgEnum.java new file mode 100644 index 0000000000..486791986b --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMpErrorMsgEnum.java @@ -0,0 +1,667 @@ +package me.chanjar.weixin.common.error; + +import lombok.Getter; + +/** + *
+ * 微信公众平台全局返回码.
+ * 参考文档:公众平台全局返回码
+ * Created by Binary Wang on 2018/5/13.
+ * 
+ * + * @author Binary Wang + */ +@Getter +public enum WxMpErrorMsgEnum { + /** + * 系统繁忙,此时请开发者稍候再试. + */ + CODE_1(-1, "系统繁忙,此时请开发者稍候再试"), + /** + * 请求成功. + */ + CODE_0(0, "请求成功"), + /** + * 获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口. + */ + CODE_40001(40001, "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口"), + /** + * 不合法的凭证类型. + */ + CODE_40002(40002, "不合法的凭证类型"), + /** + * 不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID. + */ + CODE_40003(40003, "不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID"), + /** + * 不合法的媒体文件类型. + */ + CODE_40004(40004, "不合法的媒体文件类型"), + /** + * 不合法的文件类型. + */ + CODE_40005(40005, "不合法的文件类型"), + /** + * 不合法的文件大小. + */ + CODE_40006(40006, "不合法的文件大小"), + /** + * 不合法的媒体文件 id. + */ + CODE_40007(40007, "不合法的媒体文件 id"), + /** + * 不合法的消息类型. + */ + CODE_40008(40008, "不合法的消息类型"), + /** + * 不合法的图片文件大小. + */ + CODE_40009(40009, "不合法的图片文件大小"), + /** + * 不合法的语音文件大小. + */ + CODE_40010(40010, "不合法的语音文件大小"), + /** + * 不合法的视频文件大小. + */ + CODE_40011(40011, "不合法的视频文件大小"), + /** + * 不合法的缩略图文件大小. + */ + CODE_40012(40012, "不合法的缩略图文件大小"), + /** + * 不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写. + */ + CODE_40013(40013, "不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写"), + /** + * 不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口. + */ + CODE_40014(40014, "不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口"), + /** + * 不合法的菜单类型. + */ + CODE_40015(40015, "不合法的菜单类型"), + /** + * 不合法的按钮个数. + */ + CODE_40016(40016, "不合法的按钮个数"), + /** + * 不合法的按钮类型. + */ + CODE_40017(40017, "不合法的按钮类型"), + /** + * 不合法的按钮名字长度. + */ + CODE_40018(40018, "不合法的按钮名字长度"), + /** + * 不合法的按钮 KEY 长度. + */ + CODE_40019(40019, "不合法的按钮 KEY 长度"), + /** + * 不合法的按钮 URL 长度. + */ + CODE_40020(40020, "不合法的按钮 URL 长度"), + /** + * 不合法的菜单版本号. + */ + CODE_40021(40021, "不合法的菜单版本号"), + /** + * 不合法的子菜单级数. + */ + CODE_40022(40022, "不合法的子菜单级数"), + /** + * 不合法的子菜单按钮个数. + */ + CODE_40023(40023, "不合法的子菜单按钮个数"), + /** + * 不合法的子菜单按钮类型. + */ + CODE_40024(40024, "不合法的子菜单按钮类型"), + /** + * 不合法的子菜单按钮名字长度. + */ + CODE_40025(40025, "不合法的子菜单按钮名字长度"), + /** + * 不合法的子菜单按钮 KEY 长度. + */ + CODE_40026(40026, "不合法的子菜单按钮 KEY 长度"), + /** + * 不合法的子菜单按钮 URL 长度. + */ + CODE_40027(40027, "不合法的子菜单按钮 URL 长度"), + /** + * 不合法的自定义菜单使用用户. + */ + CODE_40028(40028, "不合法的自定义菜单使用用户"), + /** + * 不合法的 oauth_code. + */ + CODE_40029(40029, "不合法的 oauth_code"), + /** + * 不合法的 refresh_token. + */ + CODE_40030(40030, "不合法的 refresh_token"), + /** + * 不合法的 openid 列表. + */ + CODE_40031(40031, "不合法的 openid 列表"), + /** + * 不合法的 openid 列表长度. + */ + CODE_40032(40032, "不合法的 openid 列表长度"), + /** + * 不合法的请求字符,不能包含\\uxxxx 格式的字符. + */ + CODE_40033(40033, "不合法的请求字符,不能包含\\uxxxx 格式的字符"), + /** + * 不合法的参数. + */ + CODE_40035(40035, "不合法的参数"), + /** + * 不合法的请求格式. + */ + CODE_40038(40038, "不合法的请求格式"), + /** + * 不合法的 URL 长度. + */ + CODE_40039(40039, "不合法的 URL 长度"), + /** + * 不合法的分组 id. + */ + CODE_40050(40050, "不合法的分组 id"), + /** + * 分组名字不合法. + */ + CODE_40051(40051, "分组名字不合法"), + /** + * 删除单篇图文时,指定的 article_idx 不合法. + */ + CODE_40060(40060, "删除单篇图文时,指定的 article_idx 不合法"), + /** + * 分组名字不合法. + */ + CODE_40117(40117, "分组名字不合法"), + /** + * media_id 大小不合法. + */ + CODE_40118(40118, "media_id 大小不合法"), + /** + * button 类型错误. + */ + CODE_40119(40119, "button 类型错误"), + /** + * button 类型错误. + */ + CODE_40120(40120, "button 类型错误"), + /** + * 不合法的 media_id 类型. + */ + CODE_40121(40121, "不合法的 media_id 类型"), + /** + * 微信号不合法. + */ + CODE_40132(40132, "微信号不合法"), + /** + * 不支持的图片格式. + */ + CODE_40137(40137, "不支持的图片格式"), + /** + * 请勿添加其他公众号的主页链接. + */ + CODE_40155(40155, "请勿添加其他公众号的主页链接"), + /** + * 缺少 access_token 参数. + */ + CODE_41001(41001, "缺少 access_token 参数"), + /** + * 缺少 appid 参数. + */ + CODE_41002(41002, "缺少 appid 参数"), + /** + * 缺少 refresh_token 参数. + */ + CODE_41003(41003, "缺少 refresh_token 参数"), + /** + * 缺少 secret 参数. + */ + CODE_41004(41004, "缺少 secret 参数"), + /** + * 缺少多媒体文件数据. + */ + CODE_41005(41005, "缺少多媒体文件数据"), + /** + * 缺少 media_id 参数. + */ + CODE_41006(41006, "缺少 media_id 参数"), + /** + * 缺少子菜单数据. + */ + CODE_41007(41007, "缺少子菜单数据"), + /** + * 缺少 oauth code. + */ + CODE_41008(41008, "缺少 oauth code"), + /** + * 缺少 openid. + */ + CODE_41009(41009, "缺少 openid"), + /** + * access_token 超时,请检查 access_token 的有效期,请参考基础支持 - 获取 access_token 中,对 access_token 的详细机制说明. + */ + CODE_42001(42001, "access_token 超时,请检查 access_token 的有效期,请参考基础支持 - 获取 access_token 中,对 access_token 的详细机制说明"), + /** + * refresh_token 超时. + */ + CODE_42002(42002, "refresh_token 超时"), + /** + * oauth_code 超时. + */ + CODE_42003(42003, "oauth_code 超时"), + /** + * 用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权. + */ + CODE_42007(42007, "用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权"), + /** + * 需要 GET 请求. + */ + CODE_43001(43001, "需要 GET 请求"), + /** + * 需要 POST 请求. + */ + CODE_43002(43002, "需要 POST 请求"), + /** + * 需要 HTTPS 请求. + */ + CODE_43003(43003, "需要 HTTPS 请求"), + /** + * 需要接收者关注. + */ + CODE_43004(43004, "需要接收者关注"), + /** + * 需要好友关系. + */ + CODE_43005(43005, "需要好友关系"), + /** + * 需要将接收者从黑名单中移除. + */ + CODE_43019(43019, "需要将接收者从黑名单中移除"), + /** + * 多媒体文件为空. + */ + CODE_44001(44001, "多媒体文件为空"), + /** + * POST 的数据包为空. + */ + CODE_44002(44002, "POST 的数据包为空"), + /** + * 图文消息内容为空. + */ + CODE_44003(44003, "图文消息内容为空"), + /** + * 文本消息内容为空. + */ + CODE_44004(44004, "文本消息内容为空"), + /** + * 多媒体文件大小超过限制. + */ + CODE_45001(45001, "多媒体文件大小超过限制"), + /** + * 消息内容超过限制. + */ + CODE_45002(45002, "消息内容超过限制"), + /** + * 标题字段超过限制. + */ + CODE_45003(45003, "标题字段超过限制"), + /** + * 描述字段超过限制. + */ + CODE_45004(45004, "描述字段超过限制"), + /** + * 链接字段超过限制. + */ + CODE_45005(45005, "链接字段超过限制"), + /** + * 图片链接字段超过限制. + */ + CODE_45006(45006, "图片链接字段超过限制"), + /** + * 语音播放时间超过限制. + */ + CODE_45007(45007, "语音播放时间超过限制"), + /** + * 图文消息超过限制. + */ + CODE_45008(45008, "图文消息超过限制"), + /** + * 接口调用超过限制. + */ + CODE_45009(45009, "接口调用超过限制"), + /** + * 创建菜单个数超过限制. + */ + CODE_45010(45010, "创建菜单个数超过限制"), + /** + * API 调用太频繁,请稍候再试. + */ + CODE_45011(45011, "API 调用太频繁,请稍候再试"), + /** + * 回复时间超过限制. + */ + CODE_45015(45015, "回复时间超过限制"), + /** + * 系统分组,不允许修改. + */ + CODE_45016(45016, "系统分组,不允许修改"), + /** + * 分组名字过长. + */ + CODE_45017(45017, "分组名字过长"), + /** + * 分组数量超过上限. + */ + CODE_45018(45018, "分组数量超过上限"), + /** + * 客服接口下行条数超过上限. + */ + CODE_45047(45047, "客服接口下行条数超过上限"), + /** + * 非法的tag_id. + */ + CODE_45159(45159, "非法的tag_id"), + /** + * 不存在媒体数据. + */ + CODE_46001(46001, "不存在媒体数据"), + /** + * 不存在的菜单版本. + */ + CODE_46002(46002, "不存在的菜单版本"), + /** + * 不存在的菜单数据. + */ + CODE_46003(46003, "不存在的菜单数据"), + /** + * 不存在的用户. + */ + CODE_46004(46004, "不存在的用户"), + /** + * 解析 JSON/XML 内容错误. + */ + CODE_47001(47001, "解析 JSON/XML 内容错误"), + /** + * api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限. + */ + CODE_48001(48001, "api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限"), + /** + * 粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” ). + */ + CODE_48002(48002, "粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” )"), + /** + * api 接口被封禁,请登录 mp.weixin.qq.com 查看详情. + */ + CODE_48004(48004, "api 接口被封禁,请登录 mp.weixin.qq.com 查看详情"), + /** + * api 禁止删除被自动回复和自定义菜单引用的素材. + */ + CODE_48005(48005, "api 禁止删除被自动回复和自定义菜单引用的素材"), + /** + * api 禁止清零调用次数,因为清零次数达到上限. + */ + CODE_48006(48006, "api 禁止清零调用次数,因为清零次数达到上限"), + /** + * 没有该类型消息的发送权限. + */ + CODE_48008(48008, "没有该类型消息的发送权限"), + /** + * 用户未授权该 api. + */ + CODE_50001(50001, "用户未授权该 api"), + /** + * 用户受限,可能是违规后接口被封禁. + */ + CODE_50002(50002, "用户受限,可能是违规后接口被封禁"), + /** + * 用户未关注公众号. + */ + CODE_50005(50005, "用户未关注公众号"), + /** + * 参数错误 (invalid parameter). + */ + CODE_61451(61451, "参数错误 (invalid parameter)"), + /** + * 无效客服账号 (invalid kf_account). + */ + CODE_61452(61452, "无效客服账号 (invalid kf_account)"), + /** + * 客服帐号已存在 (kf_account exsited). + */ + CODE_61453(61453, "客服帐号已存在 (kf_account exsited)"), + /** + * 客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length). + */ + CODE_61454(61454, "客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length)"), + /** + * 客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account). + */ + CODE_61455(61455, "客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account)"), + /** + * 客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded). + */ + CODE_61456(61456, "客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded)"), + /** + * 无效头像文件类型 (invalid file type). + */ + CODE_61457(61457, "无效头像文件类型 (invalid file type)"), + /** + * 系统错误 (system error). + */ + CODE_61450(61450, "系统错误 (system error)"), + /** + * 日期格式错误. + */ + CODE_61500(61500, "日期格式错误"), + /** + * 不存在此 menuid 对应的个性化菜单. + */ + CODE_65301(65301, "不存在此 menuid 对应的个性化菜单"), + /** + * 没有相应的用户. + */ + CODE_65302(65302, "没有相应的用户"), + /** + * 没有默认菜单,不能创建个性化菜单. + */ + CODE_65303(65303, "没有默认菜单,不能创建个性化菜单"), + /** + * MatchRule 信息为空. + */ + CODE_65304(65304, "MatchRule 信息为空"), + /** + * 个性化菜单数量受限. + */ + CODE_65305(65305, "个性化菜单数量受限"), + /** + * 不支持个性化菜单的帐号. + */ + CODE_65306(65306, "不支持个性化菜单的帐号"), + /** + * 个性化菜单信息为空. + */ + CODE_65307(65307, "个性化菜单信息为空"), + /** + * 包含没有响应类型的 button. + */ + CODE_65308(65308, "包含没有响应类型的 button"), + /** + * 个性化菜单开关处于关闭状态. + */ + CODE_65309(65309, "个性化菜单开关处于关闭状态"), + /** + * 填写了省份或城市信息,国家信息不能为空. + */ + CODE_65310(65310, "填写了省份或城市信息,国家信息不能为空"), + /** + * 填写了城市信息,省份信息不能为空. + */ + CODE_65311(65311, "填写了城市信息,省份信息不能为空"), + /** + * 不合法的国家信息. + */ + CODE_65312(65312, "不合法的国家信息"), + /** + * 不合法的省份信息. + */ + CODE_65313(65313, "不合法的省份信息"), + /** + * 不合法的城市信息. + */ + CODE_65314(65314, "不合法的城市信息"), + /** + * 该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接). + */ + CODE_65316(65316, "该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接)"), + /** + * 不合法的 URL. + */ + CODE_65317(65317, "不合法的 URL"), + /** + * POST 数据参数不合法. + */ + CODE_9001001(9001001, "POST 数据参数不合法"), + /** + * 远端服务不可用. + */ + CODE_9001002(9001002, "远端服务不可用"), + /** + * Ticket 不合法. + */ + CODE_9001003(9001003, "Ticket 不合法"), + /** + * 获取摇周边用户信息失败. + */ + CODE_9001004(9001004, "获取摇周边用户信息失败"), + /** + * 获取商户信息失败. + */ + CODE_9001005(9001005, "获取商户信息失败"), + /** + * 获取 OpenID 失败. + */ + CODE_9001006(9001006, "获取 OpenID 失败"), + /** + * 上传文件缺失. + */ + CODE_9001007(9001007, "上传文件缺失"), + /** + * 上传素材的文件类型不合法. + */ + CODE_9001008(9001008, "上传素材的文件类型不合法"), + /** + * 上传素材的文件尺寸不合法. + */ + CODE_9001009(9001009, "上传素材的文件尺寸不合法"), + /** + * 上传失败. + */ + CODE_9001010(9001010, "上传失败"), + /** + * 帐号不合法. + */ + CODE_9001020(9001020, "帐号不合法"), + /** + * 已有设备激活率低于 50% ,不能新增设备. + */ + CODE_9001021(9001021, "已有设备激活率低于 50% ,不能新增设备"), + /** + * 设备申请数不合法,必须为大于 0 的数字. + */ + CODE_9001022(9001022, "设备申请数不合法,必须为大于 0 的数字"), + /** + * 已存在审核中的设备 ID 申请. + */ + CODE_9001023(9001023, "已存在审核中的设备 ID 申请"), + /** + * 一次查询设备 ID 数量不能超过 50. + */ + CODE_9001024(9001024, "一次查询设备 ID 数量不能超过 50"), + /** + * 设备 ID 不合法. + */ + CODE_9001025(9001025, "设备 ID 不合法"), + /** + * 页面 ID 不合法. + */ + CODE_9001026(9001026, "页面 ID 不合法"), + /** + * 页面参数不合法. + */ + CODE_9001027(9001027, "页面参数不合法"), + /** + * 一次删除页面 ID 数量不能超过 10. + */ + CODE_9001028(9001028, "一次删除页面 ID 数量不能超过 10"), + /** + * 页面已应用在设备中,请先解除应用关系再删除. + */ + CODE_9001029(9001029, "页面已应用在设备中,请先解除应用关系再删除"), + /** + * 一次查询页面 ID 数量不能超过 50. + */ + CODE_9001030(9001030, "一次查询页面 ID 数量不能超过 50"), + /** + * 时间区间不合法. + */ + CODE_9001031(9001031, "时间区间不合法"), + /** + * 保存设备与页面的绑定关系参数错误. + */ + CODE_9001032(9001032, "保存设备与页面的绑定关系参数错误"), + /** + * 门店 ID 不合法. + */ + CODE_9001033(9001033, "门店 ID 不合法"), + /** + * 设备备注信息过长. + */ + CODE_9001034(9001034, "设备备注信息过长"), + /** + * 设备申请参数不合法. + */ + CODE_9001035(9001035, "设备申请参数不合法"), + /** + * 查询起始值 begin 不合法. + */ + CODE_9001036(9001036, "查询起始值 begin 不合法"), + + /** + * 设置的 speed 参数不在0到4的范围内 + */ + CODE_45083(45083, "设置的 speed 参数不在0到4的范围内"), + + /** + * 没有设置 speed 参数 + */ + CODE_45084(45084, "没有设置 speed 参数"); + + private int code; + private String msg; + + WxMpErrorMsgEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + /** + * 通过错误代码查找其中文含义.. + */ + public static String findMsgByCode(int code) { + for (WxMpErrorMsgEnum value : WxMpErrorMsgEnum.values()) { + if (value.code == code) { + return value.msg; + } + } + + return null; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/BaseWxRedisOps.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/BaseWxRedisOps.java new file mode 100644 index 0000000000..17e992ab25 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/BaseWxRedisOps.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.common.redis; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * 微信redis操作基本类 + *

+ * 非内置实现redis相关操作, 请实现该类 + */ +public abstract class BaseWxRedisOps implements WxRedisOps { + + @Override + public String getValue(String key) { + throw new UnsupportedOperationException(); + } + + @Override + public void setValue(String key, String value, int expire, TimeUnit timeUnit) { + throw new UnsupportedOperationException(); + } + + @Override + public Long getExpire(String key) { + throw new UnsupportedOperationException(); + } + + @Override + public void expire(String key, int expire, TimeUnit timeUnit) { + throw new UnsupportedOperationException(); + } + + @Override + public Lock getLock(String key) { + throw new UnsupportedOperationException(); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/JedisWxRedisOps.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/JedisWxRedisOps.java new file mode 100644 index 0000000000..2b9849ff92 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/JedisWxRedisOps.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.common.redis; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.util.locks.JedisDistributedLock; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.util.Pool; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +@RequiredArgsConstructor +public class JedisWxRedisOps implements WxRedisOps { + private final Pool jedisPool; + + @Override + public String getValue(String key) { + try (Jedis jedis = this.jedisPool.getResource()) { + return jedis.get(key); + } + } + + @Override + public void setValue(String key, String value, int expire, TimeUnit timeUnit) { + try (Jedis jedis = this.jedisPool.getResource()) { + if (expire <= 0) { + jedis.set(key, value); + } else { + jedis.psetex(key, timeUnit.toMillis(expire), value); + } + } + } + + @Override + public Long getExpire(String key) { + try (Jedis jedis = this.jedisPool.getResource()) { + return jedis.ttl(key); + } + } + + @Override + public void expire(String key, int expire, TimeUnit timeUnit) { + try (Jedis jedis = this.jedisPool.getResource()) { + jedis.pexpire(key, timeUnit.toMillis(expire)); + } + } + + @Override + public Lock getLock(String key) { + return new JedisDistributedLock(jedisPool, key); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOps.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOps.java new file mode 100644 index 0000000000..19d4046c92 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOps.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.common.redis; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.util.locks.RedisTemplateSimpleDistributedLock; +import org.springframework.data.redis.core.StringRedisTemplate; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +@RequiredArgsConstructor +public class RedisTemplateWxRedisOps implements WxRedisOps { + + private final StringRedisTemplate redisTemplate; + + @Override + public String getValue(String key) { + return redisTemplate.opsForValue().get(key); + } + + @Override + public void setValue(String key, String value, int expire, TimeUnit timeUnit) { + if (expire <= 0) { + redisTemplate.opsForValue().set(key, value); + } else { + redisTemplate.opsForValue().set(key, value, expire, timeUnit); + } + } + + @Override + public Long getExpire(String key) { + return redisTemplate.getExpire(key); + } + + @Override + public void expire(String key, int expire, TimeUnit timeUnit) { + redisTemplate.expire(key, expire, timeUnit); + } + + @Override + public Lock getLock(@NonNull String key) { + return new RedisTemplateSimpleDistributedLock(redisTemplate, key, 60 * 1000); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedissonWxRedisOps.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedissonWxRedisOps.java new file mode 100644 index 0000000000..d51cd3e1ad --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedissonWxRedisOps.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.common.redis; + +import lombok.RequiredArgsConstructor; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +@RequiredArgsConstructor +public class RedissonWxRedisOps implements WxRedisOps { + + private final RedissonClient redissonClient; + + @Override + public String getValue(String key) { + Object value = redissonClient.getBucket(key).get(); + return value == null ? null : value.toString(); + } + + @Override + public void setValue(String key, String value, int expire, TimeUnit timeUnit) { + if (expire <= 0) { + redissonClient.getBucket(key).set(value); + } else { + redissonClient.getBucket(key).set(value, expire, timeUnit); + } + } + + @Override + public Long getExpire(String key) { + long expire = redissonClient.getBucket(key).remainTimeToLive(); + if (expire > 0) { + expire = expire / 1000; + } + return expire; + } + + @Override + public void expire(String key, int expire, TimeUnit timeUnit) { + redissonClient.getBucket(key).expire(expire, timeUnit); + } + + @Override + public Lock getLock(String key) { + return redissonClient.getLock(key); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/WxRedisOps.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/WxRedisOps.java new file mode 100644 index 0000000000..5489165e74 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/WxRedisOps.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.common.redis; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * 微信Redis相关操作 + *

+ * 该接口不承诺稳定, 外部实现请继承{@link BaseWxRedisOps} + * + * @see BaseWxRedisOps 实现需要继承该类 + * @see JedisWxRedisOps jedis实现 + * @see RedissonWxRedisOps redisson实现 + * @see RedisTemplateWxRedisOps redisTemplate实现 + */ +public interface WxRedisOps { + + String getValue(String key); + + void setValue(String key, String value, int expire, TimeUnit timeUnit); + + Long getExpire(String key); + + void expire(String key, int expire, TimeUnit timeUnit); + + Lock getLock(String key); +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/requestexecuter/ocr/OcrDiscernApacheHttpRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/requestexecuter/ocr/OcrDiscernApacheHttpRequestExecutor.java new file mode 100644 index 0000000000..2a84ac0e8b --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/requestexecuter/ocr/OcrDiscernApacheHttpRequestExecutor.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.common.requestexecuter.ocr; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; + +/** + * . + * + * @author : zhayueran + * @date 2019/6/27 14:06 + */ +public class OcrDiscernApacheHttpRequestExecutor extends OcrDiscernRequestExecutor { + public OcrDiscernApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public String execute(String uri, File file, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + if (file != null) { + HttpEntity entity = MultipartEntityBuilder + .create() + .addBinaryBody("file", file) + .setMode(HttpMultipartMode.RFC6532) + .build(); + httpPost.setEntity(entity); + } + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return responseContent; + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/requestexecuter/ocr/OcrDiscernRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/requestexecuter/ocr/OcrDiscernRequestExecutor.java new file mode 100644 index 0000000000..38926e72e5 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/requestexecuter/ocr/OcrDiscernRequestExecutor.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.common.requestexecuter.ocr; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; + +import java.io.File; +import java.io.IOException; + +/** + * . + * + * @author zhayueran + * @date 2019/6/27 15:06 + */ +public abstract class OcrDiscernRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public OcrDiscernRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, File data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new OcrDiscernApacheHttpRequestExecutor(requestHttp); + default: + return null; + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxService.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxService.java new file mode 100644 index 0000000000..fc49bfd9ce --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxService.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.common.service; + +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * 微信服务接口. + * + * @author Binary Wang + * @date 2020-04-25 + */ +public interface WxService { + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. + * + * @param queryParam 参数 + * @param url 请求接口地址 + * @return 接口响应字符串 + * @throws WxErrorException 异常 + */ + String get(String url, String queryParam) throws WxErrorException; + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. + * + * @param postData 请求参数json值 + * @param url 请求接口地址 + * @return 接口响应字符串 + * @throws WxErrorException 异常 + */ + String post(String url, String postData) throws WxErrorException; + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. + * + * @param url 请求接口地址 + * @param obj 请求对象 + * @return 接口响应字符串 + * @throws WxErrorException 异常 + */ + String post(String url, Object obj) throws WxErrorException; +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/Constants.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/Constants.java index f30a0ae0d0..98d45e31d4 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/Constants.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/Constants.java @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,9 +23,6 @@ * * @author Craig R. McClanahan */ - public class Constants { - - public static final String Package = "me.chanjar.weixin.common.session"; - + public static final String PACKAGE = "me.chanjar.weixin.common.session"; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSession.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSession.java index 77d4d28291..05cb41363f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSession.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSession.java @@ -1,5 +1,9 @@ package me.chanjar.weixin.common.session; +/** + * + * @author Daniel Qian + */ public interface InternalSession { /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java index a92e107154..e3d9ab8351 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java @@ -1,5 +1,8 @@ package me.chanjar.weixin.common.session; +/** + * @author Daniel Qian + */ public interface InternalSessionManager { /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java index f9d61707e3..3c4ec20c8d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java @@ -1,23 +1,29 @@ package me.chanjar.weixin.common.session; -import me.chanjar.weixin.common.util.res.StringManager; - -import java.util.*; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import me.chanjar.weixin.common.util.res.StringManager; + +/** + * @author Daniel Qian + */ public class StandardSession implements WxSession, InternalSession { /** * The string manager for this package. */ - protected static final StringManager sm = StringManager.getManager(Constants.Package); + protected static final StringManager SM = StringManager.getManager(Constants.PACKAGE); /** * Type array. */ private static final String[] EMPTY_ARRAY = new String[0]; - // ------------------------------ WxSession protected Map attributes = new ConcurrentHashMap<>(); /** * The session identifier of this Session. @@ -73,7 +79,7 @@ public Object getAttribute(String name) { if (!isValidInternal()) { throw new IllegalStateException - (sm.getString("sessionImpl.getAttribute.ise")); + (SM.getString("sessionImpl.getAttribute.ise")); } if (name == null) { @@ -86,7 +92,7 @@ public Object getAttribute(String name) { @Override public Enumeration getAttributeNames() { if (!isValidInternal()) { - throw new IllegalStateException(sm.getString("sessionImpl.getAttributeNames.ise")); + throw new IllegalStateException(SM.getString("sessionImpl.getAttributeNames.ise")); } Set names = new HashSet<>(); @@ -98,7 +104,7 @@ public Enumeration getAttributeNames() { public void setAttribute(String name, Object value) { // Name cannot be null if (name == null) { - throw new IllegalArgumentException(sm.getString("sessionImpl.setAttribute.namenull")); + throw new IllegalArgumentException(SM.getString("sessionImpl.setAttribute.namenull")); } // Null value is the same as removeAttribute() @@ -109,7 +115,7 @@ public void setAttribute(String name, Object value) { // Validate our current state if (!isValidInternal()) { - throw new IllegalStateException(sm.getString("sessionImpl.setAttribute.ise", getIdInternal())); + throw new IllegalStateException(SM.getString("sessionImpl.setAttribute.ise", getIdInternal())); } this.attributes.put(name, value); @@ -123,8 +129,9 @@ public void removeAttribute(String name) { @Override public void invalidate() { - if (!isValidInternal()) - throw new IllegalStateException(sm.getString("sessionImpl.invalidate.ise")); + if (!isValidInternal()) { + throw new IllegalStateException(SM.getString("sessionImpl.invalidate.ise")); + } // Cause this session to expire expire(); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionFacade.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionFacade.java index e449308961..aa9f877136 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionFacade.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionFacade.java @@ -2,6 +2,9 @@ import java.util.Enumeration; +/** + * @author Daniel Qian + */ public class StandardSessionFacade implements WxSession { /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java index 7e6c95a28f..591b7025dd 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java @@ -9,12 +9,12 @@ import java.util.concurrent.atomic.AtomicBoolean; /** - * 基于内存的session manager + * 基于内存的session manager. + * + * @author Daniel Qian */ public class StandardSessionManager implements WxSessionManager, InternalSessionManager { - - protected static final StringManager sm = - StringManager.getManager(Constants.Package); + protected static final StringManager SM = StringManager.getManager(Constants.PACKAGE); /** * The descriptive name of this Manager implementation (for logging). */ @@ -49,7 +49,9 @@ public class StandardSessionManager implements WxSessionManager, InternalSession */ protected int maxInactiveInterval = 30 * 60; - // Number of sessions created by this manager + /** + * Number of sessions created by this manager + */ protected long sessionCounter = 0; protected volatile int maxActive = 0; @@ -82,7 +84,7 @@ public WxSession getSession(String sessionId) { public WxSession getSession(String sessionId, boolean create) { if (sessionId == null) { throw new IllegalStateException - (sm.getString("sessionManagerImpl.getSession.ise")); + (SM.getString("sessionManagerImpl.getSession.ise")); } InternalSession session = findSession(sessionId); @@ -124,26 +126,25 @@ public void remove(InternalSession session, boolean update) { @Override public InternalSession findSession(String id) { - - if (id == null) + if (id == null) { return (null); + } return this.sessions.get(id); - } @Override public InternalSession createSession(String sessionId) { if (sessionId == null) { throw new IllegalStateException - (sm.getString("sessionManagerImpl.createSession.ise")); + (SM.getString("sessionManagerImpl.createSession.ise")); } if ((this.maxActiveSessions >= 0) && - (getActiveSessions() >= this.maxActiveSessions)) { + (getActiveSessions() >= this.maxActiveSessions)) { this.rejectedSessions++; throw new TooManyActiveSessionsException( - sm.getString("sessionManagerImpl.createSession.tmase"), - this.maxActiveSessions); + SM.getString("sessionManagerImpl.createSession.tmase"), + this.maxActiveSessions); } // Recycle or create a Session instance @@ -153,12 +154,10 @@ public InternalSession createSession(String sessionId) { session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); - String id = sessionId; - session.setId(id); + session.setId(sessionId); this.sessionCounter++; - return (session); - + return session; } @@ -180,10 +179,8 @@ protected InternalSession getNewSession() { return new StandardSession(this); } - @Override public void add(InternalSession session) { - // 当第一次有session创建的时候,开启session清理线程 if (!this.backgroundProcessStarted.getAndSet(true)) { Thread t = new Thread(new Runnable() { @@ -192,9 +189,10 @@ public void run() { while (true) { try { // 每秒清理一次 - Thread.sleep(StandardSessionManager.this.backgroundProcessorDelay * 1000l); + Thread.sleep(StandardSessionManager.this.backgroundProcessorDelay * 1000L); backgroundProcess(); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); StandardSessionManager.this.log.error("SessionManagerImpl.backgroundProcess error", e); } } @@ -230,8 +228,9 @@ public InternalSession[] findSessions() { @Override public void backgroundProcess() { this.count = (this.count + 1) % this.processExpiresFrequency; - if (this.count == 0) + if (this.count == 0) { processExpires(); + } } /** @@ -243,16 +242,18 @@ public void processExpires() { InternalSession sessions[] = findSessions(); int expireHere = 0; - if (this.log.isDebugEnabled()) + if (this.log.isDebugEnabled()) { this.log.debug("Start expire sessions {} at {} sessioncount {}", getName(), timeNow, sessions.length); - for (int i = 0; i < sessions.length; i++) { - if (sessions[i] != null && !sessions[i].isValid()) { + } + for (InternalSession session : sessions) { + if (session != null && !session.isValid()) { expireHere++; } } long timeEnd = System.currentTimeMillis(); - if (this.log.isDebugEnabled()) + if (this.log.isDebugEnabled()) { this.log.debug("End expire sessions {} processingTime {} expired sessions: {}", getName(), timeEnd - timeNow, expireHere); + } this.processingTime += (timeEnd - timeNow); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/TooManyActiveSessionsException.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/TooManyActiveSessionsException.java index 72a2b4c416..114dd1c4ed 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/TooManyActiveSessionsException.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/TooManyActiveSessionsException.java @@ -19,9 +19,11 @@ /** * An exception that indicates the maximum number of active sessions has been * reached and the server is refusing to create any new sessions. + * + * @author Daniel Qian */ public class TooManyActiveSessionsException - extends IllegalStateException { + extends IllegalStateException { private static final long serialVersionUID = 1L; /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSession.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSession.java index 25bed2d274..3aa79f9ad2 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSession.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSession.java @@ -2,6 +2,9 @@ import java.util.Enumeration; +/** + * @author Daniel Qian + */ public interface WxSession { Object getAttribute(String name); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSessionManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSessionManager.java index c966ddab28..789e272875 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSessionManager.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/WxSessionManager.java @@ -1,5 +1,8 @@ package me.chanjar.weixin.common.session; +/** + * @author Daniel Qian + */ public interface WxSessionManager { /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/BeanUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/BeanUtils.java index 630821e954..768f2e5324 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/BeanUtils.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/BeanUtils.java @@ -1,11 +1,9 @@ package me.chanjar.weixin.common.util; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.annotation.Required; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,14 +12,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; /** *

  * bean操作的一些工具类
  * Created by Binary Wang on 2016-10-21.
- * @author binarywang(Binary Wang)
  * 
+ * + * @author binarywang(Binary Wang) */ public class BeanUtils { private static Logger log = LoggerFactory.getLogger(BeanUtils.class); @@ -30,7 +28,6 @@ public class BeanUtils { * 检查bean里标记为@Required的field是否为空,为空则抛异常 * * @param bean 要检查的bean对象 - * @throws WxErrorException */ public static void checkRequiredFields(Object bean) throws WxErrorException { List requiredFields = Lists.newArrayList(); @@ -42,55 +39,27 @@ public static void checkRequiredFields(Object bean) throws WxErrorException { boolean isAccessible = field.isAccessible(); field.setAccessible(true); if (field.isAnnotationPresent(Required.class)) { - if (field.get(bean) == null || (field.get(bean) instanceof String && StringUtils.isBlank(field.get(bean).toString()))) { - //两种情况,一种是值为null,另外一种情况是类型为字符串,但是字符串内容为空的,都认为是没有提供值 + // 两种情况,一种是值为null, + // 另外一种情况是类型为字符串,但是字符串内容为空的,都认为是没有提供值 + boolean isRequiredMissing = field.get(bean) == null + || (field.get(bean) instanceof String + && StringUtils.isBlank(field.get(bean).toString()) + ); + if (isRequiredMissing) { requiredFields.add(field.getName()); } } field.setAccessible(isAccessible); - } catch (SecurityException | IllegalArgumentException - | IllegalAccessException e) { - e.printStackTrace(); + } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) { + log.error(e.getMessage(), e); } } if (!requiredFields.isEmpty()) { - String msg = "必填字段 " + requiredFields + " 必须提供值"; + String msg = String.format("必填字段【%s】必须提供值!", requiredFields); log.debug(msg); - throw new WxErrorException(WxError.newBuilder().setErrorMsg(msg).build()); + throw new WxErrorException(WxError.builder().errorMsg(msg).build()); } } - /** - * 将bean按照@XStreamAlias标识的字符串内容生成以之为key的map对象 - * - * @param bean 包含@XStreamAlias的xml bean对象 - * @return map对象 - */ - public static Map xmlBean2Map(Object bean) { - Map result = Maps.newHashMap(); - List fields = new ArrayList<>(Arrays.asList(bean.getClass().getDeclaredFields())); - fields.addAll(Arrays.asList(bean.getClass().getSuperclass().getDeclaredFields())); - for (Field field : fields) { - try { - boolean isAccessible = field.isAccessible(); - field.setAccessible(true); - if (field.get(bean) == null) { - field.setAccessible(isAccessible); - continue; - } - - if (field.isAnnotationPresent(XStreamAlias.class)) { - result.put(field.getAnnotation(XStreamAlias.class).value(), field.get(bean).toString()); - } - - field.setAccessible(isAccessible); - } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - - } - - return result; - } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java new file mode 100644 index 0000000000..983d9a668f --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/DataUtils.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.common.util; + +import org.apache.commons.lang3.StringUtils; + +/** + *
+ *  数据处理工具类
+ *  Created by BinaryWang on 2018/5/8.
+ * 
+ * + * @author Binary Wang + */ +public class DataUtils { + /** + * 将数据中包含的secret字符使用星号替换,防止日志打印时被输出 + */ + public static E handleDataWithSecret(E data) { + E dataForLog = data; + if(data instanceof String && StringUtils.contains((String)data, "&secret=")){ + dataForLog = (E) StringUtils.replaceAll((String)data,"&secret=\\w+&","&secret=******&"); + } + return dataForLog; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/LogExceptionHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/LogExceptionHandler.java index 7ad976abd8..7487a0fe29 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/LogExceptionHandler.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/LogExceptionHandler.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.common.util; import me.chanjar.weixin.common.api.WxErrorExceptionHandler; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/SignUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/SignUtils.java new file mode 100644 index 0000000000..fc3579d45c --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/SignUtils.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.common.util; + +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Hex; + +import lombok.extern.slf4j.Slf4j; + +/** + *
+ *  签名工具类
+ *  Created by BinaryWang on 2018/7/11.
+ * 
+ * + * @author Binary Wang + */ +@Slf4j +public class SignUtils { + /** + * HmacSHA256 签名算法 + * + * @param message 签名数据 + * @param key 签名密钥 + */ + public static String createHmacSha256Sign(String message, String key) { + try { + Mac sha256 = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + sha256.init(secretKeySpec); + byte[] bytes = sha256.doFinal(message.getBytes(StandardCharsets.UTF_8)); + return Hex.encodeHexString(bytes).toUpperCase(); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + SignUtils.log.error(e.getMessage(), e); + } + + return null; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/ToStringUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/ToStringUtils.java deleted file mode 100644 index 0fa38391c0..0000000000 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/ToStringUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -package me.chanjar.weixin.common.util; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - *
- * 自定义的ToString方法,用于产生去掉空值属性的字符串
- * Created by Binary Wang on 2016-10-27.
- * @author binarywang(Binary Wang)
- * 
- */ -public class ToStringUtils { - public static final ToStringStyle THE_STYLE = new SimpleMultiLineToStringStyle(); - private static class SimpleMultiLineToStringStyle extends ToStringStyle { - private static final long serialVersionUID = 4645306494220335355L; - private static final String LINE_SEPARATOR = "\n"; - private static final String NULL_TEXT = ""; - - public SimpleMultiLineToStringStyle() { - super(); - this.setContentStart("["); - this.setFieldSeparator(LINE_SEPARATOR + " "); - this.setFieldSeparatorAtStart(true); - this.setContentEnd(LINE_SEPARATOR + "]"); - this.setNullText(NULL_TEXT); - this.setUseShortClassName(true); - this.setUseIdentityHashCode(false); - } - } - - /** - * 用于产生去掉空值属性并以换行符分割各属性键值的toString字符串 - * @param obj - */ - public static String toSimpleString(Object obj) { - String toStringResult = ToStringBuilder.reflectionToString(obj, THE_STYLE); - String[] split = toStringResult.split(SimpleMultiLineToStringStyle.LINE_SEPARATOR); - StringBuilder result = new StringBuilder(); - for (String string : split) { - if (string.endsWith(SimpleMultiLineToStringStyle.NULL_TEXT)) { - continue; - } - - result.append(string + SimpleMultiLineToStringStyle.LINE_SEPARATOR); - } - - if (result.length() == 0) { - return ""; - } - - //如果没有非空的属性,就输出 - if (StringUtils.countMatches(result, SimpleMultiLineToStringStyle.LINE_SEPARATOR) == 2) { - return result.toString().split(SimpleMultiLineToStringStyle.LINE_SEPARATOR)[0] - + "]"; - } - - return result.deleteCharAt(result.length() - 1).toString(); - } -} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/XmlUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/XmlUtils.java new file mode 100644 index 0000000000..c2ffdb001b --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/XmlUtils.java @@ -0,0 +1,101 @@ +package me.chanjar.weixin.common.util; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.Node; +import org.dom4j.io.SAXReader; +import org.dom4j.tree.DefaultText; +import org.xml.sax.SAXException; + +import java.io.StringReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *
+ * XML转换工具类.
+ * Created by Binary Wang on 2018/11/4.
+ * 
+ * + * @author Binary Wang + */ +public class XmlUtils { + + public static Map xml2Map(String xmlString) { + Map map = new HashMap<>(16); + try { + SAXReader saxReader = new SAXReader(); + saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + saxReader.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true); + saxReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + saxReader.setFeature("http://xml.org/sax/features/external-general-entities", false); + saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + Document doc = saxReader.read(new StringReader(xmlString)); + Element root = doc.getRootElement(); + List elements = root.elements(); + for (Element element : elements) { + map.put(element.getName(), element2MapOrString(element)); + } + } catch (DocumentException | SAXException e) { + throw new RuntimeException(e); + } + + return map; + } + + private static Object element2MapOrString(Element element) { + Map result = Maps.newHashMap(); + + final List content = element.content(); + if (content.size() <= 1) { + return element.getText(); + } + + final Set names = names(content); + if (names.size() == 1) { + // 说明是个列表,各个子对象是相同的name + List list = Lists.newArrayList(); + for (Node node : content) { + if (node instanceof DefaultText) { + continue; + } + + if (node instanceof Element) { + list.add(element2MapOrString((Element) node)); + } + } + + result.put(names.iterator().next(), list); + } else { + for (Node node : content) { + if (node instanceof DefaultText) { + continue; + } + + if (node instanceof Element) { + result.put(node.getName(), element2MapOrString((Element) node)); + } + } + } + + return result; + } + + private static Set names(List nodes) { + Set names = Sets.newHashSet(); + for (Node node : nodes) { + if (node instanceof DefaultText) { + continue; + } + names.add(node.getName()); + } + + return names; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/ByteGroup.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/ByteGroup.java index b12ec65555..bff08d680d 100755 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/ByteGroup.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/ByteGroup.java @@ -1,26 +1,26 @@ -package me.chanjar.weixin.common.util.crypto; - -import java.util.ArrayList; - -public class ByteGroup { - ArrayList byteContainer = new ArrayList<>(); - - public byte[] toBytes() { - byte[] bytes = new byte[this.byteContainer.size()]; - for (int i = 0; i < this.byteContainer.size(); i++) { - bytes[i] = this.byteContainer.get(i); - } - return bytes; - } - - public ByteGroup addBytes(byte[] bytes) { - for (byte b : bytes) { - this.byteContainer.add(b); - } - return this; - } - - public int size() { - return this.byteContainer.size(); - } -} +package me.chanjar.weixin.common.util.crypto; + +import java.util.ArrayList; + +public class ByteGroup { + ArrayList byteContainer = new ArrayList<>(); + + public byte[] toBytes() { + byte[] bytes = new byte[this.byteContainer.size()]; + for (int i = 0; i < this.byteContainer.size(); i++) { + bytes[i] = this.byteContainer.get(i); + } + return bytes; + } + + public ByteGroup addBytes(byte[] bytes) { + for (byte b : bytes) { + this.byteContainer.add(b); + } + return this; + } + + public int size() { + return this.byteContainer.size(); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/PKCS7Encoder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/PKCS7Encoder.java index 145896a577..efe7867baf 100755 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/PKCS7Encoder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/PKCS7Encoder.java @@ -1,68 +1,65 @@ -/** - * 对公众平台发送给公众账号的消息加解密示例代码. - * - * @copyright Copyright (c) 1998-2014 Tencent Inc. - */ - -// ------------------------------------------------------------------------ - -package me.chanjar.weixin.common.util.crypto; - -import java.nio.charset.Charset; -import java.util.Arrays; - -/** - * 提供基于PKCS7算法的加解 - */ -public class PKCS7Encoder { - - private static final Charset CHARSET = Charset.forName("utf-8"); - private static final int BLOCK_SIZE = 32; - - /** - * 获得对明文进行补位填充的字节. - * - * @param count 需要进行填充补位操作的明文字节个数 - * @return 补齐用的字节数组 - */ - public static byte[] encode(int count) { - // 计算需要填充的位数 - int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); - if (amountToPad == 0) { - amountToPad = BLOCK_SIZE; - } - // 获得补位所用的字符 - char padChr = chr(amountToPad); - String tmp = new String(); - for (int index = 0; index < amountToPad; index++) { - tmp += padChr; - } - return tmp.getBytes(CHARSET); - } - - /** - * 删除解密后明文的补位字符 - * - * @param decrypted 解密后的明文 - * @return 删除补位字符后的明文 - */ - public static byte[] decode(byte[] decrypted) { - int pad = decrypted[decrypted.length - 1]; - if (pad < 1 || pad > 32) { - pad = 0; - } - return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); - } - - /** - * 将数字转化成ASCII码对应的字符,用于对明文进行补码 - * - * @param a 需要转化的数字 - * @return 转化得到的字符 - */ - public static char chr(int a) { - byte target = (byte) (a & 0xFF); - return (char) target; - } - -} +/* + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +package me.chanjar.weixin.common.util.crypto; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * 提供基于PKCS7算法的加解. + * + * @author tencent + */ +public class PKCS7Encoder { + private static final Charset CHARSET = StandardCharsets.UTF_8; + private static final int BLOCK_SIZE = 32; + + /** + * 获得对明文进行补位填充的字节. + * + * @param count 需要进行填充补位操作的明文字节个数 + * @return 补齐用的字节数组 + */ + public static byte[] encode(int count) { + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + // 获得补位所用的字符 + char padChr = chr(amountToPad); + StringBuilder tmp = new StringBuilder(); + for (int index = 0; index < amountToPad; index++) { + tmp.append(padChr); + } + return tmp.toString().getBytes(CHARSET); + } + + /** + * 删除解密后明文的补位字符. + * + * @param decrypted 解密后的明文 + * @return 删除补位字符后的明文 + */ + public static byte[] decode(byte[] decrypted) { + int pad = decrypted[decrypted.length - 1]; + if (pad < 1 || pad > 32) { + pad = 0; + } + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + + /** + * 将数字转化成ASCII码对应的字符,用于对明文进行补码. + * + * @param a 需要转化的数字 + * @return 转化得到的字符 + */ + private static char chr(int a) { + byte target = (byte) (a & 0xFF); + return (char) target; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java index 683f2ecfc3..c82f94d871 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java @@ -1,18 +1,25 @@ package me.chanjar.weixin.common.util.crypto; -import java.util.Arrays; - import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; /** - * Created by Daniel Qian on 14/10/19. + * + * @author Daniel Qian + * @date 14/10/19 */ public class SHA1 { /** - * 串接arr参数,生成sha1 digest + * 串接arr参数,生成sha1 digest. */ public static String gen(String... arr) { + if (StringUtils.isAnyEmpty(arr)) { + throw new IllegalArgumentException("非法请求参数,有部分参数为空 : " + Arrays.toString(arr)); + } + Arrays.sort(arr); StringBuilder sb = new StringBuilder(); for (String a : arr) { @@ -22,9 +29,13 @@ public static String gen(String... arr) { } /** - * 用&串接arr参数,生成sha1 digest + * 用&串接arr参数,生成sha1 digest. */ public static String genWithAmple(String... arr) { + if (StringUtils.isAnyEmpty(arr)) { + throw new IllegalArgumentException("非法请求参数,有部分参数为空 : " + Arrays.toString(arr)); + } + Arrays.sort(arr); StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; i++) { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java index a809fe3b1b..0103fc7f06 100755 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java @@ -1,27 +1,10 @@ -/** - * 对公众平台发送给公众账号的消息加解密示例代码. - * - * @copyright Copyright (c) 1998-2014 Tencent Inc. - *

- * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - */ - -// ------------------------------------------------------------------------ - -/** - * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - */ package me.chanjar.weixin.common.util.crypto; import java.io.StringReader; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Random; - import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; @@ -29,21 +12,37 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; import org.apache.commons.codec.binary.Base64; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource; +/** + *

+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ * Copyright (c) 1998-2014 Tencent Inc.
+ * 针对org.apache.commons.codec.binary.Base64,
+ * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
+ * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
+ * 
+ * + * @author Tencent + */ public class WxCryptUtil { - private static final Base64 base64 = new Base64(); - private static final Charset CHARSET = Charset.forName("utf-8"); + private static final Base64 BASE64 = new Base64(); + private static final Charset CHARSET = StandardCharsets.UTF_8; - private static final ThreadLocal builderLocal = new ThreadLocal() { + private static final ThreadLocal BUILDER_LOCAL = new ThreadLocal() { @Override protected DocumentBuilder initialValue() { try { - return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setExpandEntityReferences(false); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + return factory.newDocumentBuilder(); } catch (ParserConfigurationException exc) { throw new IllegalArgumentException(exc); } @@ -55,26 +54,24 @@ protected DocumentBuilder initialValue() { protected String appidOrCorpid; public WxCryptUtil() { - super(); } /** - * 构造函数 + * 构造函数. * - * @param token 公众平台上,开发者设置的token - * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey - * @param appidOrCorpid 公众平台appid/corpid + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appidOrCorpid 公众平台appid/corpid */ - public WxCryptUtil(String token, String encodingAesKey, - String appidOrCorpid) { + public WxCryptUtil(String token, String encodingAesKey, String appidOrCorpid) { this.token = token; this.appidOrCorpid = appidOrCorpid; - this.aesKey = Base64.decodeBase64(encodingAesKey + "="); + this.aesKey = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey)); } - static String extractEncryptPart(String xml) { + private static String extractEncryptPart(String xml) { try { - DocumentBuilder db = builderLocal.get(); + DocumentBuilder db = BUILDER_LOCAL.get(); Document document = db.parse(new InputSource(new StringReader(xml))); Element root = document.getDocumentElement(); @@ -84,6 +81,61 @@ static String extractEncryptPart(String xml) { } } + /** + * 将一个数字转换成生成4个字节的网络字节序bytes数组. + */ + private static byte[] number2BytesInNetworkOrder(int number) { + byte[] orderBytes = new byte[4]; + orderBytes[3] = (byte) (number & 0xFF); + orderBytes[2] = (byte) (number >> 8 & 0xFF); + orderBytes[1] = (byte) (number >> 16 & 0xFF); + orderBytes[0] = (byte) (number >> 24 & 0xFF); + return orderBytes; + } + + /** + * 4个字节的网络字节序bytes数组还原成一个数字. + */ + private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { + int sourceNumber = 0; + for (int i = 0; i < 4; i++) { + sourceNumber <<= 8; + sourceNumber |= bytesInNetworkOrder[i] & 0xff; + } + return sourceNumber; + } + + /** + * 随机生成16位字符串. + */ + private static String genRandomStr() { + String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 16; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } + + /** + * 生成xml消息. + * + * @param encrypt 加密后的消息密文 + * @param signature 安全签名 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @return 生成的xml字符串 + */ + private static String generateXml(String encrypt, String signature, String timestamp, String nonce) { + String format = "\n" + "\n" + + "\n" + + "%3$s\n" + "\n" + + ""; + return String.format(format, encrypt, signature, timestamp, nonce); + } + /** * 将公众平台回复用户的消息加密打包. *
    @@ -100,12 +152,11 @@ public String encrypt(String plainText) { String encryptedXml = encrypt(genRandomStr(), plainText); // 生成安全签名 - String timeStamp = Long.toString(System.currentTimeMillis() / 1000l); + String timeStamp = Long.toString(System.currentTimeMillis() / 1000L); String nonce = genRandomStr(); String signature = SHA1.gen(this.token, timeStamp, nonce, encryptedXml); - String result = generateXml(encryptedXml, signature, timeStamp, nonce); - return result; + return generateXml(encryptedXml, signature, timeStamp, nonce); } /** @@ -118,8 +169,7 @@ protected String encrypt(String randomStr, String plainText) { ByteGroup byteCollector = new ByteGroup(); byte[] randomStringBytes = randomStr.getBytes(CHARSET); byte[] plainTextBytes = plainText.getBytes(CHARSET); - byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( - plainTextBytes.length); + byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder(plainTextBytes.length); byte[] appIdBytes = this.appidOrCorpid.getBytes(CHARSET); // randomStr + networkBytesOrder + text + appid @@ -146,9 +196,7 @@ protected String encrypt(String randomStr, String plainText) { byte[] encrypted = cipher.doFinal(unencrypted); // 使用BASE64对加密后的字符串进行编码 - String base64Encrypted = base64.encodeToString(encrypted); - - return base64Encrypted; + return BASE64.encodeToString(encrypted); } catch (Exception e) { throw new RuntimeException(e); } @@ -168,8 +216,7 @@ protected String encrypt(String randomStr, String plainText) { * @param encryptedXml 密文,对应POST请求的数据 * @return 解密后的原文 */ - public String decrypt(String msgSignature, String timeStamp, String nonce, - String encryptedXml) { + public String decrypt(String msgSignature, String timeStamp, String nonce, String encryptedXml) { // 密钥,公众账号的app corpSecret // 提取密文 String cipherText = extractEncryptPart(encryptedXml); @@ -181,8 +228,7 @@ public String decrypt(String msgSignature, String timeStamp, String nonce, } // 解密 - String result = decrypt(cipherText); - return result; + return decrypt(cipherText); } /** @@ -196,10 +242,9 @@ public String decrypt(String cipherText) { try { // 设置解密模式为AES的CBC模式 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); - SecretKeySpec key_spec = new SecretKeySpec(this.aesKey, "AES"); - IvParameterSpec iv = new IvParameterSpec( - Arrays.copyOfRange(this.aesKey, 0, 16)); - cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); + SecretKeySpec keySpec = new SecretKeySpec(this.aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(this.aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); // 使用BASE64对密文进行解码 byte[] encrypted = Base64.decodeBase64(cipherText); @@ -210,7 +255,8 @@ public String decrypt(String cipherText) { throw new RuntimeException(e); } - String xmlContent, from_appid; + String xmlContent; + String fromAppid; try { // 去除补位字符 byte[] bytes = PKCS7Encoder.decode(original); @@ -220,81 +266,19 @@ public String decrypt(String cipherText) { int xmlLength = bytesNetworkOrder2Number(networkOrder); - xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), - CHARSET); - from_appid = new String( - Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); + xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); + fromAppid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); } catch (Exception e) { throw new RuntimeException(e); } - // appid不相同的情况 - if (!from_appid.equals(this.appidOrCorpid)) { - throw new RuntimeException("AppID不正确"); - } + // appid不相同的情况 暂时忽略这段判断 +// if (!fromAppid.equals(this.appidOrCorpid)) { +// throw new RuntimeException("AppID不正确,请核实!"); +// } return xmlContent; } - /** - * 将一个数字转换成生成4个字节的网络字节序bytes数组 - * - * @param number - */ - private static byte[] number2BytesInNetworkOrder(int number) { - byte[] orderBytes = new byte[4]; - orderBytes[3] = (byte) (number & 0xFF); - orderBytes[2] = (byte) (number >> 8 & 0xFF); - orderBytes[1] = (byte) (number >> 16 & 0xFF); - orderBytes[0] = (byte) (number >> 24 & 0xFF); - return orderBytes; - } - - /** - * 4个字节的网络字节序bytes数组还原成一个数字 - * - * @param bytesInNetworkOrder - */ - private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { - int sourceNumber = 0; - for (int i = 0; i < 4; i++) { - sourceNumber <<= 8; - sourceNumber |= bytesInNetworkOrder[i] & 0xff; - } - return sourceNumber; - } - - /** - * 随机生成16位字符串 - */ - private static String genRandomStr() { - String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - Random random = new Random(); - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < 16; i++) { - int number = random.nextInt(base.length()); - sb.append(base.charAt(number)); - } - return sb.toString(); - } - - /** - * 生成xml消息 - * - * @param encrypt 加密后的消息密文 - * @param signature 安全签名 - * @param timestamp 时间戳 - * @param nonce 随机字符串 - * @return 生成的xml字符串 - */ - private static String generateXml(String encrypt, String signature, - String timestamp, String nonce) { - String format = "\n" + "\n" - + "\n" - + "%3$s\n" + "\n" - + ""; - return String.format(format, encrypt, signature, timestamp, nonce); - } - } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java index 9ff25ebc10..a00c9cbade 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java @@ -1,52 +1,66 @@ package me.chanjar.weixin.common.util.fs; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.util.Base64; public class FileUtils { - /** - * 创建临时文件 + * 创建临时文件. * - * @param inputStream + * @param inputStream 输入文件流 * @param name 文件名 * @param ext 扩展名 * @param tmpDirFile 临时文件夹目录 */ public static File createTmpFile(InputStream inputStream, String name, String ext, File tmpDirFile) throws IOException { - File tmpFile; - if (tmpDirFile == null) { - tmpFile = File.createTempFile(name, '.' + ext); - } else { - tmpFile = File.createTempFile(name, '.' + ext, tmpDirFile); - } - - tmpFile.deleteOnExit(); - - try (FileOutputStream fos = new FileOutputStream(tmpFile)) { - int read = 0; - byte[] bytes = new byte[1024 * 100]; - while ((read = inputStream.read(bytes)) != -1) { - fos.write(bytes, 0, read); - } + File resultFile = File.createTempFile(name, '.' + ext, tmpDirFile); - fos.flush(); - return tmpFile; - } + resultFile.deleteOnExit(); + org.apache.commons.io.FileUtils.copyToFile(inputStream, resultFile); + return resultFile; } /** - * 创建临时文件 + * 创建临时文件. * - * @param inputStream + * @param inputStream 输入文件流 * @param name 文件名 * @param ext 扩展名 */ public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { - return createTmpFile(inputStream, name, ext, null); + return createTmpFile(inputStream, name, ext, Files.createTempDirectory("weixin-java-tools-temp").toFile()); + } + + /** + * 文件流生成base64 + * + * @param in 文件流 + * @return base64编码 + */ + public static String imageToBase64ByStream(InputStream in) { + byte[] data = null; + // 读取图片字节数组 + try { + data = new byte[in.available()]; + in.read(data); + // 返回Base64编码过的字节数组字符串 + return Base64.getEncoder().encodeToString(data); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/BaseMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/BaseMediaDownloadRequestExecutor.java new file mode 100644 index 0000000000..ed5ec17bc9 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/BaseMediaDownloadRequestExecutor.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.common.util.http; + +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.apache.ApacheMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaDownloadRequestExecutor; + +/** + * 下载媒体文件请求执行器. + * 请求的参数是String, 返回的结果是File + * 视频文件不支持下载 + * + * @author Daniel Qian + */ +public abstract class BaseMediaDownloadRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + protected File tmpDirFile; + + public BaseMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + this.requestHttp = requestHttp; + this.tmpDirFile = tmpDirFile; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp, File tmpDirFile) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new ApacheMediaDownloadRequestExecutor(requestHttp, tmpDirFile); + case JODD_HTTP: + return new JoddHttpMediaDownloadRequestExecutor(requestHttp, tmpDirFile); + case OK_HTTP: + return new OkHttpMediaDownloadRequestExecutor(requestHttp, tmpDirFile); + default: + return null; + } + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java new file mode 100644 index 0000000000..0d68518849 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java @@ -0,0 +1,90 @@ +package me.chanjar.weixin.common.util.http; + +import jodd.http.HttpResponse; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import okhttp3.Response; +import org.apache.http.Header; +import org.apache.http.client.methods.CloseableHttpResponse; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *
    + * 三种http框架的response代理类,方便提取公共方法
    + * Created by Binary Wang on 2017-8-3.
    + * 
    + * + * @author Binary Wang + */ +public class HttpResponseProxy { + private static final Pattern PATTERN = Pattern.compile(".*filename=\"(.*)\""); + + private CloseableHttpResponse apacheHttpResponse; + private HttpResponse joddHttpResponse; + private Response okHttpResponse; + + public HttpResponseProxy(CloseableHttpResponse apacheHttpResponse) { + this.apacheHttpResponse = apacheHttpResponse; + } + + public HttpResponseProxy(HttpResponse joddHttpResponse) { + this.joddHttpResponse = joddHttpResponse; + } + + public HttpResponseProxy(Response okHttpResponse) { + this.okHttpResponse = okHttpResponse; + } + + public String getFileName() throws WxErrorException { + //由于对象只能由一个构造方法实现,因此三个response对象必定且只有一个不为空 + if (this.apacheHttpResponse != null) { + return this.getFileName(this.apacheHttpResponse); + } + + if (this.joddHttpResponse != null) { + return this.getFileName(this.joddHttpResponse); + } + + if (this.okHttpResponse != null) { + return this.getFileName(this.okHttpResponse); + } + + //cannot happen + return null; + } + + private String getFileName(CloseableHttpResponse response) throws WxErrorException { + Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); + if (contentDispositionHeader == null || contentDispositionHeader.length == 0) { + throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); + } + + return this.extractFileNameFromContentString(contentDispositionHeader[0].getValue()); + } + + private String getFileName(HttpResponse response) throws WxErrorException { + String content = response.header("Content-disposition"); + return this.extractFileNameFromContentString(content); + } + + private String getFileName(Response response) throws WxErrorException { + String content = response.header("Content-disposition"); + return this.extractFileNameFromContentString(content); + } + + private String extractFileNameFromContentString(String content) throws WxErrorException { + if (content == null || content.length() == 0) { + throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); + } + + Matcher m = PATTERN.matcher(content); + if (m.matches()) { + return m.group(1); + } + + throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java new file mode 100644 index 0000000000..eff5907f7a --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.common.util.http; + +/** + * Created by ecoolper on 2017/4/28. + */ +public enum HttpType { + /** + * jodd-http. + */ + JODD_HTTP, + /** + * apache httpclient. + */ + APACHE_HTTP, + /** + * okhttp. + */ + OK_HTTP +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java deleted file mode 100644 index 57024f5001..0000000000 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java +++ /dev/null @@ -1,93 +0,0 @@ -package me.chanjar.weixin.common.util.http; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.fs.FileUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.Header; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.entity.ContentType; -import org.apache.http.impl.client.CloseableHttpClient; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File - * 视频文件不支持下载 - * @author Daniel Qian - */ -public class MediaDownloadRequestExecutor implements RequestExecutor { - - private File tmpDirFile; - - public MediaDownloadRequestExecutor() { - } - - public MediaDownloadRequestExecutor(File tmpDirFile) { - this.tmpDirFile = tmpDirFile; - } - - @Override - public File execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String queryParam) throws WxErrorException, IOException { - if (queryParam != null) { - if (uri.indexOf('?') == -1) { - uri += '?'; - } - uri += uri.endsWith("?") ? queryParam : '&' + queryParam; - } - - HttpGet httpGet = new HttpGet(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpGet.setConfig(config); - } - - try (CloseableHttpResponse response = httpclient.execute(httpGet); - InputStream inputStream = InputStreamResponseHandler.INSTANCE - .handleResponse(response)) { - - Header[] contentTypeHeader = response.getHeaders("Content-Type"); - if (contentTypeHeader != null && contentTypeHeader.length > 0) { - if (contentTypeHeader[0].getValue().startsWith(ContentType.APPLICATION_JSON.getMimeType())) { - // application/json; encoding=utf-8 下载媒体文件出错 - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - throw new WxErrorException(WxError.fromJson(responseContent)); - } - } - - String fileName = getFileName(response); - if (StringUtils.isBlank(fileName)) { - return null; - } - - String[] nameAndExt = fileName.split("\\."); - return FileUtils.createTmpFile(inputStream, nameAndExt[0], nameAndExt[1], this.tmpDirFile); - - } finally { - httpGet.releaseConnection(); - } - - } - - private String getFileName(CloseableHttpResponse response) throws WxErrorException { - Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); - if(contentDispositionHeader == null || contentDispositionHeader.length == 0){ - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } - - Pattern p = Pattern.compile(".*filename=\"(.*)\""); - Matcher m = p.matcher(contentDispositionHeader[0].getValue()); - if(m.matches()){ - return m.group(1); - } - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } - -} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java index 54ded05a7a..14724412f1 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java @@ -1,53 +1,43 @@ package me.chanjar.weixin.common.util.http; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.impl.client.CloseableHttpClient; - import java.io.File; import java.io.IOException; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.apache.ApacheMediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaUploadRequestExecutor; + /** - * 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String + * 上传媒体文件请求执行器. + * 请求的参数是File, 返回的结果是String * * @author Daniel Qian */ -public class MediaUploadRequestExecutor implements RequestExecutor { +public abstract class MediaUploadRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MediaUploadRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } @Override - public WxMediaUploadResult execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, File file) throws WxErrorException, IOException { - HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpPost.setConfig(config); - } - if (file != null) { - HttpEntity entity = MultipartEntityBuilder - .create() - .addBinaryBody("media", file) - .setMode(HttpMultipartMode.RFC6532) - .build(); - httpPost.setEntity(entity); - httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); - } - try (CloseableHttpResponse response = httpclient.execute(httpPost)) { - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - return WxMediaUploadResult.fromJson(responseContent); - } finally { - httpPost.releaseConnection(); + public void execute(String uri, File data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new ApacheMediaUploadRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddHttpMediaUploadRequestExecutor(requestHttp); + case OK_HTTP: + return new OkHttpMediaUploadRequestExecutor(requestHttp); + default: + return null; } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java index 6d2dbe9ab8..da1292ba62 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java @@ -1,27 +1,40 @@ package me.chanjar.weixin.common.util.http; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.apache.http.HttpHost; -import org.apache.http.impl.client.CloseableHttpClient; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; import java.io.IOException; /** - * http请求执行器 + * http请求执行器. * * @param 返回值类型 * @param 请求参数类型 + * @author Daniel Qian */ public interface RequestExecutor { /** - * @param httpclient 传入的httpClient - * @param httpProxy http代理对象,如果没有配置代理则为空 - * @param uri uri - * @param data 数据 - * @throws WxErrorException - * @throws IOException + * 执行http请求. + * + * @param uri uri + * @param data 数据 + * @param wxType 微信模块类型 + * @return 响应结果 + * @throws WxErrorException 自定义异常 + * @throws IOException io异常 */ - T execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, E data) throws WxErrorException, IOException; + T execute(String uri, E data, WxType wxType) throws WxErrorException, IOException; + /** + * 执行http请求. + * + * @param uri uri + * @param data 数据 + * @param handler http响应处理器 + * @param wxType 微信模块类型 + * @throws WxErrorException 自定义异常 + * @throws IOException io异常 + */ + void execute(String uri, E data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java new file mode 100644 index 0000000000..b7bc850f8f --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.common.util.http; + +/** + * Created by ecoolper on 2017/4/22. + * + * @author ecoolper + */ +public interface RequestHttp { + + /** + * 返回httpClient. + * + * @return 返回httpClient + */ + H getRequestHttpClient(); + + /** + * 返回httpProxy. + * + * @return 返回httpProxy + */ + P getRequestHttpProxy(); + + /** + * 返回HttpType. + * + * @return HttpType + */ + HttpType getRequestType(); + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ResponseHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ResponseHandler.java new file mode 100644 index 0000000000..1571764284 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ResponseHandler.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.common.util.http; + +/** + *
    + * http请求响应回调处理接口.
    + * Created by Binary Wang on 2018/12/8.
    + * 
    + * + * @param 返回值类型 + * @author Binary Wang + */ +public interface ResponseHandler { + /** + * 响应结果处理. + * + * @param t 要处理的对象 + */ + void handle(T t); +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java index ec221e28b8..a0ce7a17e2 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java @@ -1,46 +1,51 @@ package me.chanjar.weixin.common.util.http; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; - import java.io.IOException; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.apache.ApacheSimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimpleGetRequestExecutor; + /** - * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String + * 简单的GET请求执行器. + * 请求的参数是String, 返回的结果也是String * * @author Daniel Qian */ -public class SimpleGetRequestExecutor implements RequestExecutor { +public abstract class SimpleGetRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public SimpleGetRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } @Override - public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String queryParam) throws WxErrorException, IOException { - if (queryParam != null) { - if (uri.indexOf('?') == -1) { - uri += '?'; - } - uri += uri.endsWith("?") ? queryParam : '&' + queryParam; - } - HttpGet httpGet = new HttpGet(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpGet.setConfig(config); - } + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } - try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - return responseContent; - } finally { - httpGet.releaseConnection(); + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new ApacheSimpleGetRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddHttpSimpleGetRequestExecutor(requestHttp); + case OK_HTTP: + return new OkHttpSimpleGetRequestExecutor(requestHttp); + default: + throw new IllegalArgumentException("非法请求参数"); } } + protected String handleResponse(WxType wxType, String responseContent) throws WxErrorException { + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return responseContent; + } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java index bdea8573c0..c0952b32d4 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java @@ -1,59 +1,61 @@ package me.chanjar.weixin.common.util.http; -import java.io.IOException; - -import org.apache.http.Consts; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.apache.ApacheSimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimplePostRequestExecutor; +import org.jetbrains.annotations.NotNull; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import java.io.IOException; /** * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String * * @author Daniel Qian */ -public class SimplePostRequestExecutor implements RequestExecutor { +public abstract class SimplePostRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public SimplePostRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } @Override - public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String postEntity) throws WxErrorException, IOException { - HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpPost.setConfig(config); + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) + throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new ApacheSimplePostRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddHttpSimplePostRequestExecutor(requestHttp); + case OK_HTTP: + return new OkHttpSimplePostRequestExecutor(requestHttp); + default: + throw new IllegalArgumentException("非法请求参数"); } + } - if (postEntity != null) { - StringEntity entity = new StringEntity(postEntity, Consts.UTF_8); - httpPost.setEntity(entity); + @NotNull + public String handleResponse(WxType wxType, String responseContent) throws WxErrorException { + if (responseContent.isEmpty()) { + throw new WxErrorException(WxError.builder().errorCode(9999).errorMsg("无响应内容").build()); } - try (CloseableHttpResponse response = httpclient.execute(httpPost)) { - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - if (responseContent.isEmpty()) { - throw new WxErrorException( - WxError.newBuilder().setErrorCode(9999).setErrorMsg("无响应内容") - .build()); - } - - if (responseContent.startsWith("")) { - //xml格式输出直接返回 - return responseContent; - } - - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } + if (responseContent.startsWith("")) { + //xml格式输出直接返回 return responseContent; - } finally { - httpPost.releaseConnection(); } - } + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return responseContent; + } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java index 7e42aba72e..4e45c65d69 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java @@ -1,9 +1,9 @@ package me.chanjar.weixin.common.util.http; -import org.apache.commons.lang3.StringUtils; +import java.nio.charset.StandardCharsets; -import java.io.UnsupportedEncodingException; +import org.apache.commons.lang3.StringUtils; public class URIUtil { @@ -16,27 +16,22 @@ public static String encodeURIComponent(String input) { int l = input.length(); StringBuilder o = new StringBuilder(l * 3); - try { - for (int i = 0; i < l; i++) { - String e = input.substring(i, i + 1); - if (ALLOWED_CHARS.indexOf(e) == -1) { - byte[] b = e.getBytes("utf-8"); - o.append(getHex(b)); - continue; - } - o.append(e); + for (int i = 0; i < l; i++) { + String e = input.substring(i, i + 1); + if (!ALLOWED_CHARS.contains(e)) { + byte[] b = e.getBytes(StandardCharsets.UTF_8); + o.append(getHex(b)); + continue; } - return o.toString(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + o.append(e); } - return input; + return o.toString(); } - private static String getHex(byte buf[]) { + private static String getHex(byte[] buf) { StringBuilder o = new StringBuilder(buf.length * 3); - for (int i = 0; i < buf.length; i++) { - int n = buf[i] & 0xff; + for (byte aBuf : buf) { + int n = aBuf & 0xff; o.append("%"); if (n < 0x10) { o.append("0"); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/WxDnsResolver.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/WxDnsResolver.java new file mode 100644 index 0000000000..6c6137089a --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/WxDnsResolver.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.common.util.http; + +import org.apache.http.conn.DnsResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +/** + * 微信DNS域名解析器,将微信域名绑定到指定IP + * -------------------------------------------- + * 适用于服务器端调用微信服务器需要开通出口防火墙情况 + *

    + * Created by Andy Huo on 17/03/28. + */ +public class WxDnsResolver implements DnsResolver { + + private final static String WECHAT_API_URL = "api.weixin.qq.com"; + private static Map MAPPINGS = new HashMap(); + protected final Logger log = LoggerFactory.getLogger(WxDnsResolver.class); + private String wxApiIp; + + public WxDnsResolver(String ip) { + + this.wxApiIp = ip; + this.init(); + } + + private void init() { + if (log.isDebugEnabled()) { + log.debug("init wechat dns config with ip {}", wxApiIp); + } + try { + MAPPINGS.put(WECHAT_API_URL, new InetAddress[]{InetAddress.getByName(wxApiIp)}); + } catch (UnknownHostException e) { + //如果初始化DNS配置失败则使用默认配置,不影响服务的启动 + log.error("init WxDnsResolver error", e); + MAPPINGS = new HashMap(); + } + + } + + @Override + public InetAddress[] resolve(String host) throws UnknownHostException { + + + return MAPPINGS.containsKey(host) ? MAPPINGS.get(host) : new InetAddress[0]; + } + + public String getWxApiIp() { + return wxApiIp; + } + + public void setWxApiIp(String wxApiIp) { + this.wxApiIp = wxApiIp; + this.init(); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java similarity index 63% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java index c932445d33..fcd56c48a7 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java @@ -1,53 +1,44 @@ -package me.chanjar.weixin.common.util.http; +package me.chanjar.weixin.common.util.http.apache; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; /** - * httpclient build interface + * httpclient build interface. + * * @author kakotor */ public interface ApacheHttpClientBuilder { /** - * 构建httpclient实例 + * 构建httpclient实例. * * @return new instance of CloseableHttpClient */ CloseableHttpClient build(); /** - * 代理服务器地址 - * - * @param httpProxyHost + * 代理服务器地址. */ ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); /** - * 代理服务器端口 - * - * @param httpProxyPort + * 代理服务器端口. */ ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); /** - * 代理服务器用户名 - * - * @param httpProxyUsername + * 代理服务器用户名. */ ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); /** - * 代理服务器密码 - * - * @param httpProxyPassword + * 代理服务器密码. */ ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); /** - * ssl连接socket工厂 - * - * @param sslConnectionSocketFactory + * ssl连接socket工厂. */ ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java similarity index 71% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpClientBuilder.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java index 030fe98fe2..fe5472f3c0 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java @@ -1,6 +1,11 @@ -package me.chanjar.weixin.common.util.http; +package me.chanjar.weixin.common.util.http.apache; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHost; import org.apache.http.annotation.NotThreadSafe; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -10,6 +15,7 @@ import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.config.SocketConfig; +import org.apache.http.conn.DnsResolver; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; @@ -23,18 +29,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - /** - * httpclient 连接管理器 + * httpclient 连接管理器 自带DNS解析. + *

    大部分代码拷贝自:DefaultApacheHttpClientBuilder

    * - * @author kakotor + * @author Andy.Huo */ @NotThreadSafe -public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder { - protected final Logger log = LoggerFactory.getLogger(DefaultApacheHttpClientBuilder.class); +public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { + protected final Logger log = LoggerFactory.getLogger(ApacheHttpDnsClientBuilder.class); private final AtomicBoolean prepared = new AtomicBoolean(false); private int connectionRequestTimeout = 3000; private int connectionTimeout = 5000; @@ -44,6 +47,9 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder { private int maxConnPerHost = 10; private int maxTotalConn = 50; private String userAgent; + + private DnsResolver dnsResover; + private HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() { @Override public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { @@ -56,17 +62,18 @@ public boolean retryRequest(IOException exception, int executionCount, HttpConte private int httpProxyPort; private String httpProxyUsername; private String httpProxyPassword; + /** - * 闲置连接监控线程 + * 闲置连接监控线程. */ private IdleConnectionMonitorThread idleConnectionMonitorThread; private HttpClientBuilder httpClientBuilder; - private DefaultApacheHttpClientBuilder() { + private ApacheHttpDnsClientBuilder() { } - public static DefaultApacheHttpClientBuilder get() { - return new DefaultApacheHttpClientBuilder(); + public static ApacheHttpDnsClientBuilder get() { + return new ApacheHttpDnsClientBuilder(); } @Override @@ -102,8 +109,7 @@ public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFac /** * 获取链接的超时时间设置,默认3000ms *

    - * 设置为零时不超时,一直等待. - * 设置为负数是使用系统默认设置(非上述的3000ms的默认值,而是httpclient的默认设置). + * 设置为零时不超时,一直等待. 设置为负数是使用系统默认设置(非上述的3000ms的默认值,而是httpclient的默认设置). *

    * * @param connectionRequestTimeout 获取链接的超时时间设置(单位毫秒),默认3000ms @@ -115,8 +121,7 @@ public void setConnectionRequestTimeout(int connectionRequestTimeout) { /** * 建立链接的超时时间,默认为5000ms.由于是在链接池获取链接,此设置应该并不起什么作用 *

    - * 设置为零时不超时,一直等待. - * 设置为负数是使用系统默认设置(非上述的5000ms的默认值,而是httpclient的默认设置). + * 设置为零时不超时,一直等待. 设置为负数是使用系统默认设置(非上述的5000ms的默认值,而是httpclient的默认设置). *

    * * @param connectionTimeout 建立链接的超时时间设置(单位毫秒),默认5000ms @@ -157,7 +162,7 @@ public void setCheckWaitTime(int checkWaitTime) { } /** - * 每路的最大链接数,默认10 + * 每路的最大链接数,默认10. * * @param maxConnPerHost 每路的最大链接数,默认10 */ @@ -166,7 +171,7 @@ public void setMaxConnPerHost(int maxConnPerHost) { } /** - * 最大总连接数,默认50 + * 最大总连接数,默认50. * * @param maxTotalConn 最大总连接数,默认50 */ @@ -175,7 +180,7 @@ public void setMaxTotalConn(int maxTotalConn) { } /** - * 自定义httpclient的User Agent + * 自定义httpclient的User Agent. * * @param userAgent User Agent */ @@ -188,50 +193,54 @@ public IdleConnectionMonitorThread getIdleConnectionMonitorThread() { } private synchronized void prepare() { - if(prepared.get()){ + if (prepared.get()) { return; } - Registry registry = RegistryBuilder.create() - .register("http", this.plainConnectionSocketFactory) - .register("https", this.sslConnectionSocketFactory) - .build(); + + Registry registry = + RegistryBuilder.create() + .register("http", this.plainConnectionSocketFactory) + .register("https", this.sslConnectionSocketFactory) + .build(); @SuppressWarnings("resource") - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry); + PoolingHttpClientConnectionManager connectionManager; + if (dnsResover != null) { + if (log.isDebugEnabled()) { + log.debug("specified dns resolver."); + } + connectionManager = new PoolingHttpClientConnectionManager(registry, dnsResover); + } else { + if (log.isDebugEnabled()) { + log.debug("Not specified dns resolver."); + } + connectionManager = new PoolingHttpClientConnectionManager(registry); + } + connectionManager.setMaxTotal(this.maxTotalConn); connectionManager.setDefaultMaxPerRoute(this.maxConnPerHost); - connectionManager.setDefaultSocketConfig( - SocketConfig.copy(SocketConfig.DEFAULT) - .setSoTimeout(this.soTimeout) - .build() - ); + connectionManager + .setDefaultSocketConfig(SocketConfig.copy(SocketConfig.DEFAULT).setSoTimeout(this.soTimeout).build()); this.idleConnectionMonitorThread = new IdleConnectionMonitorThread( connectionManager, this.idleConnTimeout, this.checkWaitTime); this.idleConnectionMonitorThread.setDaemon(true); this.idleConnectionMonitorThread.start(); - this.httpClientBuilder = HttpClients.custom() - .setConnectionManager(connectionManager) + this.httpClientBuilder = HttpClients.custom().setConnectionManager(connectionManager) .setConnectionManagerShared(true) - .setDefaultRequestConfig( - RequestConfig.custom() - .setSocketTimeout(this.soTimeout) - .setConnectTimeout(this.connectionTimeout) - .setConnectionRequestTimeout(this.connectionRequestTimeout) - .build() - ) + .setDefaultRequestConfig(RequestConfig.custom().setSocketTimeout(this.soTimeout) + .setConnectTimeout(this.connectionTimeout) + .setConnectionRequestTimeout(this.connectionRequestTimeout).build()) .setRetryHandler(this.httpRequestRetryHandler); - if (StringUtils.isNotBlank(this.httpProxyHost) - && StringUtils.isNotBlank(this.httpProxyUsername)) { + if (StringUtils.isNotBlank(this.httpProxyHost) && StringUtils.isNotBlank(this.httpProxyUsername)) { // 使用代理服务器 需要用户认证的代理服务器 CredentialsProvider provider = new BasicCredentialsProvider(); - provider.setCredentials( - new AuthScope(this.httpProxyHost, this.httpProxyPort), - new UsernamePasswordCredentials(this.httpProxyUsername, - this.httpProxyPassword)); + provider.setCredentials(new AuthScope(this.httpProxyHost, this.httpProxyPort), + new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); this.httpClientBuilder.setDefaultCredentialsProvider(provider); + this.httpClientBuilder.setProxy(new HttpHost(this.httpProxyHost, this.httpProxyPort)); } if (StringUtils.isNotBlank(this.userAgent)) { @@ -242,18 +251,29 @@ private synchronized void prepare() { @Override public CloseableHttpClient build() { - if(!prepared.get()){ + if (!prepared.get()) { prepare(); } return this.httpClientBuilder.build(); } + public DnsResolver getDnsResover() { + return dnsResover; + } + + public void setDnsResover(DnsResolver dnsResover) { + this.dnsResover = dnsResover; + } + public static class IdleConnectionMonitorThread extends Thread { private final HttpClientConnectionManager connMgr; private final int idleConnTimeout; private final int checkWaitTime; private volatile boolean shutdown; + /** + * 构造方法. + */ public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr, int idleConnTimeout, int checkWaitTime) { super("IdleConnectionMonitorThread"); this.connMgr = connMgr; @@ -268,20 +288,26 @@ public void run() { synchronized (this) { wait(this.checkWaitTime); this.connMgr.closeExpiredConnections(); - this.connMgr.closeIdleConnections(this.idleConnTimeout, - TimeUnit.MILLISECONDS); + this.connMgr.closeIdleConnections(this.idleConnTimeout, TimeUnit.MILLISECONDS); } } } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); } } + /** + * 触发. + */ public void trigger() { synchronized (this) { notifyAll(); } } + /** + * 关闭. + */ public void shutdown() { this.shutdown = true; synchronized (this) { @@ -289,4 +315,5 @@ public void shutdown() { } } } + } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java new file mode 100644 index 0000000000..5bb4aeba90 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java @@ -0,0 +1,79 @@ +package me.chanjar.weixin.common.util.http.apache; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.HttpResponseProxy; +import me.chanjar.weixin.common.util.http.RequestHttp; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * . + * + * @author ecoolper + * @date 2017/5/5 + */ +public class ApacheMediaDownloadRequestExecutor extends BaseMediaDownloadRequestExecutor { + public ApacheMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + super(requestHttp, tmpDirFile); + } + + @Override + public File execute(String uri, String queryParam, WxType wxType) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + HttpGet httpGet = new HttpGet(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet); + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0) { + if (contentTypeHeader[0].getValue().startsWith(ContentType.APPLICATION_JSON.getMimeType())) { + // application/json; encoding=utf-8 下载媒体文件出错 + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent, wxType)); + } + } + + String fileName = new HttpResponseProxy(response).getFileName(); + if (StringUtils.isBlank(fileName)) { + fileName = String.valueOf(System.currentTimeMillis()); + } + + String baseName = FilenameUtils.getBaseName(fileName); + if (StringUtils.isBlank(fileName) || baseName.length() < 3) { + baseName = String.valueOf(System.currentTimeMillis()); + } + + return FileUtils.createTmpFile(inputStream, baseName, FilenameUtils.getExtension(fileName), + super.tmpDirFile); + + } finally { + httpGet.releaseConnection(); + } + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaUploadRequestExecutor.java new file mode 100644 index 0000000000..ca33b8641f --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaUploadRequestExecutor.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.common.util.http.apache; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class ApacheMediaUploadRequestExecutor extends MediaUploadRequestExecutor { + public ApacheMediaUploadRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaUploadResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + if (file != null) { + HttpEntity entity = MultipartEntityBuilder + .create() + .addBinaryBody("media", file) + .setMode(HttpMultipartMode.RFC6532) + .build(); + httpPost.setEntity(entity); + } + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimpleGetRequestExecutor.java new file mode 100644 index 0000000000..6b365edd09 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimpleGetRequestExecutor.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.common.util.http.apache; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +public class ApacheSimpleGetRequestExecutor extends SimpleGetRequestExecutor { + public ApacheSimpleGetRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public String execute(String uri, String queryParam, WxType wxType) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + HttpGet httpGet = new HttpGet(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + return handleResponse(wxType, responseContent); + } finally { + httpGet.releaseConnection(); + } + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimplePostRequestExecutor.java new file mode 100644 index 0000000000..4e6f31ec67 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimplePostRequestExecutor.java @@ -0,0 +1,50 @@ +package me.chanjar.weixin.common.util.http.apache; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import org.apache.http.Consts; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +public class ApacheSimplePostRequestExecutor extends SimplePostRequestExecutor { + public ApacheSimplePostRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public String execute(String uri, String postEntity, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + + if (postEntity != null) { + StringEntity entity = new StringEntity(postEntity, Consts.UTF_8); + entity.setContentType("application/json; charset=utf-8"); + httpPost.setEntity(entity); + } + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + return this.handleResponse(wxType, responseContent); + } finally { + httpPost.releaseConnection(); + } + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java new file mode 100644 index 0000000000..3fb08ab2c6 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java @@ -0,0 +1,288 @@ +package me.chanjar.weixin.common.util.http.apache; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHost; +import org.apache.http.annotation.NotThreadSafe; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContexts; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * httpclient 连接管理器 + * + * @author kakotor + */ +@Slf4j +@Data +@NotThreadSafe +public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder { + private final AtomicBoolean prepared = new AtomicBoolean(false); + + /** + * 获取链接的超时时间设置 + *

    + * 设置为零时不超时,一直等待. + * 设置为负数是使用系统默认设置(非3000ms的默认值,而是httpClient的默认设置). + *

    + */ + private int connectionRequestTimeout = -1; + + /** + * 建立链接的超时时间,默认为5000ms.由于是在链接池获取链接,此设置应该并不起什么作用 + *

    + * 设置为零时不超时,一直等待. + * 设置为负数是使用系统默认设置(非上述的5000ms的默认值,而是httpclient的默认设置). + *

    + */ + private int connectionTimeout = 5000; + /** + * 默认NIO的socket超时设置,默认5000ms. + */ + private int soTimeout = 5000; + /** + * 空闲链接的超时时间,默认60000ms. + *

    + * 超时的链接将在下一次空闲链接检查是被销毁 + *

    + */ + private int idleConnTimeout = 60000; + /** + * 检查空间链接的间隔周期,默认60000ms. + */ + private int checkWaitTime = 60000; + /** + * 每路的最大链接数,默认10 + */ + private int maxConnPerHost = 10; + /** + * 最大总连接数,默认50 + */ + private int maxTotalConn = 50; + /** + * 自定义httpclient的User Agent + */ + private String userAgent; + + private final HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() { + @Override + public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { + return false; + } + }; + private SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory(); + private final PlainConnectionSocketFactory plainConnectionSocketFactory = PlainConnectionSocketFactory.getSocketFactory(); + private String httpProxyHost; + private int httpProxyPort; + private String httpProxyUsername; + private String httpProxyPassword; + /** + * 闲置连接监控线程 + */ + private IdleConnectionMonitorThread idleConnectionMonitorThread; + /** + * 持有client对象,仅初始化一次,避免多service实例的时候造成重复初始化的问题 + */ + private CloseableHttpClient closeableHttpClient; + + private DefaultApacheHttpClientBuilder() { + } + + public static DefaultApacheHttpClientBuilder get() { + return DefaultApacheHttpClientBuilder.SingletonHolder.INSTANCE; + } + + @Override + public ApacheHttpClientBuilder httpProxyHost(String httpProxyHost) { + this.httpProxyHost = httpProxyHost; + return this; + } + + @Override + public ApacheHttpClientBuilder httpProxyPort(int httpProxyPort) { + this.httpProxyPort = httpProxyPort; + return this; + } + + @Override + public ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername) { + this.httpProxyUsername = httpProxyUsername; + return this; + } + + @Override + public ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword) { + this.httpProxyPassword = httpProxyPassword; + return this; + } + + @Override + public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory) { + this.sslConnectionSocketFactory = sslConnectionSocketFactory; + return this; + } + + public IdleConnectionMonitorThread getIdleConnectionMonitorThread() { + return this.idleConnectionMonitorThread; + } + + private synchronized void prepare() { + if (prepared.get()) { + return; + } + Registry registry = RegistryBuilder.create() + .register("http", this.plainConnectionSocketFactory) + .register("https", this.sslConnectionSocketFactory) + .build(); + + @SuppressWarnings("resource") + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry); + connectionManager.setMaxTotal(this.maxTotalConn); + connectionManager.setDefaultMaxPerRoute(this.maxConnPerHost); + connectionManager.setDefaultSocketConfig( + SocketConfig.copy(SocketConfig.DEFAULT) + .setSoTimeout(this.soTimeout) + .build() + ); + + this.idleConnectionMonitorThread = new IdleConnectionMonitorThread( + connectionManager, this.idleConnTimeout, this.checkWaitTime); + this.idleConnectionMonitorThread.setDaemon(true); + this.idleConnectionMonitorThread.start(); + + HttpClientBuilder httpClientBuilder = HttpClients.custom() + .setConnectionManager(connectionManager) + .setConnectionManagerShared(true) + .setSSLSocketFactory(this.buildSSLConnectionSocketFactory()) + .setDefaultRequestConfig(RequestConfig.custom() + .setSocketTimeout(this.soTimeout) + .setConnectTimeout(this.connectionTimeout) + .setConnectionRequestTimeout(this.connectionRequestTimeout) + .build() + ).setRetryHandler(this.httpRequestRetryHandler); + + if (StringUtils.isNotBlank(this.httpProxyHost) && StringUtils.isNotBlank(this.httpProxyUsername)) { + // 使用代理服务器 需要用户认证的代理服务器 + CredentialsProvider provider = new BasicCredentialsProvider(); + provider.setCredentials(new AuthScope(this.httpProxyHost, this.httpProxyPort), + new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); + httpClientBuilder.setDefaultCredentialsProvider(provider); + httpClientBuilder.setProxy(new HttpHost(this.httpProxyHost, this.httpProxyPort)); + } + + if (StringUtils.isNotBlank(this.userAgent)) { + httpClientBuilder.setUserAgent(this.userAgent); + } + + this.closeableHttpClient = httpClientBuilder.build(); + prepared.set(true); + } + + private SSLConnectionSocketFactory buildSSLConnectionSocketFactory() { + try { + SSLContext sslcontext = SSLContexts.custom() + //忽略掉对服务器端证书的校验 + .loadTrustMaterial(new TrustStrategy() { + @Override + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + }).build(); + + return new SSLConnectionSocketFactory( + sslcontext, + new String[]{"TLSv1"}, + null, + SSLConnectionSocketFactory.getDefaultHostnameVerifier()); + } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { + log.error("构建SSL连接工厂时发生异常!", e); + } + + return null; + } + + @Override + public CloseableHttpClient build() { + if (!prepared.get()) { + prepare(); + } + return this.closeableHttpClient; + } + + /** + * DefaultApacheHttpClientBuilder 改为单例模式,并持有唯一的CloseableHttpClient(仅首次调用创建) + */ + private static class SingletonHolder { + private static final DefaultApacheHttpClientBuilder INSTANCE = new DefaultApacheHttpClientBuilder(); + } + + public static class IdleConnectionMonitorThread extends Thread { + private final HttpClientConnectionManager connMgr; + private final int idleConnTimeout; + private final int checkWaitTime; + private volatile boolean shutdown; + + public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr, int idleConnTimeout, int checkWaitTime) { + super("IdleConnectionMonitorThread"); + this.connMgr = connMgr; + this.idleConnTimeout = idleConnTimeout; + this.checkWaitTime = checkWaitTime; + } + + @Override + public void run() { + try { + while (!this.shutdown) { + synchronized (this) { + wait(this.checkWaitTime); + this.connMgr.closeExpiredConnections(); + this.connMgr.closeIdleConnections(this.idleConnTimeout, + TimeUnit.MILLISECONDS); + } + } + } catch (InterruptedException ignore) { + } + } + + public void trigger() { + synchronized (this) { + notifyAll(); + } + } + + public void shutdown() { + this.shutdown = true; + synchronized (this) { + notifyAll(); + } + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamResponseHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/InputStreamResponseHandler.java similarity index 80% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamResponseHandler.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/InputStreamResponseHandler.java index 0cc1562b59..5c72744cb0 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamResponseHandler.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/InputStreamResponseHandler.java @@ -1,4 +1,7 @@ -package me.chanjar.weixin.common.util.http; +package me.chanjar.weixin.common.util.http.apache; + +import java.io.IOException; +import java.io.InputStream; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -7,18 +10,20 @@ import org.apache.http.client.ResponseHandler; import org.apache.http.util.EntityUtils; -import java.io.IOException; -import java.io.InputStream; - +/** + * 输入流响应处理器. + * + * @author Daniel Qian + */ public class InputStreamResponseHandler implements ResponseHandler { - public static final ResponseHandler INSTANCE = new InputStreamResponseHandler(); + private static final int STATUS_CODE_300 = 300; @Override public InputStream handleResponse(final HttpResponse response) throws IOException { final StatusLine statusLine = response.getStatusLine(); final HttpEntity entity = response.getEntity(); - if (statusLine.getStatusCode() >= 300) { + if (statusLine.getStatusCode() >= STATUS_CODE_300) { EntityUtils.consume(entity); throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/Utf8ResponseHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/Utf8ResponseHandler.java similarity index 92% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/Utf8ResponseHandler.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/Utf8ResponseHandler.java index 148ef9650d..035726d44f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/Utf8ResponseHandler.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/Utf8ResponseHandler.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.common.util.http; +package me.chanjar.weixin.common.util.http.apache; import org.apache.http.Consts; import org.apache.http.HttpEntity; @@ -25,7 +25,7 @@ public String handleResponse(final HttpResponse response) throws IOException { final HttpEntity entity = response.getEntity(); if (statusLine.getStatusCode() >= 300) { EntityUtils.consume(entity); - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.toString()); } return entity == null ? null : EntityUtils.toString(entity, Consts.UTF_8); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaDownloadRequestExecutor.java new file mode 100644 index 0000000000..5c67cbffe1 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaDownloadRequestExecutor.java @@ -0,0 +1,77 @@ +package me.chanjar.weixin.common.util.http.jodd; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.HttpResponseProxy; +import me.chanjar.weixin.common.util.http.RequestHttp; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * . + * + * @author ecoolper + * @date 2017/5/5 + */ +public class JoddHttpMediaDownloadRequestExecutor extends BaseMediaDownloadRequestExecutor { + public JoddHttpMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + super(requestHttp, tmpDirFile); + } + + @Override + public File execute(String uri, String queryParam, WxType wxType) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + HttpRequest request = HttpRequest.get(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + + String contentType = response.header("Content-Type"); + if (contentType != null && contentType.startsWith("application/json")) { + // application/json; encoding=utf-8 下载媒体文件出错 + throw new WxErrorException(WxError.fromJson(response.bodyText(), wxType)); + } + + String fileName = new HttpResponseProxy(response).getFileName(); + if (StringUtils.isBlank(fileName)) { + return null; + } + + String baseName = FilenameUtils.getBaseName(fileName); + if (StringUtils.isBlank(fileName) || baseName.length() < 3) { + baseName = String.valueOf(System.currentTimeMillis()); + } + + try (InputStream inputStream = new ByteArrayInputStream(response.bodyBytes())) { + return FileUtils.createTmpFile(inputStream, + baseName, + FilenameUtils.getExtension(fileName), + super.tmpDirFile); + } + } + + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaUploadRequestExecutor.java new file mode 100644 index 0000000000..93f25fb740 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaUploadRequestExecutor.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.common.util.http.jodd; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; + +import java.io.File; +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/5 + */ +public class JoddHttpMediaUploadRequestExecutor extends MediaUploadRequestExecutor { + public JoddHttpMediaUploadRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaUploadResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException { + HttpRequest request = HttpRequest.post(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + request.form("media", file); + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + + String responseContent = response.bodyText(); + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimpleGetRequestExecutor.java new file mode 100644 index 0000000000..e8adad6b66 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimpleGetRequestExecutor.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.common.util.http.jodd; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; + +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +public class JoddHttpSimpleGetRequestExecutor extends SimpleGetRequestExecutor { + public JoddHttpSimpleGetRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public String execute(String uri, String queryParam, WxType wxType) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + HttpRequest request = HttpRequest.get(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + + return handleResponse(wxType, response.bodyText()); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimplePostRequestExecutor.java new file mode 100644 index 0000000000..7b2f5f61ef --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimplePostRequestExecutor.java @@ -0,0 +1,45 @@ +package me.chanjar.weixin.common.util.http.jodd; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; + +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +public class JoddHttpSimplePostRequestExecutor extends SimplePostRequestExecutor { + public JoddHttpSimplePostRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public String execute(String uri, String postEntity, WxType wxType) throws WxErrorException, IOException { + HttpConnectionProvider provider = requestHttp.getRequestHttpClient(); + ProxyInfo proxyInfo = requestHttp.getRequestHttpProxy(); + + HttpRequest request = HttpRequest.post(uri); + if (proxyInfo != null) { + provider.useProxy(proxyInfo); + } + request.withConnectionProvider(provider); + if (postEntity != null) { + request.bodyText(postEntity); + } + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + + return this.handleResponse(wxType, response.bodyText()); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaDownloadRequestExecutor.java new file mode 100644 index 0000000000..ff0cea1c2b --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaDownloadRequestExecutor.java @@ -0,0 +1,76 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.HttpResponseProxy; +import me.chanjar.weixin.common.util.http.RequestHttp; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okio.BufferedSink; +import okio.Okio; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.IOException; + +/** + *. + * @author ecoolper + * @date 2017/5/5 + */ +@Slf4j +public class OkHttpMediaDownloadRequestExecutor extends BaseMediaDownloadRequestExecutor { + public OkHttpMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + super(requestHttp, tmpDirFile); + } + + @Override + public File execute(String uri, String queryParam, WxType wxType) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + //得到httpClient + OkHttpClient client = requestHttp.getRequestHttpClient(); + + Request request = new Request.Builder().url(uri).get().build(); + + Response response = client.newCall(request).execute(); + + String contentType = response.header("Content-Type"); + if (contentType != null && contentType.startsWith("application/json")) { + // application/json; encoding=utf-8 下载媒体文件出错 + throw new WxErrorException(WxError.fromJson(response.body().string(), wxType)); + } + + String fileName = new HttpResponseProxy(response).getFileName(); + if (StringUtils.isBlank(fileName)) { + return null; + } + + String baseName = FilenameUtils.getBaseName(fileName); + if (StringUtils.isBlank(fileName) || baseName.length() < 3) { + baseName = String.valueOf(System.currentTimeMillis()); + } + + File file = File.createTempFile( + baseName, "." + FilenameUtils.getExtension(fileName), super.tmpDirFile + ); + + try (BufferedSink sink = Okio.buffer(Okio.sink(file))) { + sink.writeAll(response.body().source()); + } + + file.deleteOnExit(); + return file; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaUploadRequestExecutor.java new file mode 100644 index 0000000000..6d2602d3df --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaUploadRequestExecutor.java @@ -0,0 +1,45 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import okhttp3.*; + +import java.io.File; +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/5 + */ +public class OkHttpMediaUploadRequestExecutor extends MediaUploadRequestExecutor { + public OkHttpMediaUploadRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaUploadResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException { + + RequestBody body = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("media", + file.getName(), + RequestBody.create(MediaType.parse("application/octet-stream"), file)) + .build(); + Request request = new Request.Builder().url(uri).post(body).build(); + + Response response = requestHttp.getRequestHttpClient().newCall(request).execute(); + String responseContent = response.body().string(); + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpProxyInfo.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpProxyInfo.java new file mode 100644 index 0000000000..eb9f7ac908 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpProxyInfo.java @@ -0,0 +1,116 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import java.net.InetSocketAddress; +import java.net.Proxy; + +/** + * Created by ecoolper on 2017/4/26. + * Proxy information. + */ +public class OkHttpProxyInfo { + private final String proxyAddress; + private final int proxyPort; + private final String proxyUsername; + private final String proxyPassword; + private final ProxyType proxyType; + + public OkHttpProxyInfo(ProxyType proxyType, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) { + this.proxyType = proxyType; + this.proxyAddress = proxyHost; + this.proxyPort = proxyPort; + this.proxyUsername = proxyUser; + this.proxyPassword = proxyPassword; + } + + /** + * Creates directProxy. + */ + public static OkHttpProxyInfo directProxy() { + return new OkHttpProxyInfo(ProxyType.NONE, null, 0, null, null); + } + + // ---------------------------------------------------------------- factory + + /** + * Creates SOCKS4 proxy. + */ + public static OkHttpProxyInfo socks4Proxy(String proxyAddress, int proxyPort, String proxyUser) { + return new OkHttpProxyInfo(ProxyType.SOCKS4, proxyAddress, proxyPort, proxyUser, null); + } + + /** + * Creates SOCKS5 proxy. + */ + public static OkHttpProxyInfo socks5Proxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) { + return new OkHttpProxyInfo(ProxyType.SOCKS5, proxyAddress, proxyPort, proxyUser, proxyPassword); + } + + /** + * Creates HTTP proxy. + */ + public static OkHttpProxyInfo httpProxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) { + return new OkHttpProxyInfo(ProxyType.HTTP, proxyAddress, proxyPort, proxyUser, proxyPassword); + } + + /** + * Returns proxy type. + */ + public ProxyType getProxyType() { + return proxyType; + } + + // ---------------------------------------------------------------- getter + + /** + * Returns proxy address. + */ + public String getProxyAddress() { + return proxyAddress; + } + + /** + * Returns proxy port. + */ + public int getProxyPort() { + return proxyPort; + } + + /** + * Returns proxy user name or null if + * no authentication required. + */ + public String getProxyUsername() { + return proxyUsername; + } + + /** + * Returns proxy password or null. + */ + public String getProxyPassword() { + return proxyPassword; + } + + /** + * 返回 java.net.Proxy + */ + public Proxy getProxy() { + Proxy proxy = null; + if (getProxyType().equals(ProxyType.SOCKS5)) { + proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(getProxyAddress(), getProxyPort())); + } else if (getProxyType().equals(ProxyType.SOCKS4)) { + proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(getProxyAddress(), getProxyPort())); + } else if (getProxyType().equals(ProxyType.HTTP)) { + proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(getProxyAddress(), getProxyPort())); + } else if (getProxyType().equals(ProxyType.NONE)) { + proxy = new Proxy(Proxy.Type.DIRECT, new InetSocketAddress(getProxyAddress(), getProxyPort())); + } + return proxy; + } + + /** + * Proxy types. + */ + public enum ProxyType { + NONE, HTTP, SOCKS4, SOCKS5 + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimpleGetRequestExecutor.java new file mode 100644 index 0000000000..9be073e38a --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimpleGetRequestExecutor.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +public class OkHttpSimpleGetRequestExecutor extends SimpleGetRequestExecutor { + public OkHttpSimpleGetRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public String execute(String uri, String queryParam, WxType wxType) throws WxErrorException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + //得到httpClient + OkHttpClient client = requestHttp.getRequestHttpClient(); + Request request = new Request.Builder().url(uri).build(); + Response response = client.newCall(request).execute(); + return this.handleResponse(wxType, response.body().string()); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimplePostRequestExecutor.java new file mode 100644 index 0000000000..788ea59260 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimplePostRequestExecutor.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import okhttp3.*; + +import java.io.IOException; +import java.util.Objects; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +@Slf4j +public class OkHttpSimplePostRequestExecutor extends SimplePostRequestExecutor { + public OkHttpSimplePostRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public String execute(String uri, String postEntity, WxType wxType) throws WxErrorException, IOException { + RequestBody body = RequestBody.Companion.create(postEntity, MediaType.parse("text/plain; charset=utf-8")); + Request request = new Request.Builder().url(uri).post(body).build(); + Response response = requestHttp.getRequestHttpClient().newCall(request).execute(); + return this.handleResponse(wxType, Objects.requireNonNull(response.body()).string()); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java index 6166da9e04..882853945a 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java @@ -1,14 +1,5 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.common.util.json; - import com.google.common.collect.Lists; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -16,7 +7,6 @@ import java.util.List; - public class GsonHelper { public static boolean isNull(JsonElement element) { @@ -77,7 +67,7 @@ public static Long getAsLong(JsonElement element) { public static long getAsPrimitiveLong(JsonElement element) { Long r = getAsLong(element); - return r == null ? 0l : r; + return r == null ? 0L : r; } public static Integer getAsInteger(JsonElement element) { @@ -95,7 +85,7 @@ public static Boolean getAsBoolean(JsonElement element) { public static boolean getAsPrimitiveBool(JsonElement element) { Boolean r = getAsBoolean(element); - return r != null && r.booleanValue(); + return r != null && r; } public static Double getAsDouble(JsonElement element) { @@ -130,6 +120,20 @@ public static Integer[] getIntArray(JsonObject o, String string) { return result.toArray(new Integer[0]); } + public static String[] getStringArray(JsonObject o, String string) { + JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string)); + if (jsonArray == null) { + return null; + } + + List result = Lists.newArrayList(); + for (int i = 0; i < jsonArray.size(); i++) { + result.add(jsonArray.get(i).getAsString()); + } + + return result.toArray(new String[0]); + } + public static Long[] getLongArray(JsonObject o, String string) { JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string)); if (jsonArray == null) { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonParser.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonParser.java new file mode 100644 index 0000000000..53f51e0f3e --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonParser.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.common.util.json; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; +import lombok.NoArgsConstructor; + +import java.io.Reader; + +/** + * @author niefy + */ +public class GsonParser { + private static final JsonParser JSON_PARSER = new JsonParser(); + + public static JsonObject parse(String json) { + return JSON_PARSER.parse(json).getAsJsonObject(); + } + + public static JsonObject parse(Reader json) { + return JSON_PARSER.parse(json).getAsJsonObject(); + } + + public static JsonObject parse(JsonReader json) { + return JSON_PARSER.parse(json).getAsJsonObject(); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxAccessTokenAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxAccessTokenAdapter.java index e7d700e9d1..4d19ec3ad9 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxAccessTokenAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxAccessTokenAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.common.util.json; import com.google.gson.*; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxBooleanTypeAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxBooleanTypeAdapter.java new file mode 100644 index 0000000000..3fbbef4a43 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxBooleanTypeAdapter.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.common.util.json; + +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import org.apache.commons.lang3.BooleanUtils; + +import java.io.IOException; + +/** + *
    + * Gson 布尔类型类型转换器
    + * Created by Binary Wang on 2017-7-8.
    + * 
    + * + * @author Binary Wang + */ +public class WxBooleanTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, Boolean value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value); + } + } + + @Override + public Boolean read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + switch (peek) { + case BOOLEAN: + return in.nextBoolean(); + case NULL: + in.nextNull(); + return null; + case NUMBER: + return BooleanUtils.toBoolean(in.nextInt()); + case STRING: + return BooleanUtils.toBoolean(in.nextString()); + default: + throw new JsonParseException("Expected BOOLEAN or NUMBER but was " + peek); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxDateTypeAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxDateTypeAdapter.java new file mode 100644 index 0000000000..fd54cf3f43 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxDateTypeAdapter.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.common.util.json; + +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.Date; + +/** + *
    + * Gson 日期类型转换器
    + * Created by Binary Wang on 2017-7-8.
    + * 
    + * + * @author Binary Wang + */ +public class WxDateTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, Date value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.getTime() / 1000); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + switch (peek) { + case NULL: + in.nextNull(); + return null; + case NUMBER: + return new Date(in.nextInt() * 1000); + default: + throw new JsonParseException("Expected NUMBER but was " + peek); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxErrorAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxErrorAdapter.java index cc426bc8ca..0ea52b9a86 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxErrorAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxErrorAdapter.java @@ -1,36 +1,31 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.common.util.json; import com.google.gson.*; -import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.error.WxError; import java.lang.reflect.Type; /** - * @author Daniel Qian + * @author Daniel Qian. */ public class WxErrorAdapter implements JsonDeserializer { @Override - public WxError deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - WxError wxError = new WxError(); + public WxError deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + WxError.WxErrorBuilder errorBuilder = WxError.builder(); JsonObject wxErrorJsonObject = json.getAsJsonObject(); if (wxErrorJsonObject.get("errcode") != null && !wxErrorJsonObject.get("errcode").isJsonNull()) { - wxError.setErrorCode(GsonHelper.getAsPrimitiveInt(wxErrorJsonObject.get("errcode"))); + errorBuilder.errorCode(GsonHelper.getAsPrimitiveInt(wxErrorJsonObject.get("errcode"))); } if (wxErrorJsonObject.get("errmsg") != null && !wxErrorJsonObject.get("errmsg").isJsonNull()) { - wxError.setErrorMsg(GsonHelper.getAsString(wxErrorJsonObject.get("errmsg"))); + errorBuilder.errorMsg(GsonHelper.getAsString(wxErrorJsonObject.get("errmsg"))); } - wxError.setJson(json.toString()); - return wxError; + + errorBuilder.json(json.toString()); + + return errorBuilder.build(); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java index 100ef52303..0624923508 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java @@ -2,15 +2,18 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; - import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.bean.WxNetCheckResult; import me.chanjar.weixin.common.bean.menu.WxMenu; -import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +/** + * . + * @author chanjarster + */ public class WxGsonBuilder { - - public static final GsonBuilder INSTANCE = new GsonBuilder(); + private static final GsonBuilder INSTANCE = new GsonBuilder(); static { INSTANCE.disableHtmlEscaping(); @@ -18,6 +21,8 @@ public class WxGsonBuilder { INSTANCE.registerTypeAdapter(WxError.class, new WxErrorAdapter()); INSTANCE.registerTypeAdapter(WxMenu.class, new WxMenuGsonAdapter()); INSTANCE.registerTypeAdapter(WxMediaUploadResult.class, new WxMediaUploadResultAdapter()); + INSTANCE.registerTypeAdapter(WxNetCheckResult.class, new WxNetCheckResultGsonAdapter()); + } public static Gson create() { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMediaUploadResultAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMediaUploadResultAdapter.java index 662b744274..3cb32c5ede 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMediaUploadResultAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMediaUploadResultAdapter.java @@ -1,18 +1,14 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.common.util.json; -import com.google.gson.*; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; - import java.lang.reflect.Type; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; + /** * @author Daniel Qian */ @@ -20,22 +16,25 @@ public class WxMediaUploadResultAdapter implements JsonDeserializer { + + + @Override + public WxNetCheckResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + WxNetCheckResult result = new WxNetCheckResult(); + + JsonArray dnssJson = json.getAsJsonObject().get("dns").getAsJsonArray(); + List dnsInfoList = new ArrayList<>(); + if (dnssJson != null && dnssJson.size() > 0) { + for (int i = 0; i < dnssJson.size(); i++) { + JsonObject buttonJson = dnssJson.get(i).getAsJsonObject(); + WxNetCheckResult.WxNetCheckDnsInfo dnsInfo = new WxNetCheckResult.WxNetCheckDnsInfo(); + dnsInfo.setIp(GsonHelper.getString(buttonJson, "ip")); + dnsInfo.setRealOperator(GsonHelper.getString(buttonJson, "real_operator")); + dnsInfoList.add(dnsInfo); + } + } + + JsonArray pingsJson = json.getAsJsonObject().get("ping").getAsJsonArray(); + List pingInfoList = new ArrayList<>(); + if (pingsJson != null && pingsJson.size() > 0) { + for (int i = 0; i < pingsJson.size(); i++) { + JsonObject pingJson = pingsJson.get(i).getAsJsonObject(); + WxNetCheckResult.WxNetCheckPingInfo pingInfo = new WxNetCheckResult.WxNetCheckPingInfo(); + pingInfo.setIp(GsonHelper.getString(pingJson, "ip")); + pingInfo.setFromOperator(GsonHelper.getString(pingJson, "from_operator")); + pingInfo.setPackageLoss(GsonHelper.getString(pingJson, "package_loss")); + pingInfo.setTime(GsonHelper.getString(pingJson, "time")); + pingInfoList.add(pingInfo); + } + } + result.setDnsInfos(dnsInfoList); + result.setPingInfos(pingInfoList); + return result; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/JedisDistributedLock.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/JedisDistributedLock.java new file mode 100644 index 0000000000..074f9d9351 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/JedisDistributedLock.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.common.util.locks; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +import com.github.jedis.lock.JedisLock; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.util.Pool; + +/** + * JedisPool 分布式锁 + * + * @author 007 + */ +public class JedisDistributedLock implements Lock { + private final Pool jedisPool; + private final JedisLock lock; + + public JedisDistributedLock(Pool jedisPool, String key){ + this.jedisPool = jedisPool; + this.lock = new JedisLock(key); + } + + @Override + public void lock() { + try (Jedis jedis = jedisPool.getResource()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + try (Jedis jedis = jedisPool.getResource()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } + } + + @Override + public boolean tryLock() { + try (Jedis jedis = jedisPool.getResource()) { + return lock.acquire(jedis); + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + try (Jedis jedis = jedisPool.getResource()) { + return lock.acquire(jedis); + } + } + + @Override + public void unlock() { + try (Jedis jedis = jedisPool.getResource()) { + lock.release(jedis); + } + } + + @Override + public Condition newCondition() { + throw new RuntimeException("unsupported method"); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java new file mode 100644 index 0000000000..dfac1c28fb --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java @@ -0,0 +1,126 @@ +package me.chanjar.weixin.common.util.locks; + +import lombok.Getter; +import lombok.NonNull; +import org.jetbrains.annotations.NotNull; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.data.redis.core.types.Expiration; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +/** + * 实现简单的redis分布式锁, 支持重入, 不是红锁 + * + * @see reids distlock + */ +public class RedisTemplateSimpleDistributedLock implements Lock { + + @Getter + private final StringRedisTemplate redisTemplate; + @Getter + private final String key; + @Getter + private final int leaseMilliseconds; + + private final ThreadLocal valueThreadLocal = new ThreadLocal<>(); + + public RedisTemplateSimpleDistributedLock(@NonNull StringRedisTemplate redisTemplate, int leaseMilliseconds) { + this(redisTemplate, "lock:" + UUID.randomUUID().toString(), leaseMilliseconds); + } + + public RedisTemplateSimpleDistributedLock(@NonNull StringRedisTemplate redisTemplate, @NonNull String key, int leaseMilliseconds) { + if (leaseMilliseconds <= 0) { + throw new IllegalArgumentException("Parameter 'leaseMilliseconds' must grate then 0: " + leaseMilliseconds); + } + this.redisTemplate = redisTemplate; + this.key = key; + this.leaseMilliseconds = leaseMilliseconds; + } + + @Override + public void lock() { + while (!tryLock()) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ignore + } + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + while (!tryLock()) { + Thread.sleep(1000); + } + } + + @Override + public boolean tryLock() { + String value = valueThreadLocal.get(); + if (value == null || value.length() == 0) { + value = UUID.randomUUID().toString(); + valueThreadLocal.set(value); + } + final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + final byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8); + List redisResults = redisTemplate.executePipelined(new RedisCallback() { + @Override + public String doInRedis(RedisConnection connection) throws DataAccessException { + connection.set(keyBytes, valueBytes, Expiration.milliseconds(leaseMilliseconds), RedisStringCommands.SetOption.SET_IF_ABSENT); + connection.get(keyBytes); + return null; + } + }); + Object currentLockSecret = redisResults.size() > 1 ? redisResults.get(1) : redisResults.get(0); + return currentLockSecret != null && currentLockSecret.toString().equals(value); + } + + @Override + public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { + long waitMs = unit.toMillis(time); + boolean locked = tryLock(); + while (!locked && waitMs > 0) { + long sleep = waitMs < 1000 ? waitMs : 1000; + Thread.sleep(sleep); + waitMs -= sleep; + locked = tryLock(); + } + return locked; + } + + @Override + public void unlock() { + if (valueThreadLocal.get() != null) { + // 提示: 必须指定returnType, 类型: 此处必须为Long, 不能是Integer + RedisScript script = new DefaultRedisScript("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", Long.class); + redisTemplate.execute(script, Arrays.asList(key), valueThreadLocal.get()); + valueThreadLocal.remove(); + } + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + + /** + * 获取当前锁的值 + * return 返回null意味着没有加锁, 但是返回非null值并不以为着当前加锁成功(redis中key可能自动过期) + */ + public String getLockSecretValue() { + return valueThreadLocal.get(); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java index 32f4e42aaa..e5bdb38804 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java @@ -19,6 +19,7 @@ import java.text.MessageFormat; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * An internationalization / localization helper class which reduces @@ -46,8 +47,7 @@ */ public class StringManager { - private static final Map> managers = - new Hashtable<>(); + private static final Map> MANAGERS = new ConcurrentHashMap<>(); private static int LOCALE_CACHE_SIZE = 10; /** * The ResourceBundle for this StringManager. @@ -103,7 +103,7 @@ private StringManager(String packageName, Locale locale) { * @param packageName The package name */ public static final synchronized StringManager getManager( - String packageName) { + String packageName) { return getManager(packageName, Locale.getDefault()); } @@ -116,28 +116,28 @@ public static final synchronized StringManager getManager( * @param locale The Locale */ public static final synchronized StringManager getManager( - String packageName, Locale locale) { + String packageName, Locale locale) { - Map map = managers.get(packageName); + Map map = MANAGERS.get(packageName); if (map == null) { - /* - * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. - * Expansion occurs when size() exceeds capacity. Therefore keep - * size at or below capacity. - * removeEldestEntry() executes after insertion therefore the test - * for removal needs to use one less than the maximum desired size - * - */ + /* + * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. + * Expansion occurs when size() exceeds capacity. Therefore keep + * size at or below capacity. + * removeEldestEntry() executes after insertion therefore the test + * for removal needs to use one less than the maximum desired size + * + */ map = new LinkedHashMap(LOCALE_CACHE_SIZE, 1, true) { private static final long serialVersionUID = 1L; @Override protected boolean removeEldestEntry( - Map.Entry eldest) { + Map.Entry eldest) { return size() > (LOCALE_CACHE_SIZE - 1); } }; - managers.put(packageName, map); + MANAGERS.put(packageName, map); } StringManager mgr = map.get(locale); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/IntegerArrayConverter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/IntegerArrayConverter.java new file mode 100644 index 0000000000..3a82b213ca --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/IntegerArrayConverter.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.common.util.xml; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import com.thoughtworks.xstream.converters.basic.StringConverter; + +/** + * Integer型数组转换器. + * + * @author Binary Wang + * @date 2019-08-22 + */ +public class IntegerArrayConverter extends StringConverter { + @Override + public boolean canConvert(Class type) { + return type == Integer[].class; + } + + @Override + public String toString(Object obj) { + return ""; + } + + @Override + public Object fromString(String str) { + final Iterable iterable = Splitter.on(",").split(str); + final String[] strings = Iterables.toArray(iterable, String.class); + Integer[] result = new Integer[strings.length]; + int index = 0; + for (String string : strings) { + result[index++] = Integer.parseInt(string); + } + + return result; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/LongArrayConverter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/LongArrayConverter.java new file mode 100644 index 0000000000..a383c59674 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/LongArrayConverter.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.common.util.xml; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import com.thoughtworks.xstream.converters.basic.StringConverter; + +/** + * Long型数组转换器. + * + * @author Binary Wang + * @date 2019-08-22 + */ +public class LongArrayConverter extends StringConverter { + @Override + public boolean canConvert(Class type) { + return type == Long[].class; + } + + @Override + public String toString(Object obj) { + return ""; + } + + @Override + public Object fromString(String str) { + final Iterable iterable = Splitter.on(",").split(str); + final String[] strings = Iterables.toArray(iterable, String.class); + Long[] result = new Long[strings.length]; + int index = 0; + for (String string : strings) { + result[index++] = Long.parseLong(string); + } + + return result; + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataConverter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataConverter.java index aa948b34b5..ab1b168831 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataConverter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataConverter.java @@ -2,6 +2,11 @@ import com.thoughtworks.xstream.converters.basic.StringConverter; +/** + * CDATA 内容转换器,加上CDATA标签. + * + * @author Daniel Qian + */ public class XStreamCDataConverter extends StringConverter { @Override diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java index ba49528aaf..5fd7ceb2cb 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java @@ -1,51 +1,80 @@ package me.chanjar.weixin.common.util.xml; -import java.io.Writer; - import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.basic.*; +import com.thoughtworks.xstream.converters.collections.CollectionConverter; +import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; +import com.thoughtworks.xstream.converters.reflection.ReflectionConverter; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; -import com.thoughtworks.xstream.security.NullPermission; -import com.thoughtworks.xstream.security.PrimitiveTypePermission; +import com.thoughtworks.xstream.security.NoTypePermission; +import com.thoughtworks.xstream.security.WildcardTypePermission; + +import java.io.Writer; public class XStreamInitializer { + private static final XppDriver XPP_DRIVER = new XppDriver() { + @Override + public HierarchicalStreamWriter createWriter(Writer out) { + return new PrettyPrintWriter(out, getNameCoder()) { + private static final String PREFIX_CDATA = ""; + private static final String PREFIX_MEDIA_ID = ""; + private static final String SUFFIX_MEDIA_ID = ""; - public static XStream getInstance() { - XStream xstream = new XStream(new XppDriver() { + @Override + protected void writeText(QuickWriter writer, String text) { + if (text.startsWith(PREFIX_CDATA) && text.endsWith(SUFFIX_CDATA)) { + writer.write(text); + } else if (text.startsWith(PREFIX_MEDIA_ID) && text.endsWith(SUFFIX_MEDIA_ID)) { + writer.write(text); + } else { + super.writeText(writer, text); + } - @Override - public HierarchicalStreamWriter createWriter(Writer out) { - return new PrettyPrintWriter(out, getNameCoder()) { - protected String PREFIX_CDATA = ""; - protected String PREFIX_MEDIA_ID = ""; - protected String SUFFIX_MEDIA_ID = ""; - - @Override - protected void writeText(QuickWriter writer, String text) { - if (text.startsWith(this.PREFIX_CDATA) && text.endsWith(this.SUFFIX_CDATA)) { - writer.write(text); - } else if (text.startsWith(this.PREFIX_MEDIA_ID) && text.endsWith(this.SUFFIX_MEDIA_ID)) { - writer.write(text); - } else { - super.writeText(writer, text); - } + } - } + @Override + public String encodeNode(String name) { + //防止将_转换成__ + return name; + } + }; + } + }; - @Override - public String encodeNode(String name) { - return name;//防止将_转换成__ - } - }; + public static XStream getInstance() { + XStream xstream = new XStream(new PureJavaReflectionProvider(), XPP_DRIVER) { + // only register the converters we need; other converters generate a private access warning in the console on Java9+... + @Override + protected void setupConverters() { + registerConverter(new NullConverter(), PRIORITY_VERY_HIGH); + registerConverter(new IntConverter(), PRIORITY_NORMAL); + registerConverter(new FloatConverter(), PRIORITY_NORMAL); + registerConverter(new DoubleConverter(), PRIORITY_NORMAL); + registerConverter(new LongConverter(), PRIORITY_NORMAL); + registerConverter(new ShortConverter(), PRIORITY_NORMAL); + registerConverter(new BooleanConverter(), PRIORITY_NORMAL); + registerConverter(new ByteConverter(), PRIORITY_NORMAL); + registerConverter(new StringConverter(), PRIORITY_NORMAL); + registerConverter(new DateConverter(), PRIORITY_NORMAL); + registerConverter(new CollectionConverter(getMapper()), PRIORITY_NORMAL); + registerConverter(new ReflectionConverter(getMapper(), getReflectionProvider()), PRIORITY_VERY_LOW); } - }); + }; xstream.ignoreUnknownElements(); xstream.setMode(XStream.NO_REFERENCES); - xstream.addPermission(NullPermission.NULL); - xstream.addPermission(PrimitiveTypePermission.PRIMITIVES); + XStream.setupDefaultSecurity(xstream); + xstream.autodetectAnnotations(true); + + // setup proper security by limiting which classes can be loaded by XStream + xstream.addPermission(NoTypePermission.NONE); + xstream.addPermission(new WildcardTypePermission(new String[]{ + "me.chanjar.weixin.**", "cn.binarywang.wx.**", "com.github.binarywang.**" + })); + xstream.setClassLoader(Thread.currentThread().getContextClassLoader()); return xstream; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java b/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java deleted file mode 100644 index 7904da202c..0000000000 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java +++ /dev/null @@ -1,139 +0,0 @@ -package me.chanjar.weixin.cp.bean.article; - -/** - *
    - *  Created by BinaryWang on 2017/3/27.
    - * 
    - * @author Binary Wang - */ -public class MpnewsArticle { - private String title; - private String thumbMediaId; - private String author; - private String contentSourceUrl; - private String content; - private String digest; - private String showCoverPic; - - private MpnewsArticle(Builder builder) { - setTitle(builder.title); - setThumbMediaId(builder.thumbMediaId); - setAuthor(builder.author); - setContentSourceUrl(builder.contentSourceUrl); - setContent(builder.content); - setDigest(builder.digest); - setShowCoverPic(builder.showCoverPic); - } - - public static Builder newBuilder() { - return new Builder(); - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getThumbMediaId() { - return thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getContentSourceUrl() { - return contentSourceUrl; - } - - public void setContentSourceUrl(String contentSourceUrl) { - this.contentSourceUrl = contentSourceUrl; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getDigest() { - return digest; - } - - public void setDigest(String digest) { - this.digest = digest; - } - - public String getShowCoverPic() { - return showCoverPic; - } - - public void setShowCoverPic(String showCoverPic) { - this.showCoverPic = showCoverPic; - } - - public static final class Builder { - private String title; - private String thumbMediaId; - private String author; - private String contentSourceUrl; - private String content; - private String digest; - private String showCoverPic; - - private Builder() { - } - - public Builder title(String title) { - this.title = title; - return this; - } - - public Builder thumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - return this; - } - - public Builder author(String author) { - this.author = author; - return this; - } - - public Builder contentSourceUrl(String contentSourceUrl) { - this.contentSourceUrl = contentSourceUrl; - return this; - } - - public Builder content(String content) { - this.content = content; - return this; - } - - public Builder digest(String digest) { - this.digest = digest; - return this; - } - - public Builder showCoverPic(String showCoverPic) { - this.showCoverPic = showCoverPic; - return this; - } - - public MpnewsArticle build() { - return new MpnewsArticle(this); - } - } -} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java b/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java deleted file mode 100644 index d4c056d5ad..0000000000 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java +++ /dev/null @@ -1,49 +0,0 @@ -package me.chanjar.weixin.cp.bean.article; - -/** - *
    - *  Created by BinaryWang on 2017/3/27.
    - * 
    - * - * @author Binary Wang - */ -public class NewArticle { - - private String title; - private String description; - private String url; - private String picUrl; - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getPicUrl() { - return this.picUrl; - } - - public void setPicUrl(String picUrl) { - this.picUrl = picUrl; - } - -} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateCheckerTest.java similarity index 52% rename from weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java rename to weixin-java-common/src/test/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateCheckerTest.java index a3a243a428..fd8819272c 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateCheckerTest.java @@ -1,34 +1,37 @@ -package me.chanjar.weixin.common.util; +package me.chanjar.weixin.common.api; -import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; -import org.testng.Assert; import org.testng.annotations.Test; +import java.util.concurrent.TimeUnit; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + @Test public class WxMessageInMemoryDuplicateCheckerTest { + private WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000L, 1000L); public void test() throws InterruptedException { - Long[] msgIds = new Long[]{1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l}; - WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l); + Long[] msgIds = new Long[]{1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L}; // 第一次检查 for (Long msgId : msgIds) { boolean result = checker.isDuplicate(String.valueOf(msgId)); - Assert.assertFalse(result); + assertFalse(result); } // 过1秒再检查 - Thread.sleep(1000l); + TimeUnit.SECONDS.sleep(1); for (Long msgId : msgIds) { boolean result = checker.isDuplicate(String.valueOf(msgId)); - Assert.assertTrue(result); + assertTrue(result); } // 过1.5秒再检查 - Thread.sleep(1500l); + TimeUnit.MILLISECONDS.sleep(1500L); for (Long msgId : msgIds) { boolean result = checker.isDuplicate(String.valueOf(msgId)); - Assert.assertFalse(result); + assertFalse(result); } } diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxAccessTokenTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxAccessTokenTest.java index b2bc7fe7d1..1f88df29bb 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxAccessTokenTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxAccessTokenTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.common.bean; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.testng.*; +import org.testng.annotations.*; @Test public class WxAccessTokenTest { diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxErrorTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxErrorTest.java deleted file mode 100644 index 3fe40795f3..0000000000 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxErrorTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.chanjar.weixin.common.bean; - -import me.chanjar.weixin.common.bean.result.WxError; -import org.testng.Assert; -import org.testng.annotations.Test; - -@Test -public class WxErrorTest { - - public void testFromJson() { - - String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\" }"; - WxError wxError = WxError.fromJson(json); - Assert.assertTrue(wxError.getErrorCode() == 40003); - Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); - - } - - public void testFromBadJson1() { - - String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }"; - WxError wxError = WxError.fromJson(json); - Assert.assertTrue(wxError.getErrorCode() == 40003); - Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); - - } - - public void testFromBadJson2() { - - String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; - WxError wxError = WxError.fromJson(json); - Assert.assertTrue(wxError.getErrorCode() == 0); - Assert.assertEquals(wxError.getErrorMsg(), null); - - } - -} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java index e2a0c2c1f8..506b2dbaca 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java @@ -1,12 +1,10 @@ package me.chanjar.weixin.common.bean; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.bean.menu.WxMenuButton; import me.chanjar.weixin.common.bean.menu.WxMenuRule; +import org.testng.*; +import org.testng.annotations.*; @Test public class WxMenuTest { @@ -87,75 +85,75 @@ public Object[][] wxReturnMenu() { Object[][] res = menuJson(); String json = "{ \"menu\" : " + res[0][0] + " }"; return new Object[][]{ - new Object[]{json} + new Object[]{json} }; } @DataProvider(name = "wxPushMenu") public Object[][] menuJson() { String json = - "{" - + "\"button\":[" - + "{" - + "\"type\":\"click\"," - + "\"name\":\"今日歌曲\"," - + "\"key\":\"V1001_TODAY_MUSIC\"" - + "}," - + "{" - + "\"type\":\"click\"," - + "\"name\":\"歌手简介\"," - + "\"key\":\"V1001_TODAY_SINGER\"" - + "}," - + "{" - + "\"name\":\"菜单\"," - + "\"sub_button\":[" - + "{" - + "\"type\":\"view\"," - + "\"name\":\"搜索\"," - + "\"url\":\"http://www.soso.com/\"" - + "}," - + "{" - + "\"type\":\"view\"," - + "\"name\":\"视频\"," - + "\"url\":\"http://v.qq.com/\"" - + "}," - + "{" - + "\"type\":\"click\"," - + "\"name\":\"赞一下我们\"," - + "\"key\":\"V1001_GOOD\"" - + "}" - + "]" - + "}" - + "]" - + "}"; + "{" + + "\"button\":[" + + "{" + + "\"type\":\"click\"," + + "\"name\":\"今日歌曲\"," + + "\"key\":\"V1001_TODAY_MUSIC\"" + + "}," + + "{" + + "\"type\":\"click\"," + + "\"name\":\"歌手简介\"," + + "\"key\":\"V1001_TODAY_SINGER\"" + + "}," + + "{" + + "\"name\":\"菜单\"," + + "\"sub_button\":[" + + "{" + + "\"type\":\"view\"," + + "\"name\":\"搜索\"," + + "\"url\":\"http://www.soso.com/\"" + + "}," + + "{" + + "\"type\":\"view\"," + + "\"name\":\"视频\"," + + "\"url\":\"http://v.qq.com/\"" + + "}," + + "{" + + "\"type\":\"click\"," + + "\"name\":\"赞一下我们\"," + + "\"key\":\"V1001_GOOD\"" + + "}" + + "]" + + "}" + + "]" + + "}"; return new Object[][]{ - new Object[]{json} + new Object[]{json} }; } @DataProvider(name = "wxAddConditionalMenu") public Object[][] addConditionalMenuJson() { String json = - "{" - + "\"button\":[" - + "{" - + "\"type\":\"click\"," - + "\"name\":\"今日歌曲\"," - + "\"key\":\"V1001_TODAY_MUSIC\"" - + "}" - + "]," - + "\"matchrule\":{" - + "\"group_id\":\"2\"," - + "\"sex\":\"1\"," - + "\"country\":\"中国\"," - + "\"province\":\"广东\"," - + "\"city\":\"广州\"," - + "\"client_platform_type\":\"2\"," - + "\"language\":\"zh_CN\"" - + "}" - + "}"; + "{" + + "\"button\":[" + + "{" + + "\"type\":\"click\"," + + "\"name\":\"今日歌曲\"," + + "\"key\":\"V1001_TODAY_MUSIC\"" + + "}" + + "]," + + "\"matchrule\":{" + + "\"group_id\":\"2\"," + + "\"sex\":\"1\"," + + "\"country\":\"中国\"," + + "\"province\":\"广东\"," + + "\"city\":\"广州\"," + + "\"client_platform_type\":\"2\"," + + "\"language\":\"zh_CN\"" + + "}" + + "}"; return new Object[][]{ - new Object[]{json} + new Object[]{json} }; } diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxNetCheckResultTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxNetCheckResultTest.java new file mode 100644 index 0000000000..3f08b20bff --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxNetCheckResultTest.java @@ -0,0 +1,58 @@ +package me.chanjar.weixin.common.bean; + +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * + * @author Binary Wang + * @date 2020-06-06 + */ +public class WxNetCheckResultTest { + + @Test + public void testFromJson() { + String json = "{\n" + + " \"dns\": [\n" + + " {\n" + + " \"ip\": \"111.161.64.40\", \n" + + " \"real_operator\": \"UNICOM\"\n" + + " }, \n" + + " {\n" + + " \"ip\": \"111.161.64.48\", \n" + + " \"real_operator\": \"UNICOM\"\n" + + " }\n" + + " ], \n" + + " \"ping\": [\n" + + " {\n" + + " \"ip\": \"111.161.64.40\", \n" + + " \"from_operator\": \"UNICOM\"," + + " \"package_loss\": \"0%\", \n" + + " \"time\": \"23.079ms\"\n" + + " }, \n" + + " {\n" + + " \"ip\": \"111.161.64.48\", \n" + + " \"from_operator\": \"UNICOM\", \n" + + " \"package_loss\": \"0%\", \n" + + " \"time\": \"21.434ms\"\n" + + " }\n" + + " ]\n" + + "}"; + WxNetCheckResult result = WxNetCheckResult.fromJson(json); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getDnsInfos()); + + WxNetCheckResult.WxNetCheckDnsInfo dnsInfo = new WxNetCheckResult.WxNetCheckDnsInfo(); + dnsInfo.setIp("111.161.64.40"); + dnsInfo.setRealOperator("UNICOM"); + Assert.assertEquals(result.getDnsInfos().get(0), dnsInfo); + + WxNetCheckResult.WxNetCheckPingInfo pingInfo = new WxNetCheckResult.WxNetCheckPingInfo(); + pingInfo.setTime("21.434ms"); + pingInfo.setFromOperator("UNICOM"); + pingInfo.setIp("111.161.64.48"); + pingInfo.setPackageLoss("0%"); + Assert.assertEquals(result.getPingInfos().get(1), pingInfo); + + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/error/WxErrorTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/error/WxErrorTest.java new file mode 100644 index 0000000000..456a58ad76 --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/error/WxErrorTest.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.common.error; + +import me.chanjar.weixin.common.enums.WxType; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +@Test +public class WxErrorTest { + public void testFromJson() { + String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\" }"; + WxError wxError = WxError.fromJson(json, WxType.MP); + assertEquals(40003, wxError.getErrorCode()); + assertEquals(wxError.getErrorMsgEn(), "invalid openid"); + + } + + public void testFromBadJson1() { + String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }"; + WxError wxError = WxError.fromJson(json, WxType.MP); + assertEquals(40003, wxError.getErrorCode()); + assertEquals(wxError.getErrorMsgEn(), "invalid openid"); + + } + + public void testFromBadJson2() { + String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; + WxError wxError = WxError.fromJson(json, WxType.MP); + assertEquals(0, wxError.getErrorCode()); + assertNull(wxError.getErrorMsg()); + + } + +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/CommonWxRedisOpsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/CommonWxRedisOpsTest.java new file mode 100644 index 0000000000..96ba20ba2b --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/CommonWxRedisOpsTest.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.common.redis; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.concurrent.TimeUnit; + +public class CommonWxRedisOpsTest { + + protected WxRedisOps wxRedisOps; + private String key = "access_token"; + private String value = String.valueOf(System.currentTimeMillis()); + + @Test + public void testGetValue() { + wxRedisOps.setValue(key, value, 3, TimeUnit.SECONDS); + Assert.assertEquals(wxRedisOps.getValue(key), value); + } + + @Test + public void testSetValue() { + String key = "access_token", value = String.valueOf(System.currentTimeMillis()); + wxRedisOps.setValue(key, value, -1, TimeUnit.SECONDS); + wxRedisOps.setValue(key, value, 0, TimeUnit.SECONDS); + wxRedisOps.setValue(key, value, 1, TimeUnit.SECONDS); + } + + @Test + public void testGetExpire() { + String key = "access_token", value = String.valueOf(System.currentTimeMillis()); + wxRedisOps.setValue(key, value, -1, TimeUnit.SECONDS); + Assert.assertTrue(wxRedisOps.getExpire(key) < 0); + wxRedisOps.setValue(key, value, 4, TimeUnit.SECONDS); + Long expireSeconds = wxRedisOps.getExpire(key); + Assert.assertTrue(expireSeconds <= 4 && expireSeconds >= 0); + } + + @Test + public void testExpire() { + String key = "access_token", value = String.valueOf(System.currentTimeMillis()); + wxRedisOps.setValue(key, value, -1, TimeUnit.SECONDS); + wxRedisOps.expire(key, 4, TimeUnit.SECONDS); + Long expireSeconds = wxRedisOps.getExpire(key); + Assert.assertTrue(expireSeconds <= 4 && expireSeconds >= 0); + } + + @Test + public void testGetLock() { + Assert.assertNotNull(wxRedisOps.getLock("access_token_lock")); + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/JedisWxRedisOpsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/JedisWxRedisOpsTest.java new file mode 100644 index 0000000000..2ff2c37b81 --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/JedisWxRedisOpsTest.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.common.redis; + +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import redis.clients.jedis.JedisPool; + +public class JedisWxRedisOpsTest extends CommonWxRedisOpsTest { + + JedisPool jedisPool; + + @BeforeTest + public void init() { + this.jedisPool = new JedisPool("127.0.0.1", 6379); + this.wxRedisOps = new JedisWxRedisOps(jedisPool); + } + + @AfterTest + public void destroy() { + this.jedisPool.close(); + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOpsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOpsTest.java new file mode 100644 index 0000000000..bf3b35a7cc --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOpsTest.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.common.redis; + +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; + +public class RedisTemplateWxRedisOpsTest extends CommonWxRedisOpsTest { + + StringRedisTemplate redisTemplate; + + @BeforeTest + public void init() { + JedisConnectionFactory connectionFactory = new JedisConnectionFactory(); + connectionFactory.setHostName("127.0.0.1"); + connectionFactory.setPort(6379); + connectionFactory.afterPropertiesSet(); + StringRedisTemplate redisTemplate = new StringRedisTemplate(connectionFactory); + this.redisTemplate = redisTemplate; + this.wxRedisOps = new RedisTemplateWxRedisOps(this.redisTemplate); + } + + @AfterTest + public void destroy() { + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/RedissonWxRedisOpsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/RedissonWxRedisOpsTest.java new file mode 100644 index 0000000000..48cf7b29be --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/RedissonWxRedisOpsTest.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.common.redis; + +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.TransportMode; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; + +public class RedissonWxRedisOpsTest extends CommonWxRedisOpsTest { + + RedissonClient redissonClient; + + @BeforeTest + public void init() { + Config config = new Config(); + config.useSingleServer().setAddress("redis://127.0.0.1:6379"); + config.setTransportMode(TransportMode.NIO); + this.redissonClient = Redisson.create(config); + this.wxRedisOps = new RedissonWxRedisOps(this.redissonClient); + } + + @AfterTest + public void destroy() { + this.redissonClient.shutdown(); + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java index e0248c7960..84d80eab0d 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java @@ -1,8 +1,7 @@ package me.chanjar.weixin.common.session; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.testng.*; +import org.testng.annotations.*; @Test public class SessionTest { @@ -11,7 +10,7 @@ public class SessionTest { public Object[][] getSessionManager() { return new Object[][]{ - new Object[]{new StandardSessionManager()} + new Object[]{new StandardSessionManager()} }; } @@ -81,7 +80,7 @@ public void testBackgroundProcess(WxSessionManager sessionManager) throws Interr InternalSession abc = ism.createSession("abc"); abc.endAccess(); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -100,7 +99,7 @@ public void testBackgroundProcess2(WxSessionManager sessionManager) throws Inter abc.setMaxInactiveInterval(1); abc.endAccess(); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java new file mode 100644 index 0000000000..f5732d9a0b --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/DataUtilsTest.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.common.util; + +import org.testng.annotations.*; + +import static org.testng.Assert.*; + +/** + *
    + *  Created by BinaryWang on 2018/5/8.
    + * 
    + * + * @author Binary Wang + */ +public class DataUtilsTest { + + @Test + public void testHandleDataWithSecret() { + String data = "js_code=001tZveq0SMoiq1AEXeq0ECJeq0tZveZ&secret=5681022fa1643845392367ea88888888&grant_type=authorization_code&appid=wxe156d4848d999999"; + final String s = DataUtils.handleDataWithSecret(data); + assertTrue(s.contains("&secret=******&")); + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/XmlUtilsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/XmlUtilsTest.java new file mode 100644 index 0000000000..7b6bb536f4 --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/XmlUtilsTest.java @@ -0,0 +1,89 @@ +package me.chanjar.weixin.common.util; + +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/11/4.
    + * 
    + * + * @author Binary Wang + */ +public class XmlUtilsTest { + + @Test(expectedExceptions = {RuntimeException.class}) + public void testXml2Map_xxe() { + String xml = "\n" + + "\n" + + "\n" + + "]>\n" + + ""; + XmlUtils.xml2Map(xml); + } + + @Test + public void testXml2Map() { + String xml = "\n" + + "\n" + + "2\n" + + "\n" + + "\n" + + "1\n" + + "0\n" + + "2\n" + + "\n" + + "1\n" + + "1\n" + + "1\n" + + "1\n" + + "\n" + + "\n" + + "2\n" + + "0\n" + + "2\n" + + "\n" + + "1\n" + + "1\n" + + "1\n" + + "1\n" + + "\n" + + "\n" + + "2\n" + + "\n" + + ""; + + final Map map = XmlUtils.xml2Map(xml); + assertThat(map).isNotNull(); + final Map copyrightCheckResult = (Map) map.get("CopyrightCheckResult"); + List> resultList = (List>) ((Map) copyrightCheckResult.get("ResultList")).get("item"); + assertThat(copyrightCheckResult).isNotNull(); + + assertThat(copyrightCheckResult.get("Count")).isEqualTo("2"); + assertThat(copyrightCheckResult.get("CheckState")).isEqualTo("2"); + + assertThat(resultList.get(0).get("ArticleIdx")).isEqualTo("1"); + assertThat(resultList.get(0).get("UserDeclareState")).isEqualTo("0"); + assertThat(resultList.get(0).get("AuditState")).isEqualTo("2"); + assertThat(resultList.get(0).get("OriginalArticleUrl")).isEqualTo("Url_1"); + assertThat(resultList.get(0).get("OriginalArticleType")).isEqualTo("1"); + assertThat(resultList.get(0).get("CanReprint")).isEqualTo("1"); + assertThat(resultList.get(0).get("NeedReplaceContent")).isEqualTo("1"); + assertThat(resultList.get(0).get("NeedShowReprintSource")).isEqualTo("1"); + + assertThat(resultList.get(1).get("ArticleIdx")).isEqualTo("2"); + assertThat(resultList.get(1).get("UserDeclareState")).isEqualTo("0"); + assertThat(resultList.get(1).get("AuditState")).isEqualTo("2"); + assertThat(resultList.get(1).get("OriginalArticleUrl")).isEqualTo("Url_2"); + assertThat(resultList.get(1).get("OriginalArticleType")).isEqualTo("1"); + assertThat(resultList.get(1).get("CanReprint")).isEqualTo("1"); + assertThat(resultList.get(1).get("NeedReplaceContent")).isEqualTo("1"); + assertThat(resultList.get(1).get("NeedShowReprintSource")).isEqualTo("1"); + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/SHA1Test.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/SHA1Test.java new file mode 100644 index 0000000000..ffdb613128 --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/SHA1Test.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.common.util.crypto; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + *
    + *  Created by BinaryWang on 2017/6/10.
    + * 
    + * + * @author Binary Wang + */ +public class SHA1Test { + @Test + public void testGen() throws Exception { + final String result = SHA1.gen("123", "345"); + assertNotNull(result); + assertEquals(result,"9f537aeb751ec72605f57f94a2f6dc3e3958e1dd"); + } + + @Test(expectedExceptions = {IllegalArgumentException.class}) + public void testGen_illegalArguments() { + final String result = SHA1.gen(null, "", "345"); + assertNotNull(result); + } + + @Test + public void testGenWithAmple() throws Exception { + final String result = SHA1.genWithAmple("123", "345"); + assertNotNull(result); + assertEquals(result,"20b896ccbd5a72dde5dbe0878ff985e4069771c6"); + } + +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java index 91095a20e9..82cfa9d2d6 100755 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java @@ -1,20 +1,19 @@ package me.chanjar.weixin.common.util.crypto; -import org.testng.annotations.Test; +import java.io.IOException; +import java.io.StringReader; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.testng.annotations.*; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static org.testng.Assert.*; @Test public class WxCryptUtilTest { @@ -40,6 +39,8 @@ public void testNormal() throws ParserConfigurationException, SAXException, IOEx System.out.println(encryptedXml); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setExpandEntityReferences(false); + documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); @@ -77,11 +78,14 @@ public void testAesEncrypt2() { } public void testValidateSignatureError() throws ParserConfigurationException, SAXException, - IOException { + IOException { try { WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); String afterEncrpt = pc.encrypt(this.replyMsg); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setExpandEntityReferences(false); + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + DocumentBuilder db = dbf.newDocumentBuilder(); StringReader sr = new StringReader(afterEncrpt); InputSource is = new InputSource(sr); diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilderTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilderTest.java new file mode 100644 index 0000000000..24a45eea09 --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilderTest.java @@ -0,0 +1,64 @@ +package me.chanjar.weixin.common.util.http.apache; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class DefaultApacheHttpClientBuilderTest { + @Test + public void testBuild() throws Exception { + DefaultApacheHttpClientBuilder builder1 = DefaultApacheHttpClientBuilder.get(); + DefaultApacheHttpClientBuilder builder2 = DefaultApacheHttpClientBuilder.get(); + Assert.assertSame(builder1, builder2, "DefaultApacheHttpClientBuilder为单例,获取到的对象应该相同"); + List threadList = new ArrayList<>(10); + for (int i = 0; i < 10; i++) { + TestThread thread = new TestThread(); + thread.start(); + threadList.add(thread); + } + for (TestThread testThread : threadList) { + testThread.join(); + Assert.assertNotEquals(-1,testThread.getRespState(),"请求响应code不应为-1"); + } + + for (int i = 1; i < threadList.size(); i++) { + TestThread thread1 = threadList.get(i - 1); + TestThread thread2 = threadList.get(i); + Assert.assertSame( + thread1.getClient(), + thread2.getClient(), + "DefaultApacheHttpClientBuilder为单例,并持有了相同的HttpClient" + ); + } + } + + + public static class TestThread extends Thread { + private CloseableHttpClient client; + private int respState = -1; + + @Override + public void run() { + client = DefaultApacheHttpClientBuilder.get().build(); + HttpGet httpGet = new HttpGet("http://www.sina.com.cn/"); + try (CloseableHttpResponse resp = client.execute(httpGet)){ + respState = resp.getStatusLine().getStatusCode(); + } catch (IOException ignored) { + } + } + + public CloseableHttpClient getClient() { + return client; + } + + public int getRespState() { + return respState; + } + } +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLockTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLockTest.java new file mode 100644 index 0000000000..50a17ed94b --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLockTest.java @@ -0,0 +1,79 @@ +package me.chanjar.weixin.common.util.locks; + +import lombok.SneakyThrows; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.testng.Assert.*; + +@Test(enabled = false) +public class RedisTemplateSimpleDistributedLockTest { + + RedisTemplateSimpleDistributedLock redisLock; + + StringRedisTemplate redisTemplate; + + AtomicInteger lockCurrentExecuteCounter; + + @BeforeTest + public void init() { + JedisConnectionFactory connectionFactory = new JedisConnectionFactory(); + connectionFactory.setHostName("127.0.0.1"); + connectionFactory.setPort(6379); + connectionFactory.afterPropertiesSet(); + StringRedisTemplate redisTemplate = new StringRedisTemplate(connectionFactory); + this.redisTemplate = redisTemplate; + this.redisLock = new RedisTemplateSimpleDistributedLock(redisTemplate, 60000); + this.lockCurrentExecuteCounter = new AtomicInteger(0); + } + + @Test(description = "多线程测试锁排他性") + public void testLockExclusive() throws InterruptedException { + int threadSize = 100; + final CountDownLatch startLatch = new CountDownLatch(threadSize); + final CountDownLatch endLatch = new CountDownLatch(threadSize); + + for (int i = 0; i < threadSize; i++) { + new Thread(new Runnable() { + @SneakyThrows + @Override + public void run() { + startLatch.await(); + + redisLock.lock(); + assertEquals(lockCurrentExecuteCounter.incrementAndGet(), 1, "临界区同时只能有一个线程执行"); + lockCurrentExecuteCounter.decrementAndGet(); + redisLock.unlock(); + + endLatch.countDown(); + } + }).start(); + startLatch.countDown(); + } + endLatch.await(); + } + + @Test + public void testTryLock() throws InterruptedException { + assertTrue(redisLock.tryLock(3, TimeUnit.SECONDS), "第一次加锁应该成功"); + assertNotNull(redisLock.getLockSecretValue()); + String redisValue = this.redisTemplate.opsForValue().get(redisLock.getKey()); + assertEquals(redisValue, redisLock.getLockSecretValue()); + + redisLock.unlock(); + assertNull(redisLock.getLockSecretValue()); + redisValue = this.redisTemplate.opsForValue().get(redisLock.getKey()); + assertNull(redisValue, "释放锁后key会被删除"); + + redisLock.unlock(); + } + + +} + diff --git a/weixin-java-common/src/test/resources/logback-test.xml b/weixin-java-common/src/test/resources/logback-test.xml index 2c421e49ab..9a6fe3eea1 100644 --- a/weixin-java-common/src/test/resources/logback-test.xml +++ b/weixin-java-common/src/test/resources/logback-test.xml @@ -1,17 +1,12 @@ - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n - - + + - diff --git a/weixin-java-common/src/test/resources/testng.xml b/weixin-java-common/src/test/resources/testng.xml index f2222275b2..9eeba0df4c 100644 --- a/weixin-java-common/src/test/resources/testng.xml +++ b/weixin-java-common/src/test/resources/testng.xml @@ -7,8 +7,8 @@ - - + + diff --git a/weixin-java-cp/build.gradle b/weixin-java-cp/build.gradle deleted file mode 100644 index 85d7c4bba2..0000000000 --- a/weixin-java-cp/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ - -description = 'WeiXin Java Tools - CP' -dependencies { - compile project(':weixin-java-common') - testCompile group: 'junit', name: 'junit', version:'4.11' - testCompile group: 'org.testng', name: 'testng', version:'6.8.7' - testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' - testCompile group: 'com.google.inject', name: 'guice', version:'3.0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' -} -test.useTestNG() diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 88f83f0f24..5d9fdae34c 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -1,17 +1,18 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> 4.0.0 com.github.binarywang - weixin-java-parent - 2.6.0 + wx-java + 3.9.0 weixin-java-cp - WeiXin Java Tools - CP - 微信企业号Java SDK + WxJava - CP Java SDK + 微信企业号/企业微信 Java SDK @@ -19,10 +20,29 @@ weixin-java-common ${project.version} + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + redis.clients jedis + + org.slf4j + slf4j-api + + + + org.redisson + redisson + org.testng testng @@ -48,6 +68,21 @@ jetty-servlet test + + ch.qos.logback + logback-classic + test + + + org.projectlombok + lombok + + + + org.assertj + assertj-guava + test + @@ -64,4 +99,36 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + + diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java new file mode 100644 index 0000000000..d57ca56c21 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpAgent; + +import java.util.List; + +/** + *
    + *  管理企业号应用
    + *  文档地址:https://work.weixin.qq.com/api/doc#10087
    + *  Created by huansinho on 2018/4/13.
    + * 
    + * + * @author huansinho + */ +public interface WxCpAgentService { + /** + *
    +   * 获取企业号应用信息
    +   * 该API用于获取企业号某个应用的基本信息,包括头像、昵称、帐号类型、认证类型、可见范围等信息
    +   * 详情请见: https://work.weixin.qq.com/api/doc#10087
    +   * 
    + * + * @param agentId 企业应用的id + * @return 部门id + */ + WxCpAgent get(Integer agentId) throws WxErrorException; + + /** + *
    +   * 设置应用.
    +   * 仅企业可调用,可设置当前凭证对应的应用;第三方不可调用。
    +   * 详情请见: https://work.weixin.qq.com/api/doc#10088
    +   * 
    + * + * @param agentInfo 应用信息 + */ + void set(WxCpAgent agentInfo) throws WxErrorException; + + /** + *
    +   * 获取应用列表.
    +   * 企业仅可获取当前凭证对应的应用;第三方仅可获取被授权的应用。
    +   * 详情请见: https://work.weixin.qq.com/api/doc#11214
    +   * 
    + * + */ + List list() throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java new file mode 100644 index 0000000000..462ec75071 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java @@ -0,0 +1,59 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; +import me.chanjar.weixin.cp.bean.WxCpChat; + +import java.util.List; + +/** + * 群聊服务. + * + * @author gaigeshen + */ +public interface WxCpChatService { + /** + * 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群. + * + * @param name 群聊名,最多50个utf8字符,超过将截断 + * @param owner 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 + * @param users 群成员id列表。至少2人,至多500人 + * @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id + * @return 创建的群聊会话chatId + * @throws WxErrorException 异常 + */ + String create(String name, String owner, List users, String chatId) throws WxErrorException; + + /** + * 修改群聊会话. + * + * @param chatId 群聊id + * @param name 新的群聊名。若不需更新,请忽略此参数(null or empty)。最多50个utf8字符,超过将截断 + * @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) + * @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 + * @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合 + * @throws WxErrorException 异常 + */ + void update(String chatId, String name, String owner, List usersToAdd, List usersToDelete) throws WxErrorException; + + /** + * 获取群聊会话. + * + * @param chatId 群聊编号 + * @return 群聊会话 + * @throws WxErrorException 异常 + */ + WxCpChat get(String chatId) throws WxErrorException; + + /** + * 应用支持推送文本、图片、视频、文件、图文等类型. + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=ACCESS_TOKEN + * 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90248 + * + * @param message 要发送的消息内容对象 + * @throws WxErrorException 异常 + */ + void sendMsg(WxCpAppChatMessage message) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java deleted file mode 100644 index eb1e57cafa..0000000000 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; - -import java.io.File; - -/** - * 微信客户端配置存储 - * - * @author Daniel Qian - */ -public interface WxCpConfigStorage { - - String getAccessToken(); - - boolean isAccessTokenExpired(); - - /** - * 强制将access token过期掉 - */ - void expireAccessToken(); - - void updateAccessToken(WxAccessToken accessToken); - - void updateAccessToken(String accessToken, int expiresIn); - - String getJsapiTicket(); - - boolean isJsapiTicketExpired(); - - /** - * 强制将jsapi ticket过期掉 - */ - void expireJsapiTicket(); - - /** - * 应该是线程安全的 - * - * @param jsapiTicket - */ - void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); - - String getCorpId(); - - String getCorpSecret(); - - Integer getAgentId(); - - String getToken(); - - String getAesKey(); - - long getExpiresTime(); - - String getOauth2redirectUri(); - - String getHttpProxyHost(); - - int getHttpProxyPort(); - - String getHttpProxyUsername(); - - String getHttpProxyPassword(); - - File getTmpDirFile(); - - /** - * http client builder - * - * @return ApacheHttpClientBuilder - */ - ApacheHttpClientBuilder getApacheHttpClientBuilder(); -} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java new file mode 100644 index 0000000000..c86816b7f2 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java @@ -0,0 +1,66 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpDepart; + +import java.util.List; + +/** + *
    + *  部门管理接口
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +public interface WxCpDepartmentService { + + /** + *
    +   * 部门管理接口 - 创建部门.
    +   * 最多支持创建500个部门
    +   * 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90205
    +   * 
    + * + * @param depart 部门 + * @return 部门id + * @throws WxErrorException 异常 + */ + Long create(WxCpDepart depart) throws WxErrorException; + + /** + *
    +   * 部门管理接口 - 获取部门列表.
    +   * 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90208
    +   * 
    + * + * @param id 部门id。获取指定部门及其下的子部门。非必需,可为null + * @return 获取的部门列表 + * @throws WxErrorException 异常 + */ + List list(Long id) throws WxErrorException; + + /** + *
    +   * 部门管理接口 - 更新部门.
    +   * 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90206
    +   * 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误
    +   * 
    + * + * @param group 要更新的group,group的id,name必须设置 + * @throws WxErrorException 异常 + */ + void update(WxCpDepart group) throws WxErrorException; + + /** + *
    +   * 部门管理接口 - 删除部门.
    +   * 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/90207
    +   * 应用须拥有指定部门的管理权限
    +   * 
    + * + * @param departId 部门id + * @throws WxErrorException 异常 + */ + void delete(Long departId) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java new file mode 100644 index 0000000000..a386b0ead2 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java @@ -0,0 +1,359 @@ +package me.chanjar.weixin.cp.api; + +import lombok.NonNull; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.bean.external.*; + +import java.util.Date; +import java.util.List; + +/** + *
    + * 外部联系人管理接口,企业微信的外部联系人的接口和通讯录接口已经拆离
    + *  Created by Joe Cao on 2019/6/14
    + * 
    + * + * @author JoeCao + */ +public interface WxCpExternalContactService { + + /** + * 配置客户联系「联系我」方式 + *
    +   * 企业可以在管理后台-客户联系中配置成员的「联系我」的二维码或者小程序按钮,客户通过扫描二维码或点击小程序上的按钮,即可获取成员联系方式,主动联系到成员。
    +   * 企业可通过此接口为具有客户联系功能的成员生成专属的「联系我」二维码或者「联系我」按钮。
    +   * 如果配置的是「联系我」按钮,需要开发者的小程序接入小程序插件。
    +   *
    +   * 注意:
    +   * 通过API添加的「联系我」不会在管理端进行展示,每个企业可通过API最多配置50万个「联系我」。
    +   * 用户需要妥善存储返回的config_id,config_id丢失可能导致用户无法编辑或删除「联系我」。
    +   * 临时会话模式不占用「联系我」数量,但每日最多添加10万个,并且仅支持单人。
    +   * 临时会话模式的二维码,添加好友完成后该二维码即刻失效。
    +   * 
    + * + * @param info 客户联系「联系我」方式 + * @return wx cp contact way result + * @throws WxErrorException the wx error exception + */ + WxCpContactWayResult addContactWay(@NonNull WxCpContactWayInfo info) throws WxErrorException; + + /** + * 获取企业已配置的「联系我」方式 + * + *
    +   * 批量获取企业配置的「联系我」二维码和「联系我」小程序按钮。
    +   * 
    + * + * @param configId 联系方式的配置id,必填 + * @return contact way + * @throws WxErrorException the wx error exception + */ + WxCpContactWayInfo getContactWay(@NonNull String configId) throws WxErrorException; + + /** + * 更新企业已配置的「联系我」方式 + * + *
    +   * 更新企业配置的「联系我」二维码和「联系我」小程序按钮中的信息,如使用人员和备注等。
    +   * 
    + * + * @param info 客户联系「联系我」方式 + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp updateContactWay(@NonNull WxCpContactWayInfo info) throws WxErrorException; + + /** + * 删除企业已配置的「联系我」方式 + * + *
    +   * 删除一个已配置的「联系我」二维码或者「联系我」小程序按钮。
    +   * 
    + * + * @param configId 企业联系方式的配置id,必填 + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp deleteContactWay(@NonNull String configId) throws WxErrorException; + + /** + * 结束临时会话 + * + *
    +   * 将指定的企业成员和客户之前的临时会话断开,断开前会自动下发已配置的结束语。
    +   *
    +   * 注意:请保证传入的企业成员和客户之间有仍然有效的临时会话, 通过其他方式的添加外部联系人无法通过此接口关闭会话。
    +   * 
    + * + * @param userId the user id + * @param externalUserId the external user id + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp closeTempChat(@NonNull String userId, @NonNull String externalUserId) throws WxErrorException; + + + /** + * 获取外部联系人详情. + *
    +   *   企业可通过此接口,根据外部联系人的userid,拉取外部联系人详情。权限说明:
    +   * 企业需要使用外部联系人管理secret所获取的accesstoken来调用
    +   * 第三方应用需拥有“企业客户”权限。
    +   * 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。
    +   * 
    + * + * @param userId 外部联系人的userid + * @return . external contact + * @throws WxErrorException the wx error exception + * @deprecated 建议使用 {@link #getContactDetail(String)} + */ + @Deprecated + WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; + + /** + * 获取客户详情. + *
    +   *
    +   * 企业可通过此接口,根据外部联系人的userid(如何获取?),拉取客户详情。
    +   *
    +   * 请求方式:GET(HTTPS)
    +   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN&external_userid=EXTERNAL_USERID
    +   *
    +   * 权限说明:
    +   *
    +   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?);
    +   * 第三方/自建应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。
    +   * 
    + * + * @param userId 外部联系人的userid,注意不是企业成员的帐号 + * @return . contact detail + * @throws WxErrorException . + */ + WxCpUserExternalContactInfo getContactDetail(String userId) throws WxErrorException; + + /** + * 获取客户列表. + *
    +   *   企业可通过此接口获取指定成员添加的客户列表。客户是指配置了客户联系功能的成员所添加的外部联系人。没有配置客户联系功能的成员,所添加的外部联系人将不会作为客户返回。
    +   *
    +   * 请求方式:GET(HTTPS)
    +   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list?access_token=ACCESS_TOKEN&userid=USERID
    +   *
    +   * 权限说明:
    +   *
    +   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?);
    +   * 第三方应用需拥有“企业客户”权限。
    +   * 第三方/自建应用只能获取到可见范围内的配置了客户联系功能的成员。
    +   * 
    + * + * @param userId 企业成员的userid + * @return List of External wx id + * @throws WxErrorException . + */ + List listExternalContacts(String userId) throws WxErrorException; + + /** + * 企业和第三方服务商可通过此接口获取配置了客户联系功能的成员(Customer Contact)列表。 + *
    +   *   企业需要使用外部联系人管理secret所获取的accesstoken来调用(accesstoken如何获取?);
    +   *   第三方应用需拥有“企业客户”权限。
    +   *   第三方应用只能获取到可见范围内的配置了客户联系功能的成员
    +   * 
    + * + * @return List of CpUser id + * @throws WxErrorException . + */ + List listFollowers() throws WxErrorException; + + /** + * 企业和第三方可通过此接口,获取所有离职成员的客户列表,并可进一步调用离职成员的外部联系人再分配接口将这些客户重新分配给其他企业成员。 + * + * @param page the page + * @param pageSize the page size + * @return wx cp user external unassign list + * @throws WxErrorException the wx error exception + */ + WxCpUserExternalUnassignList listUnassignedList(Integer page, Integer pageSize) throws WxErrorException; + + /** + * 企业可通过此接口,将已离职成员的外部联系人分配给另一个成员接替联系。 + * + * @param externalUserid the external userid + * @param handOverUserid the hand over userid + * @param takeOverUserid the take over userid + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp transferExternalContact(String externalUserid, String handOverUserid, String takeOverUserid) throws WxErrorException; + + /** + *
    +   * 该接口用于获取配置过客户群管理的客户群列表。
    +   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。
    +   * 暂不支持第三方调用。
    +   * 微信文档:https://work.weixin.qq.com/api/doc/90000/90135/92119
    +   * 
    + * + * @param pageIndex the page index + * @param pageSize the page size + * @param status the status + * @param userIds the user ids + * @param partyIds the party ids + * @return the wx cp user external group chat list + * @throws WxErrorException the wx error exception + */ + WxCpUserExternalGroupChatList listGroupChat(Integer pageIndex, Integer pageSize, int status, String[] userIds, String[] partyIds) throws WxErrorException; + + /** + *
    +   * 通过客户群ID,获取详情。包括群名、群成员列表、群成员入群时间、入群方式。(客户群是由具有客户群使用权限的成员创建的外部群)
    +   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。
    +   * 暂不支持第三方调用。
    +   * 微信文档:https://work.weixin.qq.com/api/doc/90000/90135/92122
    +   * 
    + * + * @param chatId the chat id + * @return group chat + * @throws WxErrorException the wx error exception + */ + WxCpUserExternalGroupChatInfo getGroupChat(String chatId) throws WxErrorException; + + /** + *
    +   * 企业可通过此接口获取成员联系客户的数据,包括发起申请数、新增客户数、聊天数、发送消息数和删除/拉黑成员的客户数等指标。
    +   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。
    +   * 第三方应用需拥有“企业客户”权限。
    +   * 第三方/自建应用调用时传入的userid和partyid要在应用的可见范围内;
    +   * 
    + * + * @param startTime the start time + * @param endTime the end time + * @param userIds the user ids + * @param partyIds the party ids + * @return user behavior statistic + * @throws WxErrorException the wx error exception + */ + WxCpUserExternalUserBehaviorStatistic getUserBehaviorStatistic(Date startTime, Date endTime, String[] userIds, String[] partyIds) throws WxErrorException; + + /** + *
    +   * 获取指定日期全天的统计数据。注意,企业微信仅存储60天的数据。
    +   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。
    +   * 暂不支持第三方调用。
    +   * 
    + * + * @param startTime the start time + * @param orderBy the order by + * @param orderAsc the order asc + * @param pageIndex the page index + * @param pageSize the page size + * @param userIds the user ids + * @param partyIds the party ids + * @return group chat statistic + * @throws WxErrorException the wx error exception + */ + WxCpUserExternalGroupChatStatistic getGroupChatStatistic(Date startTime, Integer orderBy, Integer orderAsc, Integer pageIndex, Integer pageSize, String[] userIds, String[] partyIds) throws WxErrorException; + + /** + * 添加企业群发消息任务 + * 企业可通过此接口添加企业群发消息的任务并通知客服人员发送给相关客户或客户群。(注:企业微信终端需升级到2.7.5版本及以上) + * 注意:调用该接口并不会直接发送消息给客户/客户群,需要相关的客服人员操作以后才会实际发送(客服人员的企业微信需要升级到2.7.5及以上版本) + * 同一个企业每个自然月内仅可针对一个客户/客户群发送4条消息,超过限制的用户将会被忽略。 + *

    + * 请求方式: POST(HTTP) + *

    + * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_msg_template?access_token=ACCESS_TOKEN + *

    + * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/92135 + * + * @param wxCpMsgTemplate the wx cp msg template + * @return the wx cp msg template add result + * @throws WxErrorException the wx error exception + */ + WxCpMsgTemplateAddResult addMsgTemplate(WxCpMsgTemplate wxCpMsgTemplate) throws WxErrorException; + + /** + * 发送新客户欢迎语 + *

    +   * 企业微信在向企业推送添加外部联系人事件时,会额外返回一个welcome_code,企业以此为凭据调用接口,即可通过成员向新添加的客户发送个性化的欢迎语。
    +   * 为了保证用户体验以及避免滥用,企业仅可在收到相关事件后20秒内调用,且只可调用一次。
    +   * 如果企业已经在管理端为相关成员配置了可用的欢迎语,则推送添加外部联系人事件时不会返回welcome_code。
    +   * 每次添加新客户时可能有多个企业自建应用/第三方应用收到带有welcome_code的回调事件,但仅有最先调用的可以发送成功。后续调用将返回41051(externaluser has started chatting)错误,请用户根据实际使用需求,合理设置应用可见范围,避免冲突。
    +   * 请求方式: POST(HTTP)
    +   *
    +   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/send_welcome_msg?access_token=ACCESS_TOKEN
    +   *
    +   * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/92137
    +   * 
    + * + * @param msg . + * @throws WxErrorException . + */ + void sendWelcomeMsg(WxCpWelcomeMsg msg) throws WxErrorException; + + /** + *
    +   * 企业可通过此接口获取企业客户标签详情。
    +   * 
    + * + * @param tagId the tag id + * @return corp tag list + * @throws WxErrorException the wx error exception + */ + WxCpUserExternalTagGroupList getCorpTagList(String[] tagId) throws WxErrorException; + + /** + *
    +   * 企业可通过此接口向客户标签库中添加新的标签组和标签,每个企业最多可配置3000个企业标签。
    +   * 暂不支持第三方调用。
    +   * 
    + * + * @param tagGroup the tag group + * @return wx cp user external tag group info + * @throws WxErrorException the wx error exception + */ + WxCpUserExternalTagGroupInfo addCorpTag(WxCpUserExternalTagGroupInfo tagGroup) throws WxErrorException; + + /** + *
    +   * 企业可通过此接口编辑客户标签/标签组的名称或次序值。
    +   * 暂不支持第三方调用。
    +   * 
    + * + * @param id the id + * @param name the name + * @param order the order + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp editCorpTag(String id, String name, Integer order) throws WxErrorException; + + /** + *
    +   * 企业可通过此接口删除客户标签库中的标签,或删除整个标签组。
    +   * 暂不支持第三方调用。
    +   * 
    + * + * @param tagId the tag id + * @param groupId the group id + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp delCorpTag(String[] tagId, String[] groupId) throws WxErrorException; + + /** + *
    +   * 企业可通过此接口为指定成员的客户添加上由企业统一配置的标签。
    +   * https://work.weixin.qq.com/api/doc/90000/90135/92117
    +   * 
    + * + * @param userid the userid + * @param externalUserid the external userid + * @param addTag the add tag + * @param removeTag the remove tag + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp markTag(String userid, String externalUserid, String[] addTag, String[] removeTag) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java new file mode 100644 index 0000000000..007dff78fc --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.article.NewArticle; + +import java.util.List; + +/** + * 微信群机器人消息发送api + * 文档地址:https://work.weixin.qq.com/help?doc_id=13376 + * 调用地址:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key= + * + * @author yr + * @date 2020-8-20 + */ +public interface WxCpGroupRobotService { + + /** + * 发送text类型的消息 + * + * @param content 文本内容,最长不超过2048个字节,必须是utf8编码 + * @param mentionedList userId的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userId,可以使用mentioned_mobile_list + * @param mobileList 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人 + * @throws WxErrorException 异常 + */ + void sendText(String content, List mentionedList, List mobileList) throws WxErrorException; + + /** + * 发送markdown类型的消息 + * + * @param content markdown内容,最长不超过4096个字节,必须是utf8编码 + * @throws WxErrorException 异常 + */ + void sendMarkDown(String content) throws WxErrorException; + + /** + * 发送image类型的消息 + * + * @param base64 图片内容的base64编码 + * @param md5 图片内容(base64编码前)的md5值 + * @throws WxErrorException 异常 + */ + void sendImage(String base64, String md5) throws WxErrorException; + + /** + * 发送news类型的消息 + * + * @param articleList 图文消息,支持1到8条图文 + * @throws WxErrorException 异常 + */ + void sendNews(List articleList) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java deleted file mode 100644 index 867dd6bb68..0000000000 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java +++ /dev/null @@ -1,224 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; - -import java.io.File; - -/** - * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 - * - * @author Daniel Qian - */ -public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { - - protected volatile String corpId; - protected volatile String corpSecret; - - protected volatile String token; - protected volatile String accessToken; - protected volatile String aesKey; - protected volatile Integer agentId; - protected volatile long expiresTime; - - protected volatile String oauth2redirectUri; - - protected volatile String httpProxyHost; - protected volatile int httpProxyPort; - protected volatile String httpProxyUsername; - protected volatile String httpProxyPassword; - - protected volatile String jsapiTicket; - protected volatile long jsapiTicketExpiresTime; - - protected volatile File tmpDirFile; - - private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; - - @Override - public String getAccessToken() { - return this.accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - @Override - public boolean isAccessTokenExpired() { - return System.currentTimeMillis() > this.expiresTime; - } - - @Override - public void expireAccessToken() { - this.expiresTime = 0; - } - - @Override - public synchronized void updateAccessToken(WxAccessToken accessToken) { - updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); - } - - @Override - public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { - this.accessToken = accessToken; - this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; - } - - @Override - public String getJsapiTicket() { - return this.jsapiTicket; - } - - public void setJsapiTicket(String jsapiTicket) { - this.jsapiTicket = jsapiTicket; - } - - public long getJsapiTicketExpiresTime() { - return this.jsapiTicketExpiresTime; - } - - public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) { - this.jsapiTicketExpiresTime = jsapiTicketExpiresTime; - } - - @Override - public boolean isJsapiTicketExpired() { - return System.currentTimeMillis() > this.jsapiTicketExpiresTime; - } - - @Override - public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { - this.jsapiTicket = jsapiTicket; - // 预留200秒的时间 - this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; - } - - @Override - public void expireJsapiTicket() { - this.jsapiTicketExpiresTime = 0; - } - - @Override - public String getCorpId() { - return this.corpId; - } - - public void setCorpId(String corpId) { - this.corpId = corpId; - } - - @Override - public String getCorpSecret() { - return this.corpSecret; - } - - public void setCorpSecret(String corpSecret) { - this.corpSecret = corpSecret; - } - - @Override - public String getToken() { - return this.token; - } - - public void setToken(String token) { - this.token = token; - } - - @Override - public long getExpiresTime() { - return this.expiresTime; - } - - public void setExpiresTime(long expiresTime) { - this.expiresTime = expiresTime; - } - - @Override - public String getAesKey() { - return this.aesKey; - } - - public void setAesKey(String aesKey) { - this.aesKey = aesKey; - } - - @Override - public Integer getAgentId() { - return this.agentId; - } - - public void setAgentId(Integer agentId) { - this.agentId = agentId; - } - - @Override - public String getOauth2redirectUri() { - return this.oauth2redirectUri; - } - - public void setOauth2redirectUri(String oauth2redirectUri) { - this.oauth2redirectUri = oauth2redirectUri; - } - - @Override - public String getHttpProxyHost() { - return this.httpProxyHost; - } - - public void setHttpProxyHost(String httpProxyHost) { - this.httpProxyHost = httpProxyHost; - } - - @Override - public int getHttpProxyPort() { - return this.httpProxyPort; - } - - public void setHttpProxyPort(int httpProxyPort) { - this.httpProxyPort = httpProxyPort; - } - - @Override - public String getHttpProxyUsername() { - return this.httpProxyUsername; - } - - public void setHttpProxyUsername(String httpProxyUsername) { - this.httpProxyUsername = httpProxyUsername; - } - - @Override - public String getHttpProxyPassword() { - return this.httpProxyPassword; - } - - public void setHttpProxyPassword(String httpProxyPassword) { - this.httpProxyPassword = httpProxyPassword; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - @Override - public File getTmpDirFile() { - return this.tmpDirFile; - } - - public void setTmpDirFile(File tmpDirFile) { - this.tmpDirFile = tmpDirFile; - } - - @Override - public ApacheHttpClientBuilder getApacheHttpClientBuilder() { - return this.apacheHttpClientBuilder; - } - - public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { - this.apacheHttpClientBuilder = apacheHttpClientBuilder; - } -} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java new file mode 100644 index 0000000000..a51e04e175 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java @@ -0,0 +1,87 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + *
    + *  媒体管理接口.
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +public interface WxCpMediaService { + + /** + *
    +   * 上传多媒体文件.
    +   * 上传的多媒体文件有格式和大小限制,如下:
    +   *   图片(image): 1M,支持JPG格式
    +   *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
    +   *   视频(video):10MB,支持MP4格式
    +   *   缩略图(thumb):64KB,支持JPG格式
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
    +   * 
    + * + * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param inputStream 输入流,需要调用方控制关闭该输入流 + */ + WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) + throws WxErrorException, IOException; + + /** + * 上传多媒体文件. + * + * @param mediaType 媒体类型 + * @param file 文件对象 + * @see #upload(String, String, InputStream) + */ + WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException; + + /** + *
    +   * 下载多媒体文件.
    +   * 根据微信文档,视频文件下载不了,会返回null
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
    +   * 
    + * + * @param mediaId 媒体id + * @return 保存到本地的临时文件 + */ + File download(String mediaId) throws WxErrorException; + + /** + *
    +   * 获取高清语音素材.
    +   * 可以使用本接口获取从JSSDK的uploadVoice接口上传的临时语音素材,格式为speex,16K采样率。该音频比上文的临时素材获取接口(格式为amr,8K采样率)更加清晰,适合用作语音识别等对音质要求较高的业务。
    +   * 请求方式:GET(HTTPS)
    +   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
    +   * 仅企业微信2.4及以上版本支持。
    +   * 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90255
    +   * 
    + * + * @param mediaId 媒体id + * @return 保存到本地的临时文件 + */ + File getJssdkFile(String mediaId) throws WxErrorException; + + /** + *
    +   * 上传图片.
    +   * 上传图片得到图片URL,该URL永久有效
    +   * 返回的图片URL,仅能用于图文消息(mpnews)正文中的图片展示;若用于非企业微信域名下的页面,图片将被屏蔽。
    +   * 每个企业每天最多可上传100张图片
    +   * 接口url格式:https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param file 上传的文件对象 + * @return 返回图片url + */ + String uploadImg(File file) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java new file mode 100644 index 0000000000..309b981211 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java @@ -0,0 +1,92 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + *
    + *  菜单管理相关接口
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +public interface WxCpMenuService { + + /** + *
    +   * 自定义菜单创建接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
    +   *
    +   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 
    + * + * @param menu 菜单对象 + * @see #create(Integer, WxMenu) + */ + void create(WxMenu menu) throws WxErrorException; + + /** + *
    +   * 自定义菜单创建接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
    +   *
    +   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    +   * 
    + * + * @param agentId 企业号应用的id + * @param menu 菜单对象 + * @see #create(me.chanjar.weixin.common.bean.menu.WxMenu) + */ + void create(Integer agentId, WxMenu menu) throws WxErrorException; + + /** + *
    +   * 自定义菜单删除接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
    +   *
    +   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 
    + * + * @see #delete(Integer) + */ + void delete() throws WxErrorException; + + /** + *
    +   * 自定义菜单删除接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
    +   *
    +   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    +   * 
    + * + * @param agentId 企业号应用的id + * @see #delete() + */ + void delete(Integer agentId) throws WxErrorException; + + /** + *
    +   * 自定义菜单查询接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
    +   *
    +   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 
    + * + * @see #get(Integer) + */ + WxMenu get() throws WxErrorException; + + /** + *
    +   * 自定义菜单查询接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
    +   *
    +   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    +   * 
    + * + * @param agentId 企业号应用的id + * @see #get() + */ + WxMenu get(Integer agentId) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java new file mode 100644 index 0000000000..7c42ea63fc --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java @@ -0,0 +1,104 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; +import me.chanjar.weixin.cp.bean.WxCpUserDetail; + +/** + *
    + * OAuth2相关管理接口.
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +public interface WxCpOAuth2Service { + + /** + *
    +   * 构造oauth2授权的url连接.
    +   * 
    + * + * @param state 状态码 + * @return url + */ + String buildAuthorizationUrl(String state); + + /** + *
    +   * 构造oauth2授权的url连接.
    +   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code
    +   * 
    + * + * @param redirectUri 跳转链接地址 + * @param state 状态码 + * @return url + */ + String buildAuthorizationUrl(String redirectUri, String state); + + /** + *
    +   * 构造oauth2授权的url连接
    +   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code
    +   * 
    + * + * @param redirectUri 跳转链接地址 + * @param state 状态码 + * @param scope 取值参考me.chanjar.weixin.common.api.WxConsts.OAuth2Scope类 + * @return url + */ + String buildAuthorizationUrl(String redirectUri, String state, String scope); + + /** + *
    +   * 用oauth2获取用户信息
    +   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
    +   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
    +   *
    +   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 
    + * + * @param code 微信oauth授权返回的代码 + * @return WxCpOauth2UserInfo + * @throws WxErrorException 异常 + * @see #getUserInfo(Integer, String) + */ + WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException; + + /** + *
    +   * 根据code获取成员信息
    +   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
    +   * https://work.weixin.qq.com/api/doc#10028/根据code获取成员信息
    +   * https://work.weixin.qq.com/api/doc#90000/90135/91023  获取访问用户身份
    +   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
    +   *
    +   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    +   * 
    + * + * @param agentId 企业号应用的id + * @param code 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 + * @return WxCpOauth2UserInfo + * @throws WxErrorException 异常 + * @see #getUserInfo(String) + */ + WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErrorException; + + /** + *
    +   * 使用user_ticket获取成员详情.
    +   *
    +   * 文档地址:https://work.weixin.qq.com/api/doc#10028/%E4%BD%BF%E7%94%A8user_ticket%E8%8E%B7%E5%8F%96%E6%88%90%E5%91%98%E8%AF%A6%E6%83%85
    +   * 请求方式:POST(HTTPS)
    +   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail?access_token=ACCESS_TOKEN
    +   *
    +   * 权限说明:
    +   * 需要有对应应用的使用权限,且成员必须在授权应用的可见范围内。
    +   * 
    + * + * @param userTicket 成员票据 + * @return WxCpUserDetail + * @throws WxErrorException 异常 + */ + WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java new file mode 100644 index 0000000000..1e9c6dd5e9 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java @@ -0,0 +1,135 @@ +package me.chanjar.weixin.cp.api; + +import lombok.NonNull; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.oa.*; + +import java.util.Date; +import java.util.List; + +/** + * 企业微信OA相关接口. + * + * @author Element + * @date 2019-04-06 10:52 + */ +public interface WxCpOaService { + + /** + *
    提交审批申请
    +   * 调试工具
    +   * 企业可通过审批应用或自建应用Secret调用本接口,代应用可见范围内员工在企业微信“审批应用”内提交指定类型的审批申请。
    +   *
    +   * 请求方式:POST(HTTPS)
    +   * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent?access_token=ACCESS_TOKEN
    +   * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/91853
    +   * 
    + * + * @param request 请求 + * @return 表单提交成功后,返回的表单编号 + * @throws WxErrorException . + */ + String apply(WxCpOaApplyEventRequest request) throws WxErrorException; + + /** + *
    +   *  获取打卡数据
    +   *  API doc : https://work.weixin.qq.com/api/doc#90000/90135/90262
    +   * 
    + * + * @param openCheckinDataType 打卡类型。1:上下班打卡;2:外出打卡;3:全部打卡 + * @param startTime 获取打卡记录的开始时间 + * @param endTime 获取打卡记录的结束时间 + * @param userIdList 需要获取打卡记录的用户列表 + * @return 打卡数据列表 + * @throws WxErrorException 异常 + */ + List getCheckinData(Integer openCheckinDataType, Date startTime, Date endTime, + List userIdList) throws WxErrorException; + + /** + *
    +   *   获取打卡规则
    +   *   API doc : https://work.weixin.qq.com/api/doc#90000/90135/90263
    +   * 
    + * + * @param datetime 需要获取规则的当天日期 + * @param userIdList 需要获取打卡规则的用户列表 + * @return 打卡规则列表 + * @throws WxErrorException . + */ + List getCheckinOption(Date datetime, List userIdList) throws WxErrorException; + + /** + *
    +   *
    +   * 批量获取审批单号
    +   *
    +   * 审批应用及有权限的自建应用,可通过Secret调用本接口,以获取企业一段时间内企业微信“审批应用”单据的审批编号,支持按模板类型、申请人、部门、申请单审批状态等条件筛选。
    +   * 自建应用调用此接口,需在“管理后台-应用管理-审批-API-审批数据权限”中,授权应用允许提交审批单据。
    +   *
    +   * 一次拉取调用最多拉取100个审批记录,可以通过多次拉取的方式来满足需求,但调用频率不可超过600次/分。
    +   *
    +   * API doc : https://work.weixin.qq.com/api/doc/90000/90135/91816
    +   * 
    + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param cursor 分页查询游标,默认为0,后续使用返回的next_cursor进行分页拉取 + * @param size 一次请求拉取审批单数量,默认值为100,上限值为100 + * @param filters 筛选条件,可对批量拉取的审批申请设置约束条件,支持设置多个条件,nullable + * @return WxCpApprovalInfo + * @throws WxErrorException . + */ + WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime, Integer cursor, Integer size, + List filters) throws WxErrorException; + + /** + * short method + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return WxCpApprovalInfo + * @throws WxErrorException . + * @see me.chanjar.weixin.cp.api.WxCpOaService#getApprovalInfo + */ + WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime) throws WxErrorException; + + /** + *
    +   *   获取审批申请详情
    +   *
    +   *   企业可通过审批应用或自建应用Secret调用本接口,根据审批单号查询企业微信“审批应用”的审批申请详情。
    +   *
    +   *   API Doc : https://work.weixin.qq.com/api/doc/90000/90135/91983
    +   * 
    + * + * @param spNo 审批单编号。 + * @return WxCpApprovaldetail + * @throws WxErrorException . + */ + WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo) throws WxErrorException; + + /** + * 获取公费电话拨打记录 + * + * @param startTime 查询的起始时间戳 + * @param endTime 查询的结束时间戳 + * @param offset 分页查询的偏移量 + * @param limit 分页查询的每页大小,默认为100条,如该参数大于100则按100处理 + * @return . + * @throws WxErrorException . + */ + List getDialRecord(Date startTime, Date endTime, Integer offset, + Integer limit) throws WxErrorException; + + /** + * 获取审批模板详情 + * + * @param templateId 模板ID + * @return . + * @throws WxErrorException . + */ + WxCpTemplateResult getTemplateDetail(@NonNull String templateId) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java index bd0b322f72..036265815b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java @@ -1,27 +1,24 @@ package me.chanjar.weixin.cp.api; import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.menu.WxMenu; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSession; import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.cp.bean.WxCpDepart; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; import me.chanjar.weixin.cp.bean.WxCpMessage; -import me.chanjar.weixin.cp.bean.WxCpTag; -import me.chanjar.weixin.cp.bean.WxCpUser; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; +import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; +import me.chanjar.weixin.cp.bean.WxCpProviderToken; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; /** - * 微信API的Service + * 微信API的Service. + * + * @author chanjaster */ public interface WxCpService { - /** *
        * 验证推送过来的消息的正确性
    @@ -35,16 +32,6 @@ public interface WxCpService {
        */
       boolean checkSignature(String msgSignature, String timestamp, String nonce, String data);
     
    -  /**
    -   * 
    -   *   用在二次验证的时候
    -   *   企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。
    -   * 
    - * - * @param userId 用户id - */ - void userAuthenticated(String userId) throws WxErrorException; - /** * 获取access_token, 不强制刷新access_token * @@ -85,469 +72,291 @@ public interface WxCpService { String getJsapiTicket(boolean forceRefresh) throws WxErrorException; /** - *
    -   * 创建调用jsapi时所需要的签名
    -   *
    -   * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
    -   * 
    + * 获得jsapi_ticket,不强制刷新jsapi_ticket + * 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别: + *

    + * 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。 + * 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。 * - * @param url url + * @see #getJsapiTicket(boolean) */ - WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; + String getAgentJsapiTicket() throws WxErrorException; /** *

    -   * 上传多媒体文件
    -   * 上传的多媒体文件有格式和大小限制,如下:
    -   *   图片(image): 1M,支持JPG格式
    -   *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
    -   *   视频(video):10MB,支持MP4格式
    -   *   缩略图(thumb):64KB,支持JPG格式
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
    -   * 
    + * 获取应用的jsapi_ticket + * 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别: * - * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param inputStream 输入流 - */ - WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) - throws WxErrorException, IOException; - - /** - * @param mediaType 媒体类型 - * @param file 文件对象 - * @see #mediaUpload(String, String, InputStream) - */ - WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; - - /** - *
    -   * 下载多媒体文件
    -   * 根据微信文档,视频文件下载不了,会返回null
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
    -   * 
    + * 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。 + * 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。 * - * @param mediaId 媒体id - * @return 保存到本地的临时文件 - */ - File mediaDownload(String mediaId) throws WxErrorException; - - /** - *
    -   * 发送消息
    -   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
    -   * 
    - * - * @param message 要发送的消息对象 - */ - void messageSend(WxCpMessage message) throws WxErrorException; - - /** - *
    -   * 自定义菜单创建接口
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
    +   * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
        *
    -   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 详情请见:https://work.weixin.qq.com/api/doc#10029/%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E7%9A%84jsapi_ticket
        * 
    * - * @param menu 菜单对象 - * @see #menuCreate(Integer, WxMenu) + * @param forceRefresh 强制刷新 */ - void menuCreate(WxMenu menu) throws WxErrorException; + String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException; /** *
    -   * 自定义菜单创建接口
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
    +   * 创建调用jsapi时所需要的签名
        *
    -   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    +   * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
        * 
    * - * @param agentId 企业号应用的id - * @param menu 菜单对象 - * @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) + * @param url url */ - void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; + WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; /** *
    -   * 自定义菜单删除接口
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
    -   *
    -   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 发送消息
    +   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
        * 
    * - * @see #menuDelete(Integer) + * @param message 要发送的消息对象 */ - void menuDelete() throws WxErrorException; + WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException; /** - *
    -   * 自定义菜单删除接口
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
    -   *
    -   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    -   * 
    + * 小程序登录凭证校验 * - * @param agentId 企业号应用的id - * @see #menuDelete() + * @param jsCode 登录时获取的 code */ - void menuDelete(Integer agentId) throws WxErrorException; + WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException; /** *
    -   * 自定义菜单查询接口
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
    -   *
    -   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 获取微信服务器的ip段
    +   * http://qydev.weixin.qq.com/wiki/index.php?title=回调模式#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5
        * 
    * - * @see #menuGet(Integer) + * @return { "ip_list": ["101.226.103.*", "101.226.62.*"] } */ - WxMenu menuGet() throws WxErrorException; + String[] getCallbackIp() throws WxErrorException; /** *
    -   * 自定义菜单查询接口
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
    -   *
    -   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    +   * 获取服务商凭证
    +   * 文档地址:https://work.weixin.qq.com/api/doc#90001/90143/91200
    +   * 请求方式:POST(HTTPS)
    +   * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token
        * 
    * - * @param agentId 企业号应用的id - * @see #menuGet() + * @param corpId 服务商的corpid + * @param providerSecret 服务商的secret,在服务商管理后台可见 + * @return { + * "errcode":0 , + * "errmsg":"ok" , + * "provider_access_token":"enLSZ5xxxxxxJRL", + * "expires_in":7200 + * } + * @throws WxErrorException . */ - WxMenu menuGet(Integer agentId) throws WxErrorException; + WxCpProviderToken getProviderToken(String corpId, String providerSecret) throws WxErrorException; /** - *
    -   * 部门管理接口 - 创建部门
    -   * 最多支持创建500个部门
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
    -   * 
    + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 * - * @param depart 部门 - * @return 部门id + * @param url 接口地址 + * @param queryParam 请求参数 */ - Integer departCreate(WxCpDepart depart) throws WxErrorException; + String get(String url, String queryParam) throws WxErrorException; /** - *
    -   * 部门管理接口 - 查询所有部门
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
    -   * 
    + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 + * + * @param url 接口地址 + * @param postData 请求body字符串 */ - List departGet() throws WxErrorException; + String post(String url, String postData) throws WxErrorException; /** - *
    -   * 部门管理接口 - 修改部门名
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
    -   * 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误
    -   * 
    + * 当不需要自动带accessToken的时候,可以用这个发起post请求 * - * @param group 要更新的group,group的id,name必须设置 + * @param url 接口地址 + * @param postData 请求body字符串 */ - void departUpdate(WxCpDepart group) throws WxErrorException; + String postWithoutToken(String url, String postData) throws WxErrorException; /** *
    -   * 部门管理接口 - 删除部门
    +   * Service没有实现某个API的时候,可以用这个,
    +   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
    +   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
        * 
    * - * @param departId 部门id + * @param executor 执行器 + * @param uri 请求地址 + * @param data 参数 + * @param 请求值类型 + * @param 返回值类型 */ - void departDelete(Integer departId) throws WxErrorException; + T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; /** *
    -   * 获取部门成员(详情)
    -   *
    -   * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29
    +   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
    +   * 默认:1000ms
        * 
    * - * @param departId 必填。部门id - * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 - * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + * @param retrySleepMillis 重试休息时间 */ - List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + void setRetrySleepMillis(int retrySleepMillis); /** *
    -   * 获取部门成员
    -   *
    -   * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98
    +   * 设置当微信系统响应系统繁忙时,最大重试次数
    +   * 默认:5次
        * 
    * - * @param departId 必填。部门id - * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 - * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + * @param maxRetryTimes 最大重试次数 */ - List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + void setMaxRetryTimes(int maxRetryTimes); /** - * 新建用户 + * 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。 * - * @param user 用户对象 + * @param id id可以为任意字符串,建议使用FromUserName作为id */ - void userCreate(WxCpUser user) throws WxErrorException; + WxSession getSession(String id); /** - * 更新用户 + * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 * - * @param user 用户对象 + * @param id id可以为任意字符串,建议使用FromUserName作为id + * @param create 是否新建 */ - void userUpdate(WxCpUser user) throws WxErrorException; + WxSession getSession(String id, boolean create); /** - * 删除用户 + * 获取WxSessionManager 对象 * - * @param userid 用户id + * @return WxSessionManager */ - void userDelete(String userid) throws WxErrorException; + WxSessionManager getSessionManager(); /** *
    -   * 批量删除成员
    -   * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98
    +   * 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法,
    +   * WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager}
        * 
    * - * @param userids 员工UserID列表。对应管理端的帐号 - */ - void userDelete(String[] userids) throws WxErrorException; - - /** - * 获取用户 - * - * @param userid 用户id + * @param sessionManager 会话管理器 */ - WxCpUser userGet(String userid) throws WxErrorException; + void setSessionManager(WxSessionManager sessionManager); /** - * 创建标签 + * 上传部门列表覆盖企业号上的部门信息 * - * @param tagName 标签名 + * @param mediaId 媒体id */ - String tagCreate(String tagName) throws WxErrorException; + String replaceParty(String mediaId) throws WxErrorException; /** - * 更新标签 + * 上传用户列表覆盖企业号上的用户信息 * - * @param tagId 标签id - * @param tagName 标签名 + * @param mediaId 媒体id */ - void tagUpdate(String tagId, String tagName) throws WxErrorException; + String replaceUser(String mediaId) throws WxErrorException; /** - * 删除标签 - * - * @param tagId 标签id + * 获取异步任务结果 */ - void tagDelete(String tagId) throws WxErrorException; + String getTaskResult(String joinId) throws WxErrorException; /** - * 获得标签列表 + * 初始化http请求对象 */ - List tagGet() throws WxErrorException; + void initHttp(); /** - * 获取标签成员 + * 获取WxMpConfigStorage 对象 * - * @param tagId 标签ID + * @return WxMpConfigStorage */ - List tagGetUsers(String tagId) throws WxErrorException; + WxCpConfigStorage getWxCpConfigStorage(); /** - * 增加标签成员 + * 注入 {@link WxCpConfigStorage} 的实现 * - * @param tagId 标签id - * @param userIds 用户ID 列表 + * @param wxConfigProvider 配置对象 */ - void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException; + void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider); /** - *
    -   * 构造oauth2授权的url连接
    -   * 
    - * - * @param state 状态码 - * @return url + * 获取部门相关接口的服务类对象 */ - String oauth2buildAuthorizationUrl(String state); + WxCpDepartmentService getDepartmentService(); /** - *
    -   * 构造oauth2授权的url连接
    -   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code
    -   * 
    - * - * @param redirectUri 跳转链接地址 - * @param state 状态码 - * @return url + * 获取媒体相关接口的服务类对象 */ - String oauth2buildAuthorizationUrl(String redirectUri, String state); + WxCpMediaService getMediaService(); /** - *
    -   * 用oauth2获取用户信息
    -   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
    -   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
    -   *
    -   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    -   * 
    - * - * @param code 微信oauth授权返回的代码 - * @return [userid, deviceid] - * @see #oauth2getUserInfo(Integer, String) + * 获取菜单相关接口的服务类对象 */ - String[] oauth2getUserInfo(String code) throws WxErrorException; + WxCpMenuService getMenuService(); /** - *
    -   * 用oauth2获取用户信息
    -   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
    -   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
    -   *
    -   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
    -   * 
    - * - * @param agentId 企业号应用的id - * @param code 微信oauth授权返回的代码 - * @return [userid, deviceid] - * @see #oauth2getUserInfo(String) + * 获取Oauth2相关接口的服务类对象 */ - String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException; - + WxCpOAuth2Service getOauth2Service(); /** - * 移除标签成员 - * - * @param tagId 标签id - * @param userIds 用户id列表 + * 获取标签相关接口的服务类对象 */ - void tagRemoveUsers(String tagId, List userIds) throws WxErrorException; + WxCpTagService getTagService(); /** - *
    -   * 邀请成员关注
    -   * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8
    -   * 
    - * - * @param userId 用户的userid - * @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 - * @return 1:微信邀请 2.邮件邀请 + * 获取用户相关接口的服务类对象 */ - int invite(String userId, String inviteTips) throws WxErrorException; + WxCpUserService getUserService(); - /** - *
    -   * 获取微信服务器的ip段
    -   * http://qydev.weixin.qq.com/wiki/index.php?title=回调模式#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5
    -   * 
    - * - * @return { "ip_list": ["101.226.103.*", "101.226.62.*"] } - */ - String[] getCallbackIp() throws WxErrorException; + WxCpExternalContactService getExternalContactService(); /** - * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 + * 获取群聊服务 * - * @param url 接口地址 - * @param queryParam 请求参数 + * @return 群聊服务 */ - String get(String url, String queryParam) throws WxErrorException; + WxCpChatService getChatService(); /** - * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 + * 获取任务卡片服务 * - * @param url 接口地址 - * @param postData 请求body字符串 + * @return 任务卡片服务 */ - String post(String url, String postData) throws WxErrorException; + WxCpTaskCardService getTaskCardService(); - /** - *
    -   * Service没有实现某个API的时候,可以用这个,
    -   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
    -   * 可以参考,{@link me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor}的实现方法
    -   * 
    - * - * @param executor 执行器 - * @param uri 请求地址 - * @param data 参数 - * @param 请求值类型 - * @param 返回值类型 - */ - T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; + WxCpAgentService getAgentService(); - /** - * 注入 {@link WxCpConfigStorage} 的实现 - * - * @param wxConfigProvider 配置对象 - */ - void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider); + WxCpOaService getOAService(); /** - *
    -   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
    -   * 默认:1000ms
    -   * 
    + * 获取群机器人消息推送服务 * - * @param retrySleepMillis 重试休息时间 + * @return 群机器人消息推送服务 */ - void setRetrySleepMillis(int retrySleepMillis); + WxCpGroupRobotService getGroupRobotService(); /** - *
    -   * 设置当微信系统响应系统繁忙时,最大重试次数
    -   * 默认:5次
    -   * 
    - * - * @param maxRetryTimes 最大重试次数 + * http请求对象 */ - void setMaxRetryTimes(int maxRetryTimes); + RequestHttp getRequestHttp(); - /** - * 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。 - * - * @param id id可以为任意字符串,建议使用FromUserName作为id - */ - WxSession getSession(String id); + void setUserService(WxCpUserService userService); - /** - * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 - * - * @param id id可以为任意字符串,建议使用FromUserName作为id - * @param create 是否新建 - */ - WxSession getSession(String id, boolean create); + void setDepartmentService(WxCpDepartmentService departmentService); - /** - *
    -   * 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法,
    -   * WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager}
    -   * 
    - * - * @param sessionManager 会话管理器 - */ - void setSessionManager(WxSessionManager sessionManager); + void setMediaService(WxCpMediaService mediaService); - /** - * 上传部门列表覆盖企业号上的部门信息 - * - * @param mediaId 媒体id - */ - String replaceParty(String mediaId) throws WxErrorException; + void setMenuService(WxCpMenuService menuService); - /** - * 上传用户列表覆盖企业号上的用户信息 - * - * @param mediaId 媒体id - */ - String replaceUser(String mediaId) throws WxErrorException; + void setOauth2Service(WxCpOAuth2Service oauth2Service); - /** - * 获取异步任务结果 - */ - String getTaskResult(String joinId) throws WxErrorException; + void setTagService(WxCpTagService tagService); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java deleted file mode 100644 index 0105e78d52..0000000000 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java +++ /dev/null @@ -1,697 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import com.google.gson.*; -import com.google.gson.reflect.TypeToken; -import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.menu.WxMenu; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.session.StandardSessionManager; -import me.chanjar.weixin.common.session.WxSession; -import me.chanjar.weixin.common.session.WxSessionManager; -import me.chanjar.weixin.common.util.RandomUtils; -import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.*; -import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpMessage; -import me.chanjar.weixin.cp.bean.WxCpTag; -import me.chanjar.weixin.cp.bean.WxCpUser; -import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.BasicResponseHandler; -import org.apache.http.impl.client.CloseableHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.UUID; - -public class WxCpServiceImpl implements WxCpService { - - protected final Logger log = LoggerFactory.getLogger(WxCpServiceImpl.class); - - /** - * 全局的是否正在刷新access token的锁 - */ - protected final Object globalAccessTokenRefreshLock = new Object(); - - /** - * 全局的是否正在刷新jsapi_ticket的锁 - */ - protected final Object globalJsapiTicketRefreshLock = new Object(); - - protected WxCpConfigStorage configStorage; - - protected CloseableHttpClient httpClient; - - protected HttpHost httpProxy; - protected WxSessionManager sessionManager = new StandardSessionManager(); - /** - * 临时文件目录 - */ - protected File tmpDirFile; - private int retrySleepMillis = 1000; - private int maxRetryTimes = 5; - - @Override - public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { - try { - return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) - .equals(msgSignature); - } catch (Exception e) { - return false; - } - } - - @Override - public void userAuthenticated(String userId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId; - get(url, null); - } - - @Override - public String getAccessToken() throws WxErrorException { - return getAccessToken(false); - } - - @Override - public String getAccessToken(boolean forceRefresh) throws WxErrorException { - if (forceRefresh) { - this.configStorage.expireAccessToken(); - } - if (this.configStorage.isAccessTokenExpired()) { - synchronized (this.globalAccessTokenRefreshLock) { - if (this.configStorage.isAccessTokenExpired()) { - String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" - + "&corpid=" + this.configStorage.getCorpId() - + "&corpsecret=" + this.configStorage.getCorpSecret(); - try { - HttpGet httpGet = new HttpGet(url); - if (this.httpProxy != null) { - RequestConfig config = RequestConfig.custom() - .setProxy(this.httpProxy).build(); - httpGet.setConfig(config); - } - String resultContent = null; - try (CloseableHttpClient httpclient = getHttpclient(); - CloseableHttpResponse response = httpclient.execute(httpGet)) { - resultContent = new BasicResponseHandler().handleResponse(response); - } finally { - httpGet.releaseConnection(); - } - WxError error = WxError.fromJson(resultContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - this.configStorage.updateAccessToken( - accessToken.getAccessToken(), accessToken.getExpiresIn()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - } - return this.configStorage.getAccessToken(); - } - - @Override - public String getJsapiTicket() throws WxErrorException { - return getJsapiTicket(false); - } - - @Override - public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - if (forceRefresh) { - this.configStorage.expireJsapiTicket(); - } - if (this.configStorage.isJsapiTicketExpired()) { - synchronized (this.globalJsapiTicketRefreshLock) { - if (this.configStorage.isJsapiTicketExpired()) { - String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); - String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); - int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.configStorage.updateJsapiTicket(jsapiTicket, - expiresInSeconds); - } - } - } - return this.configStorage.getJsapiTicket(); - } - - @Override - public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { - long timestamp = System.currentTimeMillis() / 1000; - String noncestr = RandomUtils.getRandomStr(); - String jsapiTicket = getJsapiTicket(false); - String signature = SHA1.genWithAmple( - "jsapi_ticket=" + jsapiTicket, - "noncestr=" + noncestr, - "timestamp=" + timestamp, - "url=" + url - ); - WxJsapiSignature jsapiSignature = new WxJsapiSignature(); - jsapiSignature.setTimestamp(timestamp); - jsapiSignature.setNonceStr(noncestr); - jsapiSignature.setUrl(url); - jsapiSignature.setSignature(signature); - - // Fixed bug - jsapiSignature.setAppId(this.configStorage.getCorpId()); - - return jsapiSignature; - } - - @Override - public void messageSend(WxCpMessage message) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; - post(url, message.toJson()); - } - - @Override - public void menuCreate(WxMenu menu) throws WxErrorException { - menuCreate(this.configStorage.getAgentId(), menu); - } - - @Override - public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" - + this.configStorage.getAgentId(); - post(url, menu.toJson()); - } - - @Override - public void menuDelete() throws WxErrorException { - menuDelete(this.configStorage.getAgentId()); - } - - @Override - public void menuDelete(Integer agentId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; - get(url, null); - } - - @Override - public WxMenu menuGet() throws WxErrorException { - return menuGet(this.configStorage.getAgentId()); - } - - @Override - public WxMenu menuGet(Integer agentId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; - try { - String resultContent = get(url, null); - return WxMenu.fromJson(resultContent); - } catch (WxErrorException e) { - // 46003 不存在的菜单数据 - if (e.getError().getErrorCode() == 46003) { - return null; - } - throw e; - } - } - - @Override - public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) - throws WxErrorException, IOException { - return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); - } - - @Override - public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; - return execute(new MediaUploadRequestExecutor(), url, file); - } - - @Override - public File mediaDownload(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; - return execute( - new MediaDownloadRequestExecutor( - this.configStorage.getTmpDirFile()), - url, "media_id=" + mediaId); - } - - - @Override - public Integer departCreate(WxCpDepart depart) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; - String responseContent = execute( - new SimplePostRequestExecutor(), - url, - depart.toJson()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); - } - - @Override - public void departUpdate(WxCpDepart group) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update"; - post(url, group.toJson()); - } - - @Override - public void departDelete(Integer departId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId; - get(url, null); - } - - @Override - public List departGet() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; - String responseContent = get(url, null); - /* - * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } - * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } - */ - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("department"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public void userCreate(WxCpUser user) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create"; - post(url, user.toJson()); - } - - @Override - public void userUpdate(WxCpUser user) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update"; - post(url, user.toJson()); - } - - @Override - public void userDelete(String userid) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userid; - get(url, null); - } - - @Override - public void userDelete(String[] userids) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; - JsonObject jsonObject = new JsonObject(); - JsonArray jsonArray = new JsonArray(); - for (String userid : userids) { - jsonArray.add(new JsonPrimitive(userid)); - } - jsonObject.add("useridlist", jsonArray); - post(url, jsonObject.toString()); - } - - @Override - public WxCpUser userGet(String userid) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid; - String responseContent = get(url, null); - return WxCpUser.fromJson(responseContent); - } - - @Override - public List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId; - String params = ""; - if (fetchChild != null) { - params += "&fetch_child=" + (fetchChild ? "1" : "0"); - } - if (status != null) { - params += "&status=" + status; - } else { - params += "&status=0"; - } - - String responseContent = get(url, params); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId; - String params = ""; - if (fetchChild != null) { - params += "&fetch_child=" + (fetchChild ? "1" : "0"); - } - if (status != null) { - params += "&status=" + status; - } else { - params += "&status=0"; - } - - String responseContent = get(url, params); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public String tagCreate(String tagName) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create"; - JsonObject o = new JsonObject(); - o.addProperty("tagname", tagName); - String responseContent = post(url, o.toString()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); - } - - @Override - public void tagUpdate(String tagId, String tagName) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update"; - JsonObject o = new JsonObject(); - o.addProperty("tagid", tagId); - o.addProperty("tagname", tagName); - post(url, o.toString()); - } - - @Override - public void tagDelete(String tagId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId; - get(url, null); - } - - @Override - public List tagGet() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list"; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("taglist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public List tagGetUsers(String tagId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("tagid", tagId); - if (userIds != null) { - JsonArray jsonArray = new JsonArray(); - for (String userId : userIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("userlist", jsonArray); - } - if (partyIds != null) { - JsonArray jsonArray = new JsonArray(); - for (String userId : partyIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("partylist", jsonArray); - } - post(url, jsonObject.toString()); - } - - @Override - public void tagRemoveUsers(String tagId, List userIds) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("tagid", tagId); - JsonArray jsonArray = new JsonArray(); - for (String userId : userIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("userlist", jsonArray); - post(url, jsonObject.toString()); - } - - @Override - public String oauth2buildAuthorizationUrl(String state) { - return this.oauth2buildAuthorizationUrl( - this.configStorage.getOauth2redirectUri(), - state - ); - } - - @Override - public String oauth2buildAuthorizationUrl(String redirectUri, String state) { - String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; - url += "appid=" + this.configStorage.getCorpId(); - url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); - url += "&response_type=code"; - url += "&scope=snsapi_base"; - if (state != null) { - url += "&state=" + state; - } - url += "#wechat_redirect"; - return url; - } - - @Override - public String[] oauth2getUserInfo(String code) throws WxErrorException { - return oauth2getUserInfo(this.configStorage.getAgentId(), code); - } - - @Override - public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" - + "code=" + code - + "&agentid=" + agentId; - String responseText = get(url, null); - JsonElement je = new JsonParser().parse(responseText); - JsonObject jo = je.getAsJsonObject(); - return new String[]{GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId"), GsonHelper.getString(jo, "OpenId")}; - } - - @Override - public int invite(String userId, String inviteTips) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/invite/send"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("userid", userId); - if (StringUtils.isNotEmpty(inviteTips)) { - jsonObject.addProperty("invite_tips", inviteTips); - } - String responseContent = post(url, jsonObject.toString()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return tmpJsonElement.getAsJsonObject().get("type").getAsInt(); - } - - @Override - public String[] getCallbackIp() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); - String[] ips = new String[jsonArray.size()]; - for (int i = 0; i < jsonArray.size(); i++) { - ips[i] = jsonArray.get(i).getAsString(); - } - return ips; - } - - @Override - public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); - } - - @Override - public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); - } - - /** - * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 - */ - @Override - public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { - int retryTimes = 0; - do { - try { - T result = this.executeInternal(executor, uri, data); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",uri, data, result); - return result; - } catch (WxErrorException e) { - if (retryTimes + 1 > this.maxRetryTimes) { - this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); - //最后一次重试失败后,直接抛出异常,不再等待 - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - WxError error = e.getError(); - /* - * -1 系统繁忙, 1000ms后重试 - */ - if (error.getErrorCode() == -1) { - int sleepMillis = this.retrySleepMillis * (1 << retryTimes); - try { - this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); - Thread.sleep(sleepMillis); - } catch (InterruptedException e1) { - throw new RuntimeException(e1); - } - } else { - throw e; - } - } - } while (retryTimes++ < this.maxRetryTimes); - - this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - protected synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { - if (uri.contains("access_token=")) { - throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); - } - String accessToken = getAccessToken(false); - - String uriWithAccessToken = uri; - uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; - - try { - return executor.execute(getHttpclient(), this.httpProxy, uriWithAccessToken, data); - } catch (WxErrorException e) { - WxError error = e.getError(); - /* - * 发生以下情况时尝试刷新access_token - * 40001 获取access_token时AppSecret错误,或者access_token无效 - * 42001 access_token超时 - */ - if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { - // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token - this.configStorage.expireAccessToken(); - return execute(executor, uri, data); - } - - if (error.getErrorCode() != 0) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, error); - throw new WxErrorException(error); - } - return null; - } catch (IOException e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uri, data, e.getMessage()); - throw new RuntimeException(e); - } - } - - protected CloseableHttpClient getHttpclient() { - return this.httpClient; - } - - @Override - public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) { - this.configStorage = wxConfigProvider; - ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage - .getApacheHttpClientBuilder(); - if (null == apacheHttpClientBuilder) { - apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); - } - - apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) - .httpProxyPort(this.configStorage.getHttpProxyPort()) - .httpProxyUsername(this.configStorage.getHttpProxyUsername()) - .httpProxyPassword(this.configStorage.getHttpProxyPassword()); - - if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { - this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); - } - - this.httpClient = apacheHttpClientBuilder.build(); - } - - @Override - public void setRetrySleepMillis(int retrySleepMillis) { - this.retrySleepMillis = retrySleepMillis; - } - - - @Override - public void setMaxRetryTimes(int maxRetryTimes) { - this.maxRetryTimes = maxRetryTimes; - } - - @Override - public WxSession getSession(String id) { - if (this.sessionManager == null) { - return null; - } - return this.sessionManager.getSession(id); - } - - @Override - public WxSession getSession(String id, boolean create) { - if (this.sessionManager == null) { - return null; - } - return this.sessionManager.getSession(id, create); - } - - - @Override - public void setSessionManager(WxSessionManager sessionManager) { - this.sessionManager = sessionManager; - } - - @Override - public String replaceParty(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("media_id", mediaId); - return post(url, jsonObject.toString()); - } - - @Override - public String replaceUser(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("media_id", mediaId); - return post(url, jsonObject.toString()); - } - - @Override - public String getTaskResult(String joinId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId; - return get(url, null); - } - - public File getTmpDirFile() { - return this.tmpDirFile; - } - - public void setTmpDirFile(File tmpDirFile) { - this.tmpDirFile = tmpDirFile; - } - - -} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java new file mode 100644 index 0000000000..045264f7d0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java @@ -0,0 +1,100 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpTag; +import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult; +import me.chanjar.weixin.cp.bean.WxCpTagGetResult; +import me.chanjar.weixin.cp.bean.WxCpUser; + +import java.util.List; + +/** + *
    + *  标签管理接口.
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +public interface WxCpTagService { + /** + * 创建标签. + *
    +   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/tag/create?access_token=ACCESS_TOKEN
    +   * 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90210
    +   * 
    + * + * @param name 标签名称,长度限制为32个字以内(汉字或英文字母),标签名不可与其他标签重名。 + * @param id 标签id,非负整型,指定此参数时新增的标签会生成对应的标签id,不指定时则以目前最大的id自增。 + * @return 标签id + * @throws WxErrorException . + */ + String create(String name, Integer id) throws WxErrorException; + + /** + * 更新标签. + * + * @param tagId 标签id + * @param tagName 标签名 + * @throws WxErrorException . + */ + void update(String tagId, String tagName) throws WxErrorException; + + /** + * 删除标签. + * + * @param tagId 标签id + * @throws WxErrorException . + */ + void delete(String tagId) throws WxErrorException; + + /** + * 获得标签列表. + * + * @return 标签列表 + * @throws WxErrorException . + */ + List listAll() throws WxErrorException; + + /** + * 获取标签成员. + * + * @param tagId 标签ID + * @return 成员列表 + * @throws WxErrorException . + */ + List listUsersByTagId(String tagId) throws WxErrorException; + + /** + * 获取标签成员. + * 对应: http://qydev.weixin.qq.com/wiki/index.php?title=管理标签 中的get接口 + * + * @param tagId 标签id + * @return . + * @throws WxErrorException . + */ + WxCpTagGetResult get(String tagId) throws WxErrorException; + + /** + * 增加标签成员. + * + * @param tagId 标签id + * @param userIds 用户ID 列表 + * @param partyIds 企业部门ID列表 + * @return . + * @throws WxErrorException . + */ + WxCpTagAddOrRemoveUsersResult addUsers2Tag(String tagId, List userIds, List partyIds) throws WxErrorException; + + /** + * 移除标签成员. + * + * @param tagId 标签id + * @param userIds 用户id列表 + * @param partyIds 企业部门ID列表 + * @return . + * @throws WxErrorException . + */ + WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List userIds, List partyIds) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java new file mode 100644 index 0000000000..5bf50d36dc --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + *
    + *  任务卡片管理接口.
    + *  Created by Jeff on 2019-05-16.
    + * 
    + * + * @author Jeff + * @date 2019-05-16 + */ +public interface WxCpTaskCardService { + + /** + *
    +   * 更新任务卡片消息状态
    +   * 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/91579
    +   *
    +   * 注意: 这个方法使用WxCpConfigStorage里的agentId
    +   * 
    + * + * @param userIds 企业的成员ID列表 + * @param taskId 任务卡片ID + * @param clickedKey 已点击按钮的Key + */ + void update(List userIds, String taskId, String clickedKey) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTpService.java new file mode 100644 index 0000000000..ad2f403af6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTpService.java @@ -0,0 +1,208 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; +import me.chanjar.weixin.cp.bean.WxCpTpAuthInfo; +import me.chanjar.weixin.cp.bean.WxCpTpCorp; +import me.chanjar.weixin.cp.bean.WxCpTpPermanentCodeInfo; +import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; + +/** + * 微信第三方应用API的Service. + * + * @author zhenjun cai + */ +public interface WxCpTpService { + /** + *
    +   * 验证推送过来的消息的正确性
    +   * 详情请见: https://work.weixin.qq.com/api/doc#90000/90139/90968/消息体签名校验
    +   * 
    + * + * @param msgSignature 消息签名 + * @param timestamp 时间戳 + * @param nonce 随机数 + * @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 + */ + boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); + + /** + * 获取suite_access_token, 不强制刷新suite_access_token + * + * @see #getSuiteAccessToken(boolean) + */ + String getSuiteAccessToken() throws WxErrorException; + + /** + *
    +   * 获取suite_access_token,本方法线程安全
    +   * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
    +   * 另:本service的所有方法都会在suite_access_token过期是调用此方法
    +   * 程序员在非必要情况下尽量不要主动调用此方法
    +   * 详情请见: https://work.weixin.qq.com/api/doc#90001/90143/90600
    +   * 
    + * + * @param forceRefresh 强制刷新 + */ + String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException; + + /** + * 获得suite_ticket,不强制刷新suite_ticket + * + * @see #getSuiteTicket(boolean) + */ + String getSuiteTicket() throws WxErrorException; + + /** + *
    +   * 获得suite_ticket
    +   * 由于suite_ticket是微信服务器定时推送(每10分钟),不能主动获取,如果碰到过期只能抛异常
    +   *
    +   * 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628
    +   * 
    + * + * @param forceRefresh 强制刷新 + */ + String getSuiteTicket(boolean forceRefresh) throws WxErrorException; + + /** + * 小程序登录凭证校验 + * + * @param jsCode 登录时获取的 code + */ + WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException; + + /** + * 获取企业凭证 + * + * @param authCorpid 授权方corpid + * @param permanentCode 永久授权码,通过get_permanent_code获取 + */ + WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException; + + /** + * 获取企业永久授权码 . + * + * @param authCode . + * @return . + */ + @Deprecated + WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException; + + /** + * 获取企业永久授权码信息 + *
    +   *   原来的方法实现不全
    +   * 
    + * + * @param authCode + * @return + * + * @author yuan + * @since 2020-03-18 + * + * @throws WxErrorException + */ + WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException; + + /** + *
    +   *   获取预授权链接
    +   * 
    + * @param redirectUri 授权完成后的回调网址 + * @param state a-zA-Z0-9的参数值(不超过128个字节),用于第三方自行校验session,防止跨域攻击 + * @return + * @throws WxErrorException + */ + String getPreAuthUrl(String redirectUri,String state) throws WxErrorException; + + /** + * 获取企业的授权信息 + * + * @param authCorpId 授权企业的corpId + * @param permanentCode 授权企业的永久授权码 + * @return + * @throws WxErrorException + */ + WxCpTpAuthInfo getAuthInfo(String authCorpId,String permanentCode) throws WxErrorException; + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. + * + * @param url 接口地址 + * @param queryParam 请求参数 + */ + String get(String url, String queryParam) throws WxErrorException; + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. + * + * @param url 接口地址 + * @param postData 请求body字符串 + */ + String post(String url, String postData) throws WxErrorException; + + /** + *
    +   * Service没有实现某个API的时候,可以用这个,
    +   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
    +   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
    +   * 
    + * + * @param executor 执行器 + * @param uri 请求地址 + * @param data 参数 + * @param 请求值类型 + * @param 返回值类型 + */ + T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; + + /** + *
    +   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试.
    +   * 默认:1000ms
    +   * 
    + * + * @param retrySleepMillis 重试休息时间 + */ + void setRetrySleepMillis(int retrySleepMillis); + + /** + *
    +   * 设置当微信系统响应系统繁忙时,最大重试次数.
    +   * 默认:5次
    +   * 
    + * + * @param maxRetryTimes 最大重试次数 + */ + void setMaxRetryTimes(int maxRetryTimes); + + /** + * 初始化http请求对象 + */ + void initHttp(); + + /** + * 获取WxMpConfigStorage 对象. + * + * @return WxMpConfigStorage + */ + WxCpTpConfigStorage getWxCpTpConfigStorage(); + + /** + * 注入 {@link WxCpTpConfigStorage} 的实现. + * + * @param wxConfigProvider 配置对象 + */ + void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider); + + /** + * http请求对象. + */ + RequestHttp getRequestHttp(); + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java new file mode 100644 index 0000000000..8cf5670f9b --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java @@ -0,0 +1,173 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpInviteResult; +import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.cp.bean.external.WxCpUserExternalContactInfo; + +import java.util.List; +import java.util.Map; + +/** + *
    + * 用户管理接口
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +public interface WxCpUserService { + + /** + *
    +   *   用在二次验证的时候.
    +   *   企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。
    +   * 
    + * + * @param userId 用户id + */ + void authenticate(String userId) throws WxErrorException; + + /** + *
    +   * 获取部门成员(详情).
    +   *
    +   * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29
    +   * 
    + * + * @param departId 必填。部门id + * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 + * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + */ + List listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException; + + /** + *
    +   * 获取部门成员.
    +   *
    +   * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98
    +   * 
    + * + * @param departId 必填。部门id + * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 + * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + */ + List listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException; + + /** + * 新建用户. + * + * @param user 用户对象 + */ + void create(WxCpUser user) throws WxErrorException; + + /** + * 更新用户. + * + * @param user 用户对象 + */ + void update(WxCpUser user) throws WxErrorException; + + /** + *
    +   * 删除用户/批量删除成员.
    +   * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98
    +   * 
    + * + * @param userIds 员工UserID列表。对应管理端的帐号 + */ + void delete(String... userIds) throws WxErrorException; + + /** + * 获取用户. + * + * @param userid 用户id + */ + WxCpUser getById(String userid) throws WxErrorException; + + /** + *
    +   * 邀请成员.
    +   * 企业可通过接口批量邀请成员使用企业微信,邀请后将通过短信或邮件下发通知。
    +   * 请求方式:POST(HTTPS)
    +   * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/batch/invite?access_token=ACCESS_TOKEN
    +   * 文档地址:https://work.weixin.qq.com/api/doc#12543
    +   * 
    + * + * @param userIds 成员ID列表, 最多支持1000个。 + * @param partyIds 部门ID列表,最多支持100个。 + * @param tagIds 标签ID列表,最多支持100个。 + */ + WxCpInviteResult invite(List userIds, List partyIds, List tagIds) throws WxErrorException; + + /** + *
    +   *  userid转openid.
    +   *  该接口使用场景为微信支付、微信红包和企业转账。
    +   *
    +   * 在使用微信支付的功能时,需要自行将企业微信的userid转成openid。
    +   * 在使用微信红包功能时,需要将应用id和userid转成appid和openid才能使用。
    +   * 注:需要成员使用微信登录企业微信或者关注微信插件才能转成openid
    +   *
    +   * 文档地址:https://work.weixin.qq.com/api/doc#11279
    +   * 
    + * + * @param userId 企业内的成员id + * @param agentId 非必填,整型,仅用于发红包。其它场景该参数不要填,如微信支付、企业转账、电子发票 + * @return map对象,可能包含以下值: + * - openid 企业微信成员userid对应的openid,若有传参agentid,则是针对该agentid的openid。否则是针对企业微信corpid的openid + * - appid 应用的appid,若请求包中不包含agentid则不返回appid。该appid在使用微信红包时会用到 + */ + Map userId2Openid(String userId, Integer agentId) throws WxErrorException; + + /** + *
    +   * openid转userid.
    +   *
    +   * 该接口主要应用于使用微信支付、微信红包和企业转账之后的结果查询。
    +   * 开发者需要知道某个结果事件的openid对应企业微信内成员的信息时,可以通过调用该接口进行转换查询。
    +   * 权限说明:
    +   * 管理组需对openid对应的企业微信成员有查看权限。
    +   *
    +   * 文档地址:https://work.weixin.qq.com/api/doc#11279
    +   * 
    + * + * @param openid 在使用微信支付、微信红包和企业转账之后,返回结果的openid + * @return userid 该openid在企业微信对应的成员userid + */ + String openid2UserId(String openid) throws WxErrorException; + + /** + *
    +   *
    +   * 通过手机号获取其所对应的userid。
    +   *
    +   * 请求方式:POST(HTTPS)
    +   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN
    +   *
    +   * 文档地址:https://work.weixin.qq.com/api/doc#90001/90143/91693
    +   * 
    + * + * @param mobile 手机号码。长度为5~32个字节 + * @return userid mobile对应的成员userid + * @throws WxErrorException . + */ + String getUserId(String mobile) throws WxErrorException; + + /** + * 获取外部联系人详情. + *
    +   *   企业可通过此接口,根据外部联系人的userid,拉取外部联系人详情。权限说明:
    +   * 企业需要使用外部联系人管理secret所获取的accesstoken来调用
    +   * 第三方应用需拥有“企业客户”权限。
    +   * 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。
    +   * 
    + * + * @param userId 外部联系人的userid + * @return 联系人详情 + * @throws WxErrorException . + */ + WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; + + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java new file mode 100644 index 0000000000..52d88e4564 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java @@ -0,0 +1,492 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.common.base.Joiner; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.StandardSessionManager; +import me.chanjar.weixin.common.session.WxSession; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.common.util.DataUtils; +import me.chanjar.weixin.common.util.RandomUtils; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.*; +import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; +import me.chanjar.weixin.cp.bean.WxCpMessage; +import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; +import me.chanjar.weixin.cp.bean.WxCpProviderToken; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.*; + +/** + * . + * + * @author chanjarster + */ +@Slf4j +public abstract class BaseWxCpServiceImpl implements WxCpService, RequestHttp { + private WxCpUserService userService = new WxCpUserServiceImpl(this); + private WxCpChatService chatService = new WxCpChatServiceImpl(this); + private WxCpDepartmentService departmentService = new WxCpDepartmentServiceImpl(this); + private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this); + private WxCpMenuService menuService = new WxCpMenuServiceImpl(this); + private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this); + private WxCpTagService tagService = new WxCpTagServiceImpl(this); + private WxCpAgentService agentService = new WxCpAgentServiceImpl(this); + private WxCpOaService oaService = new WxCpOaServiceImpl(this); + private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this); + private WxCpExternalContactService externalContactService = new WxCpExternalContactServiceImpl(this); + private WxCpGroupRobotService groupRobotService = new WxCpGroupRobotServiceImpl(this); + + /** + * 全局的是否正在刷新access token的锁. + */ + protected final Object globalAccessTokenRefreshLock = new Object(); + + /** + * 全局的是否正在刷新jsapi_ticket的锁. + */ + protected final Object globalJsapiTicketRefreshLock = new Object(); + + /** + * 全局的是否正在刷新agent的jsapi_ticket的锁. + */ + protected final Object globalAgentJsapiTicketRefreshLock = new Object(); + + protected WxCpConfigStorage configStorage; + + private WxSessionManager sessionManager = new StandardSessionManager(); + + /** + * 临时文件目录. + */ + private File tmpDirFile; + private int retrySleepMillis = 1000; + private int maxRetryTimes = 5; + + @Override + public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { + try { + return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) + .equals(msgSignature); + } catch (Exception e) { + log.error("Checking signature failed, and the reason is :" + e.getMessage()); + return false; + } + } + + @Override + public String getAccessToken() throws WxErrorException { + return getAccessToken(false); + } + + @Override + public String getAgentJsapiTicket() throws WxErrorException { + return this.getAgentJsapiTicket(false); + } + + @Override + public String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + this.configStorage.expireAgentJsapiTicket(); + } + + if (this.configStorage.isAgentJsapiTicketExpired()) { + synchronized (this.globalAgentJsapiTicketRefreshLock) { + if (this.configStorage.isAgentJsapiTicketExpired()) { + String responseContent = this.get(this.configStorage.getApiUrl(GET_AGENT_CONFIG_TICKET), null); + JsonObject jsonObject = GsonParser.parse(responseContent); + this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(), + jsonObject.get("expires_in").getAsInt()); + } + } + } + + return this.configStorage.getAgentJsapiTicket(); + } + + @Override + public String getJsapiTicket() throws WxErrorException { + return getJsapiTicket(false); + } + + @Override + public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + this.configStorage.expireJsapiTicket(); + } + + if (this.configStorage.isJsapiTicketExpired()) { + synchronized (this.globalJsapiTicketRefreshLock) { + if (this.configStorage.isJsapiTicketExpired()) { + String responseContent = this.get(this.configStorage.getApiUrl(GET_JSAPI_TICKET), null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(), + tmpJsonObject.get("expires_in").getAsInt()); + } + } + } + + return this.configStorage.getJsapiTicket(); + } + + @Override + public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { + long timestamp = System.currentTimeMillis() / 1000; + String noncestr = RandomUtils.getRandomStr(); + String jsapiTicket = getJsapiTicket(false); + String signature = SHA1.genWithAmple( + "jsapi_ticket=" + jsapiTicket, + "noncestr=" + noncestr, + "timestamp=" + timestamp, + "url=" + url + ); + WxJsapiSignature jsapiSignature = new WxJsapiSignature(); + jsapiSignature.setTimestamp(timestamp); + jsapiSignature.setNonceStr(noncestr); + jsapiSignature.setUrl(url); + jsapiSignature.setSignature(signature); + + // Fixed bug + jsapiSignature.setAppId(this.configStorage.getCorpId()); + + return jsapiSignature; + } + + @Override + public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException { + Integer agentId = message.getAgentId(); + if (null == agentId) { + message.setAgentId(this.getWxCpConfigStorage().getAgentId()); + } + + return WxCpMessageSendResult.fromJson(this.post(this.configStorage.getApiUrl(MESSAGE_SEND), message.toJson())); + } + + @Override + public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException { + Map params = new HashMap<>(2); + params.put("js_code", jsCode); + params.put("grant_type", "authorization_code"); + + final String url = this.configStorage.getApiUrl(JSCODE_TO_SESSION); + return WxCpMaJsCode2SessionResult.fromJson(this.get(url, Joiner.on("&").withKeyValueSeparator("=").join(params))); + } + + @Override + public String[] getCallbackIp() throws WxErrorException { + String responseContent = get(this.configStorage.getApiUrl(GET_CALLBACK_IP), null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + JsonArray jsonArray = tmpJsonObject.get("ip_list").getAsJsonArray(); + String[] ips = new String[jsonArray.size()]; + for (int i = 0; i < jsonArray.size(); i++) { + ips[i] = jsonArray.get(i).getAsString(); + } + return ips; + } + + @Override + public WxCpProviderToken getProviderToken(String corpId, String providerSecret) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("corpid", corpId); + jsonObject.addProperty("provider_secret", providerSecret); + return WxCpProviderToken.fromJson(this.post(this.configStorage.getApiUrl(Tp.GET_PROVIDER_TOKEN), jsonObject.toString())); + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(SimpleGetRequestExecutor.create(this), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(SimplePostRequestExecutor.create(this), url, postData); + } + + @Override + public String postWithoutToken(String url, String postData) throws WxErrorException { + return this.executeNormal(SimplePostRequestExecutor.create(this), url, postData); + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求. + */ + @Override + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + int retryTimes = 0; + do { + try { + return this.executeInternal(executor, uri, data); + } catch (WxErrorException e) { + if (retryTimes + 1 > this.maxRetryTimes) { + log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + //最后一次重试失败后,直接抛出异常,不再等待 + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + WxError error = e.getError(); + /* + * -1 系统繁忙, 1000ms后重试 + */ + if (error.getErrorCode() == -1) { + int sleepMillis = this.retrySleepMillis * (1 << retryTimes); + try { + log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + Thread.sleep(sleepMillis); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } + } else { + throw e; + } + } + } while (retryTimes++ < this.maxRetryTimes); + + log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + E dataForLog = DataUtils.handleDataWithSecret(data); + + if (uri.contains("access_token=")) { + throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); + } + String accessToken = getAccessToken(false); + + String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken; + + try { + T result = executor.execute(uriWithAccessToken, data, WxType.CP); + log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); + return result; + } catch (WxErrorException e) { + WxError error = e.getError(); + + if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) { + // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token + this.configStorage.expireAccessToken(); + if (this.getWxCpConfigStorage().autoRefreshToken()) { + log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg()); + return this.execute(executor, uri, data); + } + } + + if (error.getErrorCode() != 0) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); + throw new WxErrorException(error, e); + } + return null; + } catch (IOException e) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * 普通请求,不自动带accessToken + */ + private T executeNormal(RequestExecutor executor, String uri, E data) throws WxErrorException { + try { + T result = executor.execute(uri, data, WxType.CP); + log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uri, data, result); + return result; + } catch (WxErrorException e) { + WxError error = e.getError(); + if (error.getErrorCode() != 0) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uri, data, error); + throw new WxErrorException(error, e); + } + return null; + } catch (IOException e) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uri, data, e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) { + this.configStorage = wxConfigProvider; + this.initHttp(); + } + + @Override + public void setRetrySleepMillis(int retrySleepMillis) { + this.retrySleepMillis = retrySleepMillis; + } + + + @Override + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + @Override + public WxSession getSession(String id) { + if (this.sessionManager == null) { + return null; + } + return this.sessionManager.getSession(id); + } + + @Override + public WxSession getSession(String id, boolean create) { + if (this.sessionManager == null) { + return null; + } + return this.sessionManager.getSession(id, create); + } + + @Override + public void setSessionManager(WxSessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + @Override + public WxSessionManager getSessionManager() { + return this.sessionManager; + } + + @Override + public String replaceParty(String mediaId) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("media_id", mediaId); + return post(this.configStorage.getApiUrl(BATCH_REPLACE_PARTY), jsonObject.toString()); + } + + @Override + public String replaceUser(String mediaId) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("media_id", mediaId); + return post(this.configStorage.getApiUrl(BATCH_REPLACE_USER), jsonObject.toString()); + } + + @Override + public String getTaskResult(String joinId) throws WxErrorException { + String url = this.configStorage.getApiUrl(BATCH_GET_RESULT + joinId); + return get(url, null); + } + + public File getTmpDirFile() { + return this.tmpDirFile; + } + + public void setTmpDirFile(File tmpDirFile) { + this.tmpDirFile = tmpDirFile; + } + + @Override + public WxCpDepartmentService getDepartmentService() { + return departmentService; + } + + @Override + public WxCpMediaService getMediaService() { + return mediaService; + } + + @Override + public WxCpMenuService getMenuService() { + return menuService; + } + + @Override + public WxCpOAuth2Service getOauth2Service() { + return oauth2Service; + } + + @Override + public WxCpTagService getTagService() { + return tagService; + } + + @Override + public WxCpUserService getUserService() { + return userService; + } + + @Override + public WxCpExternalContactService getExternalContactService() { + return externalContactService; + } + + @Override + public WxCpChatService getChatService() { + return chatService; + } + + @Override + public WxCpOaService getOAService() { + return oaService; + } + + @Override + public WxCpGroupRobotService getGroupRobotService() { + return groupRobotService; + } + + @Override + public WxCpTaskCardService getTaskCardService() { + return taskCardService; + } + + @Override + public RequestHttp getRequestHttp() { + return this; + } + + @Override + public void setUserService(WxCpUserService userService) { + this.userService = userService; + } + + @Override + public void setDepartmentService(WxCpDepartmentService departmentService) { + this.departmentService = departmentService; + } + + @Override + public void setMediaService(WxCpMediaService mediaService) { + this.mediaService = mediaService; + } + + @Override + public void setMenuService(WxCpMenuService menuService) { + this.menuService = menuService; + } + + @Override + public void setOauth2Service(WxCpOAuth2Service oauth2Service) { + this.oauth2Service = oauth2Service; + } + + @Override + public void setTagService(WxCpTagService tagService) { + this.tagService = tagService; + } + + @Override + public WxCpAgentService getAgentService() { + return agentService; + } + + public void setAgentService(WxCpAgentService agentService) { + this.agentService = agentService; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java new file mode 100644 index 0000000000..191bfec0d8 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java @@ -0,0 +1,276 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.common.base.Joiner; +import com.google.gson.JsonObject; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxCpErrorMsgEnum; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.DataUtils; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpTpService; +import me.chanjar.weixin.cp.bean.*; +import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tp.*; + +/** + * . + * + * @author zhenjun cai + */ +@Slf4j +public abstract class BaseWxCpTpServiceImpl implements WxCpTpService, RequestHttp { + + /** + * 全局的是否正在刷新access token的锁. + */ + protected final Object globalSuiteAccessTokenRefreshLock = new Object(); + + /** + * 全局的是否正在刷新jsapi_ticket的锁. + */ + protected final Object globalSuiteTicketRefreshLock = new Object(); + + protected WxCpTpConfigStorage configStorage; + + /** + * 临时文件目录. + */ + private File tmpDirFile; + private int retrySleepMillis = 1000; + private int maxRetryTimes = 5; + + @Override + public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { + try { + return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) + .equals(msgSignature); + } catch (Exception e) { + log.error("Checking signature failed, and the reason is :" + e.getMessage()); + return false; + } + } + + @Override + public String getSuiteAccessToken() throws WxErrorException { + return getSuiteAccessToken(false); + } + + @Override + public String getSuiteTicket() throws WxErrorException { + return getSuiteTicket(false); + } + + @Override + public String getSuiteTicket(boolean forceRefresh) throws WxErrorException { +// suite ticket由微信服务器推送,不能强制刷新 +// if (forceRefresh) { +// this.configStorage.expireSuiteTicket(); +// } + + if (this.configStorage.isSuiteTicketExpired()) { + // 本地suite ticket 不存在或者过期 + WxError wxError = WxError.fromJson("{\"errcode\":40085, \"errmsg\":\"invaild suite ticket\"}", WxType.CP); + throw new WxErrorException(wxError); + } + return this.configStorage.getSuiteTicket(); + } + + + @Override + public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException { + Map params = new HashMap<>(2); + params.put("js_code", jsCode); + params.put("grant_type", "authorization_code"); + + final String url = configStorage.getApiUrl(JSCODE_TO_SESSION); + return WxCpMaJsCode2SessionResult.fromJson(this.get(url, Joiner.on("&").withKeyValueSeparator("=").join(params))); + } + + + @Override + public WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("auth_corpid", authCorpid); + jsonObject.addProperty("permanent_code", permanentCode); + String result = post(configStorage.getApiUrl(GET_CORP_TOKEN), jsonObject.toString()); + + return WxAccessToken.fromJson(result); + } + + @Override + public WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("auth_code", authCode); + + String result = post(configStorage.getApiUrl(GET_PERMANENT_CODE), jsonObject.toString()); + jsonObject = GsonParser.parse(result); + WxCpTpCorp wxCpTpCorp = WxCpTpCorp.fromJson(jsonObject.get("auth_corp_info").getAsJsonObject().toString()); + wxCpTpCorp.setPermanentCode(jsonObject.get("permanent_code").getAsString()); + return wxCpTpCorp; + } + + @Override + public WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException{ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("auth_code", authCode); + String result = post(configStorage.getApiUrl(GET_PERMANENT_CODE), jsonObject.toString()); + return WxCpTpPermanentCodeInfo.fromJson(result); + } + + @Override + @SneakyThrows + public String getPreAuthUrl(String redirectUri,String state) throws WxErrorException{ + String result = get(configStorage.getApiUrl(GET_PREAUTH_CODE),null); + WxCpTpPreauthCode preauthCode = WxCpTpPreauthCode.fromJson(result); + String preAuthUrl = "https://open.work.weixin.qq.com/3rdapp/install?suite_id="+configStorage.getSuiteId()+ + "&pre_auth_code="+preauthCode.getPreAuthCode()+"&redirect_uri="+ URLEncoder.encode(redirectUri,"utf-8"); + if(StringUtils.isNotBlank(state)) + preAuthUrl += "&state="+state; + return preAuthUrl; + } + + @Override + public WxCpTpAuthInfo getAuthInfo(String authCorpId, String permanentCode) throws WxErrorException{ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("auth_corpid", authCorpId); + jsonObject.addProperty("permanent_code", permanentCode); + String result = post(configStorage.getApiUrl(GET_AUTH_INFO), jsonObject.toString()); + return WxCpTpAuthInfo.fromJson(result); + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(SimpleGetRequestExecutor.create(this), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(SimplePostRequestExecutor.create(this), url, postData); + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求. + */ + @Override + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + int retryTimes = 0; + do { + try { + return this.executeInternal(executor, uri, data); + } catch (WxErrorException e) { + if (retryTimes + 1 > this.maxRetryTimes) { + log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + //最后一次重试失败后,直接抛出异常,不再等待 + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + WxError error = e.getError(); + /* + * -1 系统繁忙, 1000ms后重试 + */ + if (error.getErrorCode() == -1) { + int sleepMillis = this.retrySleepMillis * (1 << retryTimes); + try { + log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + Thread.sleep(sleepMillis); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } + } else { + throw e; + } + } + } while (retryTimes++ < this.maxRetryTimes); + + log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + E dataForLog = DataUtils.handleDataWithSecret(data); + + if (uri.contains("suite_access_token=")) { + throw new IllegalArgumentException("uri参数中不允许有suite_access_token: " + uri); + } + String suiteAccessToken = getSuiteAccessToken(false); + + String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "suite_access_token=" + suiteAccessToken; + + try { + T result = executor.execute(uriWithAccessToken, data, WxType.CP); + log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); + return result; + } catch (WxErrorException e) { + WxError error = e.getError(); + /* + * 发生以下情况时尝试刷新suite_access_token + * 42009 suite_access_token已过期 + */ + if (error.getErrorCode() == WxCpErrorMsgEnum.CODE_42009.getCode()) { + // 强制设置wxCpTpConfigStorage它的suite access token过期了,这样在下一次请求里就会刷新suite access token + this.configStorage.expireSuiteAccessToken(); + if (this.getWxCpTpConfigStorage().autoRefreshToken()) { + log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg()); + return this.execute(executor, uri, data); + } + } + + if (error.getErrorCode() != 0) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); + throw new WxErrorException(error, e); + } + return null; + } catch (IOException e) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider) { + this.configStorage = wxConfigProvider; + this.initHttp(); + } + + @Override + public void setRetrySleepMillis(int retrySleepMillis) { + this.retrySleepMillis = retrySleepMillis; + } + + + @Override + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + public File getTmpDirFile() { + return this.tmpDirFile; + } + + public void setTmpDirFile(File tmpDirFile) { + this.tmpDirFile = tmpDirFile; + } + + @Override + public RequestHttp getRequestHttp() { + return this; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java new file mode 100644 index 0000000000..4dd661fbf0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java @@ -0,0 +1,67 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpAgentService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpAgent; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Agent.*; + + +/** + *
    + *  管理企业号应用
    + *  Created by huansinho on 2018/4/13.
    + * 
    + * + * @author huansinho + */ +@RequiredArgsConstructor +public class WxCpAgentServiceImpl implements WxCpAgentService { + + + private final WxCpService mainService; + + @Override + public WxCpAgent get(Integer agentId) throws WxErrorException { + if (agentId == null) { + throw new IllegalArgumentException("缺少agentid参数"); + } + + final String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_GET), agentId); + return WxCpAgent.fromJson(this.mainService.get(url, null)); + } + + @Override + public void set(WxCpAgent agentInfo) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_SET); + String responseContent = this.mainService.post(url, agentInfo.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.CP)); + } + } + + @Override + public List list() throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_LIST); + String responseContent = this.mainService.get(url, null); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.CP)); + } + + return WxCpGsonBuilder.create().fromJson(jsonObject.get("agentlist").toString(), new TypeToken>() { + }.getType()); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java new file mode 100644 index 0000000000..10af36afe6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java @@ -0,0 +1,86 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.cp.api.WxCpChatService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; +import me.chanjar.weixin.cp.bean.WxCpChat; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Chat.*; + +/** + * 群聊服务实现. + * + * @author gaigeshen + */ +@RequiredArgsConstructor +public class WxCpChatServiceImpl implements WxCpChatService { + private final WxCpService cpService; + + @Override + public String create(String name, String owner, List users, String chatId) throws WxErrorException { + Map data = new HashMap<>(4); + if (StringUtils.isNotBlank(name)) { + data.put("name", name); + } + if (StringUtils.isNotBlank(owner)) { + data.put("owner", owner); + } + if (users != null) { + data.put("userlist", users); + } + if (StringUtils.isNotBlank(chatId)) { + data.put("chatid", chatId); + } + final String url = this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_CREATE); + String result = this.cpService.post(url, WxGsonBuilder.create().toJson(data)); + return GsonParser.parse(result).get("chatid").getAsString(); + } + + @Override + public void update(String chatId, String name, String owner, List usersToAdd, List usersToDelete) + throws WxErrorException { + Map data = new HashMap<>(5); + if (StringUtils.isNotBlank(chatId)) { + data.put("chatid", chatId); + } + if (StringUtils.isNotBlank(name)) { + data.put("name", name); + } + if (StringUtils.isNotBlank(owner)) { + data.put("owner", owner); + } + if (usersToAdd != null && !usersToAdd.isEmpty()) { + data.put("add_user_list", usersToAdd); + } + if (usersToDelete != null && !usersToDelete.isEmpty()) { + data.put("del_user_list", usersToDelete); + } + + final String url = this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_UPDATE); + this.cpService.post(url, WxGsonBuilder.create().toJson(data)); + } + + @Override + public WxCpChat get(String chatId) throws WxErrorException { + final String url = this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_GET_CHATID + chatId); + String result = this.cpService.get(url, null); + final String chatInfo = GsonParser.parse(result).getAsJsonObject("chat_info").toString(); + return WxCpGsonBuilder.create().fromJson(chatInfo, WxCpChat.class); + } + + @Override + public void sendMsg(WxCpAppChatMessage message) throws WxErrorException { + this.cpService.post(this.cpService.getWxCpConfigStorage().getApiUrl(APPCHAT_SEND), message.toJson()); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java new file mode 100644 index 0000000000..3a5ef87985 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpDepartmentService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpDepart; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Department.*; + +/** + *
    + *  部门管理接口
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { + private final WxCpService mainService; + + @Override + public Long create(WxCpDepart depart) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_CREATE); + String responseContent = this.mainService.post(url, depart.toJson()); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + return GsonHelper.getAsLong(tmpJsonObject.get("id")); + } + + @Override + public void update(WxCpDepart group) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_UPDATE); + this.mainService.post(url, group.toJson()); + } + + @Override + public void delete(Long departId) throws WxErrorException { + String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_DELETE), departId); + this.mainService.get(url, null); + } + + @Override + public List list(Long id) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST); + if (id != null) { + url += "?id=" + id; + } + + String responseContent = this.mainService.get(url, null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + return WxCpGsonBuilder.create() + .fromJson(tmpJsonObject.get("department"), + new TypeToken>() { + }.getType() + ); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java new file mode 100644 index 0000000000..b64ec0e870 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java @@ -0,0 +1,298 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxCpErrorMsgEnum; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpExternalContactService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.*; +import me.chanjar.weixin.cp.bean.external.*; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.ExternalContact.*; + +/** + * @author 曹祖鹏 & yuanqixun + */ +@RequiredArgsConstructor +public class WxCpExternalContactServiceImpl implements WxCpExternalContactService { + private final WxCpService mainService; + + @Override + public WxCpContactWayResult addContactWay(@NonNull WxCpContactWayInfo info) throws WxErrorException { + + if (info.getContactWay().getUsers() != null && info.getContactWay().getUsers().size() > 100) { + throw new RuntimeException("「联系我」使用人数默认限制不超过100人(包括部门展开后的人数)"); + } + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(ADD_CONTACT_WAY); + String responseContent = this.mainService.post(url, info.getContactWay().toJson()); + + return WxCpContactWayResult.fromJson(responseContent); + } + + @Override + public WxCpContactWayInfo getContactWay(@NonNull String configId) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("config_id", configId); + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CONTACT_WAY); + String responseContent = this.mainService.post(url, json.toString()); + return WxCpContactWayInfo.fromJson(responseContent); + } + + @Override + public WxCpBaseResp updateContactWay(@NonNull WxCpContactWayInfo info) throws WxErrorException { + if (StringUtils.isBlank(info.getContactWay().getConfigId())) { + throw new RuntimeException("更新「联系我」方式需要指定configId"); + } + if (info.getContactWay().getUsers() != null && info.getContactWay().getUsers().size() > 100) { + throw new RuntimeException("「联系我」使用人数默认限制不超过100人(包括部门展开后的人数)"); + } + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPDATE_CONTACT_WAY); + String responseContent = this.mainService.post(url, info.getContactWay().toJson()); + + return WxCpBaseResp.fromJson(responseContent); + } + + @Override + public WxCpBaseResp deleteContactWay(@NonNull String configId) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("config_id",configId); + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEL_CONTACT_WAY); + String responseContent = this.mainService.post(url, json.toString()); + + return WxCpBaseResp.fromJson(responseContent); + } + + @Override + public WxCpBaseResp closeTempChat(@NonNull String userId, @NonNull String externalUserId) throws WxErrorException { + + JsonObject json = new JsonObject(); + json.addProperty("userid",userId); + json.addProperty("external_userid",externalUserId); + + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(CLOSE_TEMP_CHAT); + String responseContent = this.mainService.post(url, json.toString()); + + return WxCpBaseResp.fromJson(responseContent); + } + + @Override + public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId); + String responseContent = this.mainService.get(url, null); + return WxCpUserExternalContactInfo.fromJson(responseContent); + } + + @Override + public WxCpUserExternalContactInfo getContactDetail(String userId) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CONTACT_DETAIL + userId); + String responseContent = this.mainService.get(url, null); + return WxCpUserExternalContactInfo.fromJson(responseContent); + } + + @Override + public List listExternalContacts(String userId) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(LIST_EXTERNAL_CONTACT + userId); + try { + String responseContent = this.mainService.get(url, null); + return WxCpUserExternalContactList.fromJson(responseContent).getExternalUserId(); + } catch (WxErrorException e) { + // not external contact,无客户则返回空列表 + if (e.getError().getErrorCode() == WxCpErrorMsgEnum.CODE_84061.getCode()) { + return Collections.emptyList(); + } + throw e; + } + } + + @Override + public List listFollowers() throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_FOLLOW_USER_LIST); + String responseContent = this.mainService.get(url, null); + return WxCpUserWithExternalPermission.fromJson(responseContent).getFollowers(); + } + + @Override + public WxCpUserExternalUnassignList listUnassignedList(Integer pageIndex, Integer pageSize) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("page_id", pageIndex == null ? 0 : pageIndex); + json.addProperty("page_size", pageSize == null ? 100 : pageSize); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(LIST_UNASSIGNED_CONTACT); + final String result = this.mainService.post(url, json.toString()); + return WxCpUserExternalUnassignList.fromJson(result); + } + + @Override + public WxCpBaseResp transferExternalContact(String externalUserid, String handOverUserid, String takeOverUserid) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("external_userid", externalUserid); + json.addProperty("handover_userid", handOverUserid); + json.addProperty("takeover_userid", takeOverUserid); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(TRANSFER_UNASSIGNED_CONTACT); + final String result = this.mainService.post(url, json.toString()); + return WxCpBaseResp.fromJson(result); + } + + @Override + public WxCpUserExternalGroupChatList listGroupChat(Integer pageIndex, Integer pageSize, int status, String[] userIds, String[] partyIds) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("offset", pageIndex == null ? 0 : pageIndex); + json.addProperty("limit", pageSize == null ? 100 : pageSize); + json.addProperty("status_filter", status); + if (ArrayUtils.isNotEmpty(userIds) || ArrayUtils.isNotEmpty(partyIds)) { + JsonObject ownerFilter = new JsonObject(); + if (ArrayUtils.isNotEmpty(userIds)) { + json.add("userid_list", new Gson().toJsonTree(userIds).getAsJsonArray()); + } + if (ArrayUtils.isNotEmpty(partyIds)) { + json.add("partyid_list", new Gson().toJsonTree(partyIds).getAsJsonArray()); + } + json.add("owner_filter", ownerFilter); + } + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GROUP_CHAT_LIST); + final String result = this.mainService.post(url, json.toString()); + return WxCpUserExternalGroupChatList.fromJson(result); + } + + @Override + public WxCpUserExternalGroupChatInfo getGroupChat(String chatId) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("chat_id", chatId); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GROUP_CHAT_INFO); + final String result = this.mainService.post(url, json.toString()); + return WxCpUserExternalGroupChatInfo.fromJson(result); + } + + @Override + public WxCpUserExternalUserBehaviorStatistic getUserBehaviorStatistic(Date startTime, Date endTime, String[] userIds, String[] partyIds) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("start_time", startTime.getTime() / 1000); + json.addProperty("end_time", endTime.getTime() / 1000); + if (ArrayUtils.isNotEmpty(userIds) || ArrayUtils.isNotEmpty(partyIds)) { + if (ArrayUtils.isNotEmpty(userIds)) { + json.add("userid", new Gson().toJsonTree(userIds).getAsJsonArray()); + } + if (ArrayUtils.isNotEmpty(partyIds)) { + json.add("partyid", new Gson().toJsonTree(partyIds).getAsJsonArray()); + } + } + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(LIST_USER_BEHAVIOR_DATA); + final String result = this.mainService.post(url, json.toString()); + return WxCpUserExternalUserBehaviorStatistic.fromJson(result); + } + + @Override + public WxCpUserExternalGroupChatStatistic getGroupChatStatistic(Date startTime, Integer orderBy, Integer orderAsc, Integer pageIndex, Integer pageSize, String[] userIds, String[] partyIds) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("day_begin_time", startTime.getTime() / 1000); + json.addProperty("order_by", orderBy == null ? 1 : orderBy); + json.addProperty("order_asc", orderAsc == null ? 0 : orderAsc); + json.addProperty("offset", pageIndex == null ? 0 : pageIndex); + json.addProperty("limit", pageSize == null ? 500 : pageSize); + if (ArrayUtils.isNotEmpty(userIds) || ArrayUtils.isNotEmpty(partyIds)) { + JsonObject ownerFilter = new JsonObject(); + if (ArrayUtils.isNotEmpty(userIds)) { + json.add("userid_list", new Gson().toJsonTree(userIds).getAsJsonArray()); + } + if (ArrayUtils.isNotEmpty(partyIds)) { + json.add("partyid_list", new Gson().toJsonTree(partyIds).getAsJsonArray()); + } + json.add("owner_filter", ownerFilter); + } + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(LIST_GROUP_CHAT_DATA); + final String result = this.mainService.post(url, json.toString()); + return WxCpUserExternalGroupChatStatistic.fromJson(result); + } + + @Override + public WxCpMsgTemplateAddResult addMsgTemplate(WxCpMsgTemplate wxCpMsgTemplate) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(ADD_MSG_TEMPLATE); + final String result = this.mainService.post(url, wxCpMsgTemplate.toJson()); + return WxCpMsgTemplateAddResult.fromJson(result); + } + + @Override + public void sendWelcomeMsg(WxCpWelcomeMsg msg) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(SEND_WELCOME_MSG); + this.mainService.post(url, msg.toJson()); + } + + @Override + public WxCpUserExternalTagGroupList getCorpTagList(String[] tagId) throws WxErrorException { + JsonObject json = new JsonObject(); + if(ArrayUtils.isNotEmpty(tagId)){ + json.add("tag_id",new Gson().toJsonTree(tagId).getAsJsonArray()); + } + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CORP_TAG_LIST); + final String result = this.mainService.post(url,json.toString()); + return WxCpUserExternalTagGroupList.fromJson(result); + } + + @Override + public WxCpUserExternalTagGroupInfo addCorpTag(WxCpUserExternalTagGroupInfo tagGroup) throws WxErrorException{ + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(ADD_CORP_TAG); + final String result = this.mainService.post(url,tagGroup.getTagGroup().toJson()); + return WxCpUserExternalTagGroupInfo.fromJson(result); + } + + @Override + public WxCpBaseResp editCorpTag(String id, String name, Integer order) throws WxErrorException{ + + JsonObject json = new JsonObject(); + json.addProperty("id",id); + json.addProperty("name",name); + json.addProperty("order",order); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(EDIT_CORP_TAG); + final String result = this.mainService.post(url,json.toString()); + return WxCpBaseResp.fromJson(result); + } + + @Override + public WxCpBaseResp delCorpTag(String[] tagId, String[] groupId) throws WxErrorException{ + JsonObject json = new JsonObject(); + if(ArrayUtils.isNotEmpty(tagId)){ + json.add("tag_id",new Gson().toJsonTree(tagId).getAsJsonArray()); + } + if(ArrayUtils.isNotEmpty(groupId)){ + json.add("group_id",new Gson().toJsonTree(groupId).getAsJsonArray()); + } + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEL_CORP_TAG); + final String result = this.mainService.post(url,json.toString()); + return WxCpBaseResp.fromJson(result); + } + + @Override + public WxCpBaseResp markTag(String userid, String externalUserid, String[] addTag, String[] removeTag)throws WxErrorException{ + + + JsonObject json = new JsonObject(); + json.addProperty("userid",userid); + json.addProperty("external_userid",externalUserid); + + if(ArrayUtils.isNotEmpty(addTag)){ + json.add("add_tag",new Gson().toJsonTree(addTag).getAsJsonArray()); + } + if(ArrayUtils.isNotEmpty(removeTag)){ + json.add("remove_tag",new Gson().toJsonTree(removeTag).getAsJsonArray()); + } + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(MARK_TAG); + final String result = this.mainService.post(url,json.toString()); + return WxCpBaseResp.fromJson(result); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImpl.java new file mode 100644 index 0000000000..ed4d8a108e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImpl.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpGroupRobotService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpGroupRobotMessage; +import me.chanjar.weixin.cp.bean.article.NewArticle; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; + +import java.util.List; + +/** + * 微信群机器人消息发送api 实现 + * + * @author yr + * @date 2020-08-20 + */ +@RequiredArgsConstructor +public class WxCpGroupRobotServiceImpl implements WxCpGroupRobotService { + private final WxCpService cpService; + + private String getApiUrl() { + WxCpConfigStorage wxCpConfigStorage = cpService.getWxCpConfigStorage(); + return wxCpConfigStorage.getApiUrl(WxCpApiPathConsts.WEBHOOK_SEND) + wxCpConfigStorage.getWebhookKey(); + } + + @Override + public void sendText(String content, List mentionedList, List mobileList) throws WxErrorException { + WxCpGroupRobotMessage message = new WxCpGroupRobotMessage() + .setMsgType(WxConsts.GroupRobotMsgType.TEXT) + .setContent(content) + .setMentionedList(mentionedList) + .setMentionedMobileList(mobileList); + cpService.postWithoutToken(this.getApiUrl(), message.toJson()); + } + + @Override + public void sendMarkDown(String content) throws WxErrorException { + WxCpGroupRobotMessage message = new WxCpGroupRobotMessage() + .setMsgType(WxConsts.GroupRobotMsgType.MARKDOWN) + .setContent(content); + cpService.postWithoutToken(this.getApiUrl(), message.toJson()); + } + + @Override + public void sendImage(String base64, String md5) throws WxErrorException { + WxCpGroupRobotMessage message = new WxCpGroupRobotMessage() + .setMsgType(WxConsts.GroupRobotMsgType.IMAGE) + .setBase64(base64) + .setMd5(md5); + cpService.postWithoutToken(this.getApiUrl(), message.toJson()); + } + + @Override + public void sendNews(List articleList) throws WxErrorException { + WxCpGroupRobotMessage message = new WxCpGroupRobotMessage() + .setMsgType(WxConsts.GroupRobotMsgType.NEWS) + .setArticles(articleList); + cpService.postWithoutToken(this.getApiUrl(), message.toJson()); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java new file mode 100644 index 0000000000..b83b6d39ab --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.cp.api.WxCpMediaService; +import me.chanjar.weixin.cp.api.WxCpService; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*; + +/** + *
    + * 媒体管理接口.
    + * Created by Binary Wang on 2017-6-25.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxCpMediaServiceImpl implements WxCpMediaService { + private final WxCpService mainService; + + @Override + public WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) + throws WxErrorException, IOException { + return this.upload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + } + + @Override + public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { + return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), + this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType), file); + } + + @Override + public File download(String mediaId) throws WxErrorException { + return this.mainService.execute( + BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), + this.mainService.getWxCpConfigStorage().getTmpDirFile()), + this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_GET), "media_id=" + mediaId); + } + + @Override + public File getJssdkFile(String mediaId) throws WxErrorException { + return this.mainService.execute( + BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), + this.mainService.getWxCpConfigStorage().getTmpDirFile()), + this.mainService.getWxCpConfigStorage().getApiUrl(JSSDK_MEDIA_GET), "media_id=" + mediaId); + } + + @Override + public String uploadImg(File file) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(IMG_UPLOAD); + return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), url, file) + .getUrl(); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java new file mode 100644 index 0000000000..d008e77083 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpMenuService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Menu.*; + +/** + *
    + * 菜单管理相关接口.
    + * Created by Binary Wang on 2017-6-25.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxCpMenuServiceImpl implements WxCpMenuService { + private final WxCpService mainService; + + @Override + public void create(WxMenu menu) throws WxErrorException { + this.create(this.mainService.getWxCpConfigStorage().getAgentId(), menu); + } + + @Override + public void create(Integer agentId, WxMenu menu) throws WxErrorException { + String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_CREATE), agentId); + this.mainService.post(url, menu.toJson()); + } + + @Override + public void delete() throws WxErrorException { + this.delete(this.mainService.getWxCpConfigStorage().getAgentId()); + } + + @Override + public void delete(Integer agentId) throws WxErrorException { + String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_DELETE), agentId); + this.mainService.get(url, null); + } + + @Override + public WxMenu get() throws WxErrorException { + return this.get(this.mainService.getWxCpConfigStorage().getAgentId()); + } + + @Override + public WxMenu get(Integer agentId) throws WxErrorException { + String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(MENU_GET), agentId); + try { + String resultContent = this.mainService.get(url, null); + return WxCpGsonBuilder.create().fromJson(resultContent, WxMenu.class); + } catch (WxErrorException e) { + // 46003 不存在的菜单数据 + if (e.getError().getErrorCode() == 46003) { + return null; + } + throw e; + } + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java new file mode 100644 index 0000000000..f271149ee9 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java @@ -0,0 +1,89 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.URIUtil; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpOAuth2Service; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; +import me.chanjar.weixin.cp.bean.WxCpUserDetail; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.*; +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.OAuth2.*; + +/** + *
    + * oauth2相关接口实现类.
    + * Created by Binary Wang on 2017-6-25.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { + private final WxCpService mainService; + + @Override + public String buildAuthorizationUrl(String state) { + return this.buildAuthorizationUrl( + this.mainService.getWxCpConfigStorage().getOauth2redirectUri(), + state + ); + } + + @Override + public String buildAuthorizationUrl(String redirectUri, String state) { + return this.buildAuthorizationUrl(redirectUri, state, SNSAPI_BASE); + } + + @Override + public String buildAuthorizationUrl(String redirectUri, String state, String scope) { + StringBuilder url = new StringBuilder(URL_OAUTH2_AUTHORIZE); + url.append("?appid=").append(this.mainService.getWxCpConfigStorage().getCorpId()); + url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectUri)); + url.append("&response_type=code"); + url.append("&scope=").append(scope); + + if (SNSAPI_PRIVATEINFO.equals(scope) || SNSAPI_USERINFO.equals(scope)) { + url.append("&agentid=").append(this.mainService.getWxCpConfigStorage().getAgentId()); + } + + if (state != null) { + url.append("&state=").append(state); + } + + url.append("#wechat_redirect"); + return url.toString(); + } + + @Override + public WxCpOauth2UserInfo getUserInfo(String code) throws WxErrorException { + return this.getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code); + } + + @Override + public WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErrorException { + String responseText = this.mainService.get(String.format(this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_INFO), code, agentId), null); + JsonObject jo = GsonParser.parse(responseText); + + return WxCpOauth2UserInfo.builder() + .userId(GsonHelper.getString(jo, "UserId")) + .deviceId(GsonHelper.getString(jo, "DeviceId")) + .openId(GsonHelper.getString(jo, "OpenId")) + .userTicket(GsonHelper.getString(jo, "user_ticket")) + .expiresIn(GsonHelper.getString(jo, "expires_in")) + .build(); + } + + @Override + public WxCpUserDetail getUserDetail(String userTicket) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("user_ticket", userTicket); + String responseText = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_DETAIL), param.toString()); + return WxCpGsonBuilder.create().fromJson(responseText, WxCpUserDetail.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java new file mode 100644 index 0000000000..17a2ba274d --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java @@ -0,0 +1,214 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpOaService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.oa.*; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.Date; +import java.util.List; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Oa.*; + +/** + * 企业微信 OA 接口实现 + * + * @author Element + * @date 2019-04-06 11:20 + */ +@RequiredArgsConstructor +public class WxCpOaServiceImpl implements WxCpOaService { + private final WxCpService mainService; + + private static final int MONTH_SECONDS = 30 * 24 * 60 * 60; + private static final int USER_IDS_LIMIT = 100; + + @Override + public String apply(WxCpOaApplyEventRequest request) throws WxErrorException { + String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(APPLY_EVENT), + request.toJson()); + return GsonParser.parse(responseContent).get("sp_no").getAsString(); + } + + @Override + public List getCheckinData(Integer openCheckinDataType, Date startTime, Date endTime, + List userIdList) throws WxErrorException { + if (startTime == null || endTime == null) { + throw new RuntimeException("starttime and endtime can't be null"); + } + + if (userIdList == null || userIdList.size() > USER_IDS_LIMIT) { + throw new RuntimeException("用户列表不能为空,不超过 " + USER_IDS_LIMIT + " 个,若用户超过 " + USER_IDS_LIMIT + " 个,请分批获取"); + } + + long endTimestamp = endTime.getTime() / 1000L; + long startTimestamp = startTime.getTime() / 1000L; + + if (endTimestamp - startTimestamp < 0 || endTimestamp - startTimestamp >= MONTH_SECONDS) { + throw new RuntimeException("获取记录时间跨度不超过一个月"); + } + + JsonObject jsonObject = new JsonObject(); + JsonArray jsonArray = new JsonArray(); + + jsonObject.addProperty("opencheckindatatype", openCheckinDataType); + jsonObject.addProperty("starttime", startTimestamp); + jsonObject.addProperty("endtime", endTimestamp); + + for (String userid : userIdList) { + jsonArray.add(userid); + } + + jsonObject.add("useridlist", jsonArray); + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CHECKIN_DATA); + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonObject tmpJson = GsonParser.parse(responseContent); + return WxCpGsonBuilder.create() + .fromJson( + tmpJson.get("checkindata"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List getCheckinOption(Date datetime, List userIdList) throws WxErrorException { + if (datetime == null) { + throw new RuntimeException("datetime can't be null"); + } + + if (userIdList == null || userIdList.size() > USER_IDS_LIMIT) { + throw new RuntimeException("用户列表不能为空,不超过 " + USER_IDS_LIMIT + " 个,若用户超过 " + USER_IDS_LIMIT + " 个,请分批获取"); + } + + JsonArray jsonArray = new JsonArray(); + for (String userid : userIdList) { + jsonArray.add(userid); + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("datetime", datetime.getTime() / 1000L); + jsonObject.add("useridlist", jsonArray); + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CHECKIN_OPTION); + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonObject tmpJson = GsonParser.parse(responseContent); + + return WxCpGsonBuilder.create() + .fromJson( + tmpJson.get("info"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime, + Integer cursor, Integer size, List filters) throws WxErrorException { + + if (cursor == null) { + cursor = 0; + } + + if (size == null) { + size = 100; + } + + if (size < 0 || size > 100) { + throw new IllegalArgumentException("size参数错误,请使用[1-100]填充,默认100"); + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("starttime", startTime.getTime() / 1000L); + jsonObject.addProperty("endtime", endTime.getTime() / 1000L); + jsonObject.addProperty("size", size); + jsonObject.addProperty("cursor", cursor); + + if (filters != null && !filters.isEmpty()) { + JsonArray filterJsonArray = new JsonArray(); + for (WxCpApprovalInfoQueryFilter filter : filters) { + filterJsonArray.add(new JsonParser().parse(filter.toJson())); + } + jsonObject.add("filters", filterJsonArray); + } + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_APPROVAL_INFO); + String responseContent = this.mainService.post(url, jsonObject.toString()); + + return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalInfo.class); + } + + @Override + public WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime) throws WxErrorException { + return this.getApprovalInfo(startTime, endTime, null, null, null); + } + + @Override + public WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo) throws WxErrorException { + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("sp_no", spNo); + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_APPROVAL_DETAIL); + String responseContent = this.mainService.post(url, jsonObject.toString()); + + return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDetailResult.class); + } + + @Override + public List getDialRecord(Date startTime, Date endTime, Integer offset, Integer limit) + throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + + if (offset == null) { + offset = 0; + } + + if (limit == null || limit <= 0) { + limit = 100; + } + + jsonObject.addProperty("offset", offset); + jsonObject.addProperty("limit", limit); + + if (startTime != null && endTime != null) { + + long endtimestamp = endTime.getTime() / 1000L; + long starttimestamp = startTime.getTime() / 1000L; + + if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= MONTH_SECONDS) { + throw new RuntimeException("受限于网络传输,起止时间的最大跨度为30天,如超过30天,则以结束时间为基准向前取30天进行查询"); + } + + jsonObject.addProperty("start_time", starttimestamp); + jsonObject.addProperty("end_time", endtimestamp); + } + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_DIAL_RECORD); + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonObject tmpJson = GsonParser.parse(responseContent); + + return WxCpGsonBuilder.create().fromJson(tmpJson.get("record"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public WxCpTemplateResult getTemplateDetail(@NonNull String templateId) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("template_id", templateId); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_TEMPLATE_DETAIL); + String responseContent = this.mainService.post(url, jsonObject.toString()); + return WxCpGsonBuilder.create().fromJson(responseContent, WxCpTemplateResult.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java new file mode 100644 index 0000000000..076d0205a5 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java @@ -0,0 +1,106 @@ +package me.chanjar.weixin.cp.api.impl; + + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * @author someone + */ +public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl { + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.APACHE_HTTP; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getAccessToken(); + } + + synchronized (this.globalAccessTokenRefreshLock) { + String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), this.configStorage.getCorpId(), this.configStorage.getCorpSecret()); + + try { + HttpGet httpGet = new HttpGet(url); + if (this.httpProxy != null) { + RequestConfig config = RequestConfig.custom() + .setProxy(this.httpProxy).build(); + httpGet.setConfig(config); + } + String resultContent; + try (CloseableHttpClient httpClient = getRequestHttpClient(); + CloseableHttpResponse response = httpClient.execute(httpGet)) { + resultContent = new BasicResponseHandler().handleResponse(response); + } finally { + httpGet.releaseConnection(); + } + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return this.configStorage.getAccessToken(); + } + + @Override + public void initHttp() { + ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage + .getApacheHttpClientBuilder(); + if (null == apacheHttpClientBuilder) { + apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); + } + + apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) + .httpProxyPort(this.configStorage.getHttpProxyPort()) + .httpProxyUsername(this.configStorage.getHttpProxyUsername()) + .httpProxyPassword(this.configStorage.getHttpProxyPassword()); + + if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + } + + @Override + public WxCpConfigStorage getWxCpConfigStorage() { + return this.configStorage; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java new file mode 100644 index 0000000000..e78432b5a2 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java @@ -0,0 +1,125 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; +import java.util.concurrent.locks.Lock; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_AGENT_CONFIG_TICKET; +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_JSAPI_TICKET; + +/** + *
    + *  默认接口实现类,使用apache httpclient实现
    + * Created by Binary Wang on 2017-5-27.
    + * 
    + *
    + * 增加分布式锁(基于WxCpConfigStorage实现)的支持
    + * Updated by yuanqixun on 2020-05-13
    + * 
    + * + * + * @author Binary Wang + */ +public class WxCpServiceImpl extends WxCpServiceApacheHttpClientImpl { + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!getWxCpConfigStorage().isAccessTokenExpired() && !forceRefresh) { + return getWxCpConfigStorage().getAccessToken(); + } + Lock lock = getWxCpConfigStorage().getAccessTokenLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (!getWxCpConfigStorage().isAccessTokenExpired() && !forceRefresh) { + return getWxCpConfigStorage().getAccessToken(); + } + String url = String.format(getWxCpConfigStorage().getApiUrl(WxCpApiPathConsts.GET_TOKEN), this.configStorage.getCorpId(), this.configStorage.getCorpSecret()); + try { + HttpGet httpGet = new HttpGet(url); + if (getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom() + .setProxy(getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + String resultContent; + try (CloseableHttpClient httpClient = getRequestHttpClient(); + CloseableHttpResponse response = httpClient.execute(httpGet)) { + resultContent = new BasicResponseHandler().handleResponse(response); + } finally { + httpGet.releaseConnection(); + } + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + getWxCpConfigStorage().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } finally { + lock.unlock(); + } + return getWxCpConfigStorage().getAccessToken(); + } + + @Override + public String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + getWxCpConfigStorage().expireAgentJsapiTicket(); + } + if (getWxCpConfigStorage().isAgentJsapiTicketExpired()) { + Lock lock = getWxCpConfigStorage().getAgentJsapiTicketLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (getWxCpConfigStorage().isAgentJsapiTicketExpired()) { + String responseContent = this.get(getWxCpConfigStorage().getApiUrl(GET_AGENT_CONFIG_TICKET), null); + JsonObject jsonObject = GsonParser.parse(responseContent); + getWxCpConfigStorage().updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(), + jsonObject.get("expires_in").getAsInt()); + } + } finally { + lock.unlock(); + } + } + return getWxCpConfigStorage().getAgentJsapiTicket(); + } + + @Override + public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + getWxCpConfigStorage().expireJsapiTicket(); + } + + if (getWxCpConfigStorage().isJsapiTicketExpired()) { + Lock lock = getWxCpConfigStorage().getJsapiTicketLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (getWxCpConfigStorage().isJsapiTicketExpired()) { + String responseContent = this.get(getWxCpConfigStorage().getApiUrl(GET_JSAPI_TICKET), null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + getWxCpConfigStorage().updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(), + tmpJsonObject.get("expires_in").getAsInt()); + } + } finally { + lock.unlock(); + } + } + return getWxCpConfigStorage().getJsapiTicket(); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java new file mode 100644 index 0000000000..1b53630688 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java @@ -0,0 +1,78 @@ +package me.chanjar.weixin.cp.api.impl; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.http.net.SocketHttpConnectionProvider; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; + +/** + * @author someone + */ +public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl { + private HttpConnectionProvider httpClient; + private ProxyInfo httpProxy; + + @Override + public HttpConnectionProvider getRequestHttpClient() { + return httpClient; + } + + @Override + public ProxyInfo getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.JODD_HTTP; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getAccessToken(); + } + + synchronized (this.globalAccessTokenRefreshLock) { + HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), + this.configStorage.getCorpId(), this.configStorage.getCorpSecret())); + if (this.httpProxy != null) { + httpClient.useProxy(this.httpProxy); + } + request.withConnectionProvider(httpClient); + HttpResponse response = request.send(); + + String resultContent = response.bodyText(); + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } + return this.configStorage.getAccessToken(); + } + + @Override + public void initHttp() { + if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { + httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(), + configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); + } + + httpClient = new SocketHttpConnectionProvider(); + } + + @Override + public WxCpConfigStorage getWxCpConfigStorage() { + return this.configStorage; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java new file mode 100644 index 0000000000..5fb5a73756 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java @@ -0,0 +1,106 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import okhttp3.*; + +import java.io.IOException; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_TOKEN; + +/** + * @author someone + */ +@Slf4j +public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl { + private OkHttpClient httpClient; + private OkHttpProxyInfo httpProxy; + + @Override + public OkHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public OkHttpProxyInfo getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.OK_HTTP; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getAccessToken(); + } + + synchronized (this.globalAccessTokenRefreshLock) { + //得到httpClient + OkHttpClient client = getRequestHttpClient(); + //请求的request + Request request = new Request.Builder() + .url(String.format(this.configStorage.getApiUrl(GET_TOKEN), this.configStorage.getCorpId(), this.configStorage.getCorpSecret())) + .get() + .build(); + String resultContent = null; + try { + Response response = client.newCall(request).execute(); + resultContent = response.body().string(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateAccessToken(accessToken.getAccessToken(), + accessToken.getExpiresIn()); + } + return this.configStorage.getAccessToken(); + } + + @Override + public void initHttp() { + log.debug("WxCpServiceOkHttpImpl initHttp"); + //设置代理 + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + httpProxy = OkHttpProxyInfo.httpProxy(configStorage.getHttpProxyHost(), + configStorage.getHttpProxyPort(), + configStorage.getHttpProxyUsername(), + configStorage.getHttpProxyPassword()); + } + + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + if (httpProxy != null) { + clientBuilder.proxy(getRequestHttpProxy().getProxy()); + + //设置授权 + clientBuilder.authenticator(new Authenticator() { + @Override + public Request authenticate(Route route, Response response) throws IOException { + String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); + return response.request().newBuilder() + .header("Authorization", credential) + .build(); + } + }); + } + httpClient = clientBuilder.build(); + } + + @Override + public WxCpConfigStorage getWxCpConfigStorage() { + return this.configStorage; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOnTpImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOnTpImpl.java new file mode 100644 index 0000000000..35eab626a7 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOnTpImpl.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpTpService; + +/** + *
    + *  默认接口实现类,使用apache httpclient实现,配合第三方应用service使用
    + * Created by zhenjun cai.
    + * 
    + * + * @author zhenjun cai + */ +@RequiredArgsConstructor +public class WxCpServiceOnTpImpl extends WxCpServiceApacheHttpClientImpl { + private final WxCpTpService wxCpTpService; + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getAccessToken(); + } + //access token通过第三方应用service获取 + //corpSecret对应企业永久授权码 + WxAccessToken accessToken = wxCpTpService.getCorpToken(this.configStorage.getCorpId(), this.configStorage.getCorpSecret()); + + this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + return this.configStorage.getAccessToken(); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java new file mode 100644 index 0000000000..0e079160fb --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java @@ -0,0 +1,139 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.WxCpTagService; +import me.chanjar.weixin.cp.bean.WxCpTag; +import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult; +import me.chanjar.weixin.cp.bean.WxCpTagGetResult; +import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tag.*; + +/** + *
    + *  标签管理接口.
    + * Created by Binary Wang on 2017-6-25.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxCpTagServiceImpl implements WxCpTagService { + private final WxCpService mainService; + + @Override + public String create(String name, Integer id) throws WxErrorException { + JsonObject o = new JsonObject(); + o.addProperty("tagname", name); + + if (id != null) { + o.addProperty("tagid", id); + } + return this.create(o); + } + + private String create(JsonObject param) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_CREATE); + String responseContent = this.mainService.post(url, param.toString()); + JsonObject jsonObject = GsonParser.parse(responseContent); + return jsonObject.get("tagid").getAsString(); + } + + @Override + public void update(String tagId, String tagName) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_UPDATE); + JsonObject o = new JsonObject(); + o.addProperty("tagid", tagId); + o.addProperty("tagname", tagName); + this.mainService.post(url, o.toString()); + } + + @Override + public void delete(String tagId) throws WxErrorException { + String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_DELETE), tagId); + this.mainService.get(url, null); + } + + @Override + public List listAll() throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_LIST); + String responseContent = this.mainService.get(url, null); + JsonObject tmpJson = GsonParser.parse(responseContent); + return WxCpGsonBuilder.create() + .fromJson( + tmpJson.get("taglist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List listUsersByTagId(String tagId) throws WxErrorException { + String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_GET), tagId); + String responseContent = this.mainService.get(url, null); + JsonObject tmpJson = GsonParser.parse(responseContent); + return WxCpGsonBuilder.create() + .fromJson( + tmpJson.get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public WxCpTagAddOrRemoveUsersResult addUsers2Tag(String tagId, List userIds, List partyIds) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_ADD_TAG_USERS); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("tagid", tagId); + this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); + + return WxCpTagAddOrRemoveUsersResult.fromJson(this.mainService.post(url, jsonObject.toString())); + } + + @Override + public WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List userIds, List partyIds) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(TAG_DEL_TAG_USERS); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("tagid", tagId); + this.addUserIdsAndPartyIdsToJson(userIds, partyIds, jsonObject); + + return WxCpTagAddOrRemoveUsersResult.fromJson(this.mainService.post(url, jsonObject.toString())); + } + + private void addUserIdsAndPartyIdsToJson(List userIds, List partyIds, JsonObject jsonObject) { + if (userIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : userIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("userlist", jsonArray); + } + + if (partyIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : partyIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("partylist", jsonArray); + } + } + + @Override + public WxCpTagGetResult get(String tagId) throws WxErrorException { + if (tagId == null) { + throw new IllegalArgumentException("缺少tagId参数"); + } + + String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(TAG_GET), tagId); + String responseContent = this.mainService.get(url, null); + return WxCpTagGetResult.fromJson(responseContent); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java new file mode 100644 index 0000000000..3993960642 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.WxCpTaskCardService; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.TaskCard.*; + +/** + *
    + *  任务卡片管理接口.
    + *  Created by Jeff on 2019-05-16.
    + * 
    + * + * @author Jeff + * @date 2019-05-16 + */ +@RequiredArgsConstructor +public class WxCpTaskCardServiceImpl implements WxCpTaskCardService { + private final WxCpService mainService; + + @Override + public void update(List userIds, String taskId, String clickedKey) throws WxErrorException { + Integer agentId = this.mainService.getWxCpConfigStorage().getAgentId(); + + Map data = new HashMap<>(4); + data.put("userids", userIds); + data.put("agentid", agentId); + data.put("task_id", taskId); + data.put("clicked_key", clickedKey); + + String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPDATE_TASK_CARD); + this.mainService.post(url, WxGsonBuilder.create().toJson(data)); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceApacheHttpClientImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceApacheHttpClientImpl.java new file mode 100644 index 0000000000..cdc6b2cfc0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceApacheHttpClientImpl.java @@ -0,0 +1,114 @@ +package me.chanjar.weixin.cp.api.impl; + + +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; +import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; +import org.apache.http.Consts; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * @author someone + */ +public class WxCpTpServiceApacheHttpClientImpl extends BaseWxCpTpServiceImpl { + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.APACHE_HTTP; + } + + @Override + public String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isSuiteAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getSuiteAccessToken(); + } + + synchronized (this.globalSuiteAccessTokenRefreshLock) { + try { + HttpPost httpPost = new HttpPost(configStorage.getApiUrl(WxCpApiPathConsts.Tp.GET_SUITE_TOKEN)); + if (this.httpProxy != null) { + RequestConfig config = RequestConfig.custom() + .setProxy(this.httpProxy).build(); + httpPost.setConfig(config); + } + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("suite_id", this.configStorage.getSuiteId()); + jsonObject.addProperty("suite_secret", this.configStorage.getSuiteSecret()); + jsonObject.addProperty("suite_ticket", this.getSuiteTicket()); + StringEntity entity = new StringEntity(jsonObject.toString(), Consts.UTF_8); + httpPost.setEntity(entity); + + String resultContent; + try (CloseableHttpClient httpclient = getRequestHttpClient(); + CloseableHttpResponse response = httpclient.execute(httpPost)) { + resultContent = new BasicResponseHandler().handleResponse(response); + } finally { + httpPost.releaseConnection(); + } + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + jsonObject = GsonParser.parse(resultContent); + String suiteAccussToken = jsonObject.get("suite_access_token").getAsString(); + Integer expiresIn = jsonObject.get("expires_in").getAsInt(); + this.configStorage.updateSuiteAccessToken(suiteAccussToken, expiresIn); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return this.configStorage.getSuiteAccessToken(); + } + + @Override + public void initHttp() { + ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage.getApacheHttpClientBuilder(); + if (null == apacheHttpClientBuilder) { + apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); + } + + apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) + .httpProxyPort(this.configStorage.getHttpProxyPort()) + .httpProxyUsername(this.configStorage.getHttpProxyUsername()) + .httpProxyPassword(this.configStorage.getHttpProxyPassword()); + + if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + } + + @Override + public WxCpTpConfigStorage getWxCpTpConfigStorage() { + return this.configStorage; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceImpl.java new file mode 100644 index 0000000000..f5582021e7 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTpServiceImpl.java @@ -0,0 +1,12 @@ +package me.chanjar.weixin.cp.api.impl; + +/** + *
    + *  默认接口实现类,使用apache httpclient实现
    + * Created by zhenjun cai.
    + * 
    + * + * @author zhenjun cai + */ +public class WxCpTpServiceImpl extends WxCpTpServiceApacheHttpClientImpl { +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java new file mode 100644 index 0000000000..d6d401624f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java @@ -0,0 +1,201 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.common.collect.Maps; +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.WxCpUserService; +import me.chanjar.weixin.cp.bean.WxCpInviteResult; +import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.cp.bean.external.WxCpUserExternalContactInfo; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; +import java.util.Map; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.User.*; + +/** + *
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxCpUserServiceImpl implements WxCpUserService { + private final WxCpService mainService; + + @Override + public void authenticate(String userId) throws WxErrorException { + this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(USER_AUTHENTICATE + userId), null); + } + + @Override + public void create(WxCpUser user) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CREATE); + this.mainService.post(url, user.toJson()); + } + + @Override + public void update(WxCpUser user) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_UPDATE); + this.mainService.post(url, user.toJson()); + } + + @Override + public void delete(String... userIds) throws WxErrorException { + if (userIds.length == 1) { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_DELETE + userIds[0]); + this.mainService.get(url, null); + return; + } + + JsonObject jsonObject = new JsonObject(); + JsonArray jsonArray = new JsonArray(); + for (String userId : userIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + + jsonObject.add("useridlist", jsonArray); + this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(USER_BATCH_DELETE), jsonObject.toString()); + } + + @Override + public WxCpUser getById(String userid) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_GET + userid); + String responseContent = this.mainService.get(url, null); + return WxCpUser.fromJson(responseContent); + } + + @Override + public List listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException { + String params = ""; + if (fetchChild != null) { + params += "&fetch_child=" + (fetchChild ? "1" : "0"); + } + if (status != null) { + params += "&status=" + status; + } else { + params += "&status=0"; + } + + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_LIST + departId); + String responseContent = this.mainService.get(url, params); + JsonObject jsonObject = GsonParser.parse(responseContent); + return WxCpGsonBuilder.create() + .fromJson(jsonObject.get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) + throws WxErrorException { + String params = ""; + if (fetchChild != null) { + params += "&fetch_child=" + (fetchChild ? "1" : "0"); + } + if (status != null) { + params += "&status=" + status; + } else { + params += "&status=0"; + } + + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_SIMPLE_LIST + departId); + String responseContent = this.mainService.get(url, params); + JsonObject tmpJson = GsonParser.parse(responseContent); + return WxCpGsonBuilder.create() + .fromJson( + tmpJson.get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public WxCpInviteResult invite(List userIds, List partyIds, List tagIds) + throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + if (userIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : userIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("user", jsonArray); + } + + if (partyIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : partyIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("party", jsonArray); + } + + if (tagIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String tagId : tagIds) { + jsonArray.add(new JsonPrimitive(tagId)); + } + jsonObject.add("tag", jsonArray); + } + + String url = this.mainService.getWxCpConfigStorage().getApiUrl(BATCH_INVITE); + return WxCpInviteResult.fromJson(this.mainService.post(url, jsonObject.toString())); + } + + @Override + public Map userId2Openid(String userId, Integer agentId) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_OPENID); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("userid", userId); + if (agentId != null) { + jsonObject.addProperty("agentid", agentId); + } + + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonObject tmpJson = GsonParser.parse(responseContent); + Map result = Maps.newHashMap(); + if (tmpJson.get("openid") != null) { + result.put("openid", tmpJson.get("openid").getAsString()); + } + + if (tmpJson.get("appid") != null) { + result.put("appid", tmpJson.get("appid").getAsString()); + } + + return result; + } + + @Override + public String openid2UserId(String openid) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("openid", openid); + String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_USERID); + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonObject tmpJson = GsonParser.parse(responseContent); + return tmpJson.get("userid").getAsString(); + } + + @Override + public String getUserId(String mobile) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("mobile", mobile); + String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_ID); + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonObject tmpJson = GsonParser.parse(responseContent); + return tmpJson.get("userid").getAsString(); + } + + @Override + public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException { + String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId); + String responseContent = this.mainService.get(url, null); + return WxCpUserExternalContactInfo.fromJson(responseContent); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/Gender.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/Gender.java new file mode 100644 index 0000000000..d56bd57da9 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/Gender.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.cp.bean; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *
    + *  性别枚举
    + *  Created by BinaryWang on 2018/4/22.
    + * 
    + * + * @author Binary Wang + */ +@Getter +@AllArgsConstructor +public enum Gender { + /** + * 未定义 + */ + UNDEFINED("未定义", "0"), + /** + * 男 + */ + MALE("男", "1"), + /** + * 女 + */ + FEMALE("女", "2"); + + private final String genderName; + private final String code; + + public static Gender fromCode(String code) { + for(Gender a: Gender.values()){ + if(a.code.equals(code)){ + return a; + } + } + + return null; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgent.java new file mode 100644 index 0000000000..18dfb346ce --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgent.java @@ -0,0 +1,107 @@ +package me.chanjar.weixin.cp.bean; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + *
    + * 企业号应用信息.
    + * Created by huansinho on 2018/4/13.
    + * 
    + * + * @author huansinho + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxCpAgent implements Serializable { + private static final long serialVersionUID = 5002894979081127234L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("agentid") + private Integer agentId; + + @SerializedName("name") + private String name; + + @SerializedName("square_logo_url") + private String squareLogoUrl; + + @SerializedName("logo_mediaid") + private String logoMediaId; + + @SerializedName("description") + private String description; + + @SerializedName("allow_userinfos") + private Users allowUserInfos; + + @SerializedName("allow_partys") + private Parties allowParties; + + @SerializedName("allow_tags") + private Tags allowTags; + + @SerializedName("close") + private Integer close; + + @SerializedName("redirect_domain") + private String redirectDomain; + + @SerializedName("report_location_flag") + private Integer reportLocationFlag; + + @SerializedName("isreportenter") + private Integer isReportEnter; + + @SerializedName("home_url") + private String homeUrl; + + public static WxCpAgent fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpAgent.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + @Data + public static class Users implements Serializable { + private static final long serialVersionUID = 8801100463558788565L; + @SerializedName("user") + private List users; + } + + @Data + public class User implements Serializable { + private static final long serialVersionUID = 7287632514385508024L; + @SerializedName("userid") + private String userId; + } + + @Data + public class Parties { + @SerializedName("partyid") + private List partyIds = null; + } + + @Data + public class Tags { + @SerializedName("tagid") + private List tagIds = null; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java new file mode 100644 index 0000000000..9aa2a2fc49 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java @@ -0,0 +1,196 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.article.MpnewsArticle; +import me.chanjar.weixin.cp.bean.article.NewArticle; +import me.chanjar.weixin.cp.constant.WxCpConsts.AppChatMsgType; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + * 应用推送消息
    + * Created by Binary Wang on 2019/1/26.
    + * 
    + * + * @author Binary Wang + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxCpAppChatMessage implements Serializable { + private static final long serialVersionUID = -5469013416372240229L; + + /** + * 消息类型 + */ + private String msgType; + /** + * 消息内容 + */ + private String content; + /** + * 群聊id + */ + private String chatId; + /** + * 图片媒体文件id,可以调用上传临时素材接口获取 + */ + private String mediaId; + /** + * 视频消息的标题,不超过128个字节,超过会自动截断 + */ + private String title; + /** + * 视频消息的描述,不超过512个字节,超过会自动截断 + */ + private String description; + /** + * 表示是否是保密消息 + */ + private Boolean safe; + /** + * 点击后跳转的链接。 + */ + private String url; + /** + * 按钮文字。 默认为“详情”, 不超过4个文字,超过自动截断。 + */ + private String btnTxt; + /** + * 图文消息,一个图文消息支持1到8条图文 + */ + private List articles; + /** + * Mpnews图文消息,一个图文消息支持1到8条图文 + */ + private List mpnewsArticles; + + /** + * 构建文本消息. + */ + public static WxCpAppChatMessage buildTextMsg(String chatId, String content, boolean safe) { + final WxCpAppChatMessage message = new WxCpAppChatMessage(); + message.setMsgType(AppChatMsgType.TEXT); + message.setContent(content); + message.setChatId(chatId); + message.setSafe(safe); + return message; + } + + /** + * 生成json字符串. + */ + public String toJson() { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("msgtype", this.getMsgType()); + messageJson.addProperty("chatid", this.getChatId()); + + if (this.getSafe() != null && this.getSafe()) { + messageJson.addProperty("safe", 1); + } + + this.handleMsgType(messageJson); + + return messageJson.toString(); + } + + private void handleMsgType(JsonObject messageJson) { + switch (this.getMsgType()) { + case AppChatMsgType.TEXT: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("text", text); + break; + } + case AppChatMsgType.MARKDOWN: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("markdown", text); + break; + } + case AppChatMsgType.TEXTCARD: { + JsonObject text = new JsonObject(); + text.addProperty("title", this.getTitle()); + text.addProperty("description", this.getDescription()); + text.addProperty("url", this.getUrl()); + text.addProperty("btntxt", this.getBtnTxt()); + messageJson.add("textcard", text); + break; + } + case AppChatMsgType.IMAGE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("image", image); + break; + } + case AppChatMsgType.FILE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("file", image); + break; + } + case AppChatMsgType.VOICE: { + JsonObject voice = new JsonObject(); + voice.addProperty("media_id", this.getMediaId()); + messageJson.add("voice", voice); + break; + } + case AppChatMsgType.VIDEO: { + JsonObject video = new JsonObject(); + video.addProperty("media_id", this.getMediaId()); + video.addProperty("title", this.getTitle()); + video.addProperty("description", this.getDescription()); + messageJson.add("video", video); + break; + } + case AppChatMsgType.NEWS: { + JsonObject newsJsonObject = new JsonObject(); + JsonArray articleJsonArray = new JsonArray(); + for (NewArticle article : this.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("description", article.getDescription()); + articleJson.addProperty("url", article.getUrl()); + articleJson.addProperty("picurl", article.getPicUrl()); + articleJsonArray.add(articleJson); + } + newsJsonObject.add("articles", articleJsonArray); + messageJson.add("news", newsJsonObject); + break; + } + case AppChatMsgType.MPNEWS: { + JsonObject newsJsonObject = new JsonObject(); + if (this.getMediaId() != null) { + newsJsonObject.addProperty("media_id", this.getMediaId()); + } else { + JsonArray articleJsonArray = new JsonArray(); + for (MpnewsArticle article : this.getMpnewsArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); + articleJson.addProperty("author", article.getAuthor()); + articleJson.addProperty("content_source_url", article.getContentSourceUrl()); + articleJson.addProperty("content", article.getContent()); + articleJson.addProperty("digest", article.getDigest()); + articleJsonArray.add(articleJson); + } + + newsJsonObject.add("articles", articleJsonArray); + } + messageJson.add("mpnews", newsJsonObject); + break; + } + default: { + //do nothing + } + } + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpBaseResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpBaseResp.java new file mode 100644 index 0000000000..e94110a055 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpBaseResp.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * @author yqx + * @date 2020/3/16 + */ +@Getter +@Setter +public class WxCpBaseResp { + @SerializedName("errcode") + protected Long errcode; + + @SerializedName("errmsg") + protected String errmsg; + + public boolean success() { + return getErrcode() == 0; + } + + public static WxCpBaseResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpBaseResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java new file mode 100644 index 0000000000..1f593e4746 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java @@ -0,0 +1,20 @@ +package me.chanjar.weixin.cp.bean; + +import java.util.List; + +import lombok.Data; + +/** + * 群聊 + * + * @author gaigeshen + */ +@Data +public class WxCpChat { + + private String id; + private String name; + private String owner; + private List users; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java index a679dffe23..f5b9b32592 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java @@ -2,68 +2,30 @@ import java.io.Serializable; +import lombok.Data; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; /** - * 微信部门 + * 企业微信的部门. * * @author Daniel Qian */ +@Data public class WxCpDepart implements Serializable { - private static final long serialVersionUID = -5028321625140879571L; - private Integer id; + + private Long id; private String name; - private Integer parentId; - private Integer order; + private String enName; + private Long parentId; + private Long order; public static WxCpDepart fromJson(String json) { return WxCpGsonBuilder.create().fromJson(json, WxCpDepart.class); } - public Integer getId() { - return this.id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getParentId() { - return this.parentId; - } - - public void setParentId(Integer parentId) { - this.parentId = parentId; - } - - public Integer getOrder() { - return this.order; - } - - public void setOrder(Integer order) { - this.order = order; - } - public String toJson() { return WxCpGsonBuilder.create().toJson(this); } - @Override - public String toString() { - return "WxCpDepart{" + - "id=" + this.id + - ", name='" + this.name + '\'' + - ", parentId=" + this.parentId + - ", order=" + this.order + - '}'; - } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpGroupRobotMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpGroupRobotMessage.java new file mode 100644 index 0000000000..937a88cb09 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpGroupRobotMessage.java @@ -0,0 +1,118 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.bean.article.NewArticle; + +import java.util.List; + +import static me.chanjar.weixin.common.api.WxConsts.GroupRobotMsgType.*; + +/** + * 微信群机器人消息 + * + * @author yr + * @date 2020-08-20 + */ +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = true) +@Data +public class WxCpGroupRobotMessage { + /** + * 消息类型 + */ + private String msgType; + + /** + * 文本内容,最长不超过2048个字节,markdown内容,最长不超过4096个字节,必须是utf8编码 + * 必填 + */ + private String content; + /** + * userid的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userid,可以使用mentioned_mobile_list + */ + private List mentionedList; + /** + * 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人 + */ + private List mentionedMobileList; + /** + * 图片内容的base64编码 + */ + private String base64; + /** + * 图片内容(base64编码前)的md5值 + */ + private String md5; + /** + * 图文消息,一个图文消息支持1到8条图文 + */ + private List articles; + + public String toJson() { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("msgtype", this.getMsgType()); + + switch (this.getMsgType()) { + case TEXT: { + JsonObject text = new JsonObject(); + JsonArray uidJsonArray = new JsonArray(); + JsonArray mobileJsonArray = new JsonArray(); + + text.addProperty("content", this.getContent()); + + if (this.getMentionedList() != null) { + for (String item : this.getMentionedList()) { + uidJsonArray.add(item); + } + } + if (this.getMentionedMobileList() != null) { + for (String item : this.getMentionedMobileList()) { + mobileJsonArray.add(item); + } + } + text.add("mentioned_list", uidJsonArray); + text.add("mentioned_mobile_list", mobileJsonArray); + messageJson.add("text", text); + break; + } + case MARKDOWN: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("markdown", text); + break; + } + case IMAGE: { + JsonObject text = new JsonObject(); + text.addProperty("base64", this.getBase64()); + text.addProperty("md5", this.getMd5()); + messageJson.add("image", text); + break; + } + case NEWS: { + JsonObject text = new JsonObject(); + JsonArray array = new JsonArray(); + for (NewArticle article : this.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("description", article.getDescription()); + articleJson.addProperty("url", article.getUrl()); + articleJson.addProperty("picurl", article.getPicUrl()); + array.add(articleJson); + } + text.add("articles", array); + messageJson.add("news", text); + break; + } + default: + + } + + return messageJson.toString(); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpInviteResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpInviteResult.java new file mode 100644 index 0000000000..f5a0a66bf1 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpInviteResult.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.cp.bean; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 邀请成员的结果对象类. + * Created by Binary Wang on 2018-5-13. + * + * @author Binary Wang + */ +@Data +public class WxCpInviteResult implements Serializable { + private static final long serialVersionUID = 1420065684270213578L; + + @Override + public String toString() { + return WxCpGsonBuilder.create().toJson(this); + } + + public static WxCpInviteResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpInviteResult.class); + } + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("invaliduser") + private String[] invalidUsers; + + @SerializedName("invalidparty") + private String[] invalidParties; + + @SerializedName("invalidtag") + private String[] invalidTags; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java new file mode 100644 index 0000000000..90f1ae840c --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + *
    + * 小程序登录凭证校验
    + * 文档地址:https://work.weixin.qq.com/api/doc#90000/90136/90289/wx.qy.login
    + * 
    + * @author Binary Wang + */ +@Data +public class WxCpMaJsCode2SessionResult implements Serializable { + private static final long serialVersionUID = 6229609023682814765L; + + @SerializedName("session_key") + private String sessionKey; + + @SerializedName("userid") + private String userId; + + @SerializedName("corpid") + private String corpId; + + public static WxCpMaJsCode2SessionResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpMaJsCode2SessionResult.class); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java index 324df20902..b39c2229e0 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java @@ -1,22 +1,31 @@ package me.chanjar.weixin.cp.bean; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.Data; +import me.chanjar.weixin.common.api.WxConsts.KefuMsgType; import me.chanjar.weixin.cp.bean.article.MpnewsArticle; import me.chanjar.weixin.cp.bean.article.NewArticle; import me.chanjar.weixin.cp.bean.messagebuilder.*; -import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; +import org.apache.commons.lang3.StringUtils; import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Map; + +import static me.chanjar.weixin.common.api.WxConsts.KefuMsgType.*; /** - * 消息 + * 消息. * * @author Daniel Qian */ +@Data public class WxCpMessage implements Serializable { - private static final long serialVersionUID = -2082278303476631708L; + private String toUser; private String toParty; private String toTag; @@ -30,112 +39,111 @@ public class WxCpMessage implements Serializable { private String musicUrl; private String hqMusicUrl; private String safe; + private String url; + private String btnTxt; private List articles = new ArrayList<>(); private List mpnewsArticles = new ArrayList<>(); + private String appId; + private String page; + private Boolean emphasisFirstItem; + private Map contentItems; - public List getMpnewsArticles() { - return mpnewsArticles; - } - - public void setMpnewsArticles(List mpnewsArticles) { - this.mpnewsArticles = mpnewsArticles; - } + /** + * 任务卡片特有的属性. + */ + private String taskId; + private List taskButtons = new ArrayList<>(); /** - * 获得文本消息builder + * 获得文本消息builder. */ public static TextBuilder TEXT() { return new TextBuilder(); } /** - * 获得图片消息builder + * 获得文本卡片消息builder. + */ + public static TextCardBuilder TEXTCARD() { + return new TextCardBuilder(); + } + + /** + * 获得图片消息builder. */ public static ImageBuilder IMAGE() { return new ImageBuilder(); } /** - * 获得语音消息builder + * 获得语音消息builder. */ public static VoiceBuilder VOICE() { return new VoiceBuilder(); } /** - * 获得视频消息builder + * 获得视频消息builder. */ public static VideoBuilder VIDEO() { return new VideoBuilder(); } /** - * 获得图文消息builder + * 获得图文消息builder. */ public static NewsBuilder NEWS() { return new NewsBuilder(); } /** - * 获得mpnews图文消息builder + * 获得mpnews图文消息builder. */ public static MpnewsBuilder MPNEWS() { return new MpnewsBuilder(); } /** - * 获得文件消息builder + * 获得markdown消息builder. */ - public static FileBuilder FILE() { - return new FileBuilder(); - } - - public String getToUser() { - return this.toUser; - } - - public void setToUser(String toUser) { - this.toUser = toUser; + public static MarkdownMsgBuilder MARKDOWN() { + return new MarkdownMsgBuilder(); } - public String getToParty() { - return this.toParty; - } - - public void setToParty(String toParty) { - this.toParty = toParty; - } - - public String getToTag() { - return this.toTag; - } - - public void setToTag(String toTag) { - this.toTag = toTag; - } - - public Integer getAgentId() { - return this.agentId; + /** + * 获得文件消息builder. + */ + public static FileBuilder FILE() { + return new FileBuilder(); } - public void setAgentId(Integer agentId) { - this.agentId = agentId; + /** + * 获得任务卡片消息builder. + */ + public static TaskCardBuilder TASKCARD() { + return new TaskCardBuilder(); } - public String getMsgType() { - return this.msgType; + /** + * 获得小程序通知消息builder. + */ + public static MiniProgramNoticeMsgBuilder newMiniProgramNoticeBuilder() { + return new MiniProgramNoticeMsgBuilder(); } /** *
    -   * 请使用
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VOICE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MUSIC}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MPNEWS}
    +   * 请使用.
    +   * {@link KefuMsgType#TEXT}
    +   * {@link KefuMsgType#IMAGE}
    +   * {@link KefuMsgType#VOICE}
    +   * {@link KefuMsgType#MUSIC}
    +   * {@link KefuMsgType#VIDEO}
    +   * {@link KefuMsgType#NEWS}
    +   * {@link KefuMsgType#MPNEWS}
    +   * {@link KefuMsgType#MARKDOWN}
    +   * {@link KefuMsgType#TASKCARD}
    +   * {@link KefuMsgType#MINIPROGRAM_NOTICE}
        * 
    * * @param msgType 消息类型 @@ -144,80 +152,182 @@ public void setMsgType(String msgType) { this.msgType = msgType; } - public String getSafe() { - return this.safe; - } - - public void setSafe(String safe) { - this.safe = safe; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getMusicUrl() { - return this.musicUrl; - } - - public void setMusicUrl(String musicUrl) { - this.musicUrl = musicUrl; - } - - public String getHqMusicUrl() { - return this.hqMusicUrl; - } - - public void setHqMusicUrl(String hqMusicUrl) { - this.hqMusicUrl = hqMusicUrl; - } - - public List getArticles() { - return this.articles; - } - - public void setArticles(List articles) { - this.articles = articles; - } - public String toJson() { - return WxCpGsonBuilder.INSTANCE.create().toJson(this); + JsonObject messageJson = new JsonObject(); + if (this.getAgentId() != null) { + messageJson.addProperty("agentid", this.getAgentId()); + } + + if (StringUtils.isNotBlank(this.getToUser())) { + messageJson.addProperty("touser", this.getToUser()); + } + + messageJson.addProperty("msgtype", this.getMsgType()); + + if (StringUtils.isNotBlank(this.getToParty())) { + messageJson.addProperty("toparty", this.getToParty()); + } + + if (StringUtils.isNotBlank(this.getToTag())) { + messageJson.addProperty("totag", this.getToTag()); + } + + this.handleMsgType(messageJson); + + if (StringUtils.isNotBlank(this.getSafe())) { + messageJson.addProperty("safe", this.getSafe()); + } + + return messageJson.toString(); + } + + private void handleMsgType(JsonObject messageJson) { + switch (this.getMsgType()) { + case TEXT: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("text", text); + break; + } + case MARKDOWN: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("markdown", text); + break; + } + case TEXTCARD: { + JsonObject text = new JsonObject(); + text.addProperty("title", this.getTitle()); + text.addProperty("description", this.getDescription()); + text.addProperty("url", this.getUrl()); + text.addProperty("btntxt", this.getBtnTxt()); + messageJson.add("textcard", text); + break; + } + case IMAGE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("image", image); + break; + } + case FILE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("file", image); + break; + } + case VOICE: { + JsonObject voice = new JsonObject(); + voice.addProperty("media_id", this.getMediaId()); + messageJson.add("voice", voice); + break; + } + case VIDEO: { + JsonObject video = new JsonObject(); + video.addProperty("media_id", this.getMediaId()); + video.addProperty("thumb_media_id", this.getThumbMediaId()); + video.addProperty("title", this.getTitle()); + video.addProperty("description", this.getDescription()); + messageJson.add("video", video); + break; + } + case NEWS: { + JsonObject newsJsonObject = new JsonObject(); + JsonArray articleJsonArray = new JsonArray(); + for (NewArticle article : this.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("description", article.getDescription()); + articleJson.addProperty("url", article.getUrl()); + articleJson.addProperty("picurl", article.getPicUrl()); + articleJsonArray.add(articleJson); + } + newsJsonObject.add("articles", articleJsonArray); + messageJson.add("news", newsJsonObject); + break; + } + case MPNEWS: { + JsonObject newsJsonObject = new JsonObject(); + if (this.getMediaId() != null) { + newsJsonObject.addProperty("media_id", this.getMediaId()); + } else { + JsonArray articleJsonArray = new JsonArray(); + for (MpnewsArticle article : this.getMpnewsArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); + articleJson.addProperty("author", article.getAuthor()); + articleJson.addProperty("content_source_url", article.getContentSourceUrl()); + articleJson.addProperty("content", article.getContent()); + articleJson.addProperty("digest", article.getDigest()); + articleJson.addProperty("show_cover_pic", article.getShowCoverPic()); + articleJsonArray.add(articleJson); + } + + newsJsonObject.add("articles", articleJsonArray); + } + messageJson.add("mpnews", newsJsonObject); + break; + } + case TASKCARD: { + JsonObject text = new JsonObject(); + text.addProperty("title", this.getTitle()); + text.addProperty("description", this.getDescription()); + + if (StringUtils.isNotBlank(this.getUrl())) { + text.addProperty("url", this.getUrl()); + } + + text.addProperty("task_id", this.getTaskId()); + + JsonArray buttonJsonArray = new JsonArray(); + for (TaskCardButton button : this.getTaskButtons()) { + JsonObject buttonJson = new JsonObject(); + buttonJson.addProperty("key", button.getKey()); + buttonJson.addProperty("name", button.getName()); + + if (StringUtils.isNotBlank(button.getReplaceName())) { + buttonJson.addProperty("replace_name", button.getReplaceName()); + } + + if (StringUtils.isNotBlank(button.getColor())) { + buttonJson.addProperty("color", button.getColor()); + } + + if (button.getBold() != null) { + buttonJson.addProperty("is_bold", button.getBold()); + } + + buttonJsonArray.add(buttonJson); + } + text.add("btn", buttonJsonArray); + + messageJson.add("taskcard", text); + break; + } + case MINIPROGRAM_NOTICE: { + JsonObject notice = new JsonObject(); + notice.addProperty("appid", this.getAppId()); + notice.addProperty("page", this.getPage()); + notice.addProperty("description", this.getDescription()); + notice.addProperty("title", this.getTitle()); + notice.addProperty("emphasis_first_item", this.getEmphasisFirstItem()); + JsonArray content = new JsonArray(); + for (Map.Entry item : this.getContentItems().entrySet()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("key", item.getKey()); + articleJson.addProperty("value", item.getValue()); + content.add(articleJson); + } + notice.add("content_item", content); + + messageJson.add("miniprogram_notice", notice); + break; + } + default: { + // do nothing + } + } } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessageSendResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessageSendResult.java new file mode 100644 index 0000000000..d850adebcc --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessageSendResult.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.cp.bean; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Splitter; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 消息发送结果对象类. + * Created by Binary Wang on 2017-6-22. + * + * @author Binary Wang + */ +@Data +public class WxCpMessageSendResult implements Serializable { + private static final long serialVersionUID = 916455987193190004L; + + @Override + public String toString() { + return WxCpGsonBuilder.create().toJson(this); + } + + public static WxCpMessageSendResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpMessageSendResult.class); + } + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("invaliduser") + private String invalidUser; + + @SerializedName("invalidparty") + private String invalidParty; + + @SerializedName("invalidtag") + private String invalidTag; + + + public List getInvalidUserList() { + return this.content2List(this.invalidUser); + } + + private List content2List(String content) { + if (StringUtils.isBlank(content)) { + return Collections.emptyList(); + } + + return Splitter.on("|").splitToList(content); + } + + public List getInvalidPartyList() { + return this.content2List(this.invalidParty); + } + + public List getInvalidTagList() { + return this.content2List(this.invalidTag); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java new file mode 100644 index 0000000000..9122f18d3a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.cp.bean; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *
    + *  用oauth2获取用户信息的结果类
    + *  Created by BinaryWang on 2019/5/26.
    + * 
    + * + * @author Binary Wang + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class WxCpOauth2UserInfo { + private String openId; + private String deviceId; + private String userId; + private String userTicket; + private String expiresIn; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpProviderToken.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpProviderToken.java new file mode 100644 index 0000000000..2c98f8e3fd --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpProviderToken.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 服务商凭证. + * + * @author Binary Wang + * @date 2019-11-02 + */ +@Data +public class WxCpProviderToken { + /** + * 服务商的access_token,最长为512字节。 + */ + @SerializedName("provider_access_token") + private String providerAccessToken; + + /** + * provider_access_token有效期(秒) + */ + @SerializedName("expires_in") + private Integer expiresIn; + + public static WxCpProviderToken fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpProviderToken.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java index 4e1c034f6a..f6b9fa0276 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java @@ -2,49 +2,30 @@ import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; /** - * Created by Daniel Qian + * Created by Daniel Qian. + * @author Daniel Qian */ +@Data +@AllArgsConstructor +@NoArgsConstructor public class WxCpTag implements Serializable { - private static final long serialVersionUID = -7243320279646928402L; private String id; private String name; - public WxCpTag() { - super(); - } - - public WxCpTag(String id, String name) { - super(); - this.id = id; - this.name = name; - } public static WxCpTag fromJson(String json) { return WxCpGsonBuilder.create().fromJson(json, WxCpTag.class); } - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - public String toJson() { return WxCpGsonBuilder.create().toJson(this); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java new file mode 100644 index 0000000000..037740ca96 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.cp.bean; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Splitter; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 为标签添加或移除用户结果对象类. + * Created by Binary Wang on 2017-6-22. + * + * @author Binary Wang + */ +@Data +public class WxCpTagAddOrRemoveUsersResult implements Serializable { + private static final long serialVersionUID = 1420065684270213578L; + + @Override + public String toString() { + return WxCpGsonBuilder.create().toJson(this); + } + + public static WxCpTagAddOrRemoveUsersResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTagAddOrRemoveUsersResult.class); + } + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("invalidlist") + private String invalidUsers; + + @SerializedName("invalidparty") + private String[] invalidParty; + + public List getInvalidUserList() { + return this.content2List(this.invalidUsers); + } + + private List content2List(String content) { + if (StringUtils.isBlank(content)) { + return Collections.emptyList(); + } + + return Splitter.on("|").splitToList(content); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagGetResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagGetResult.java new file mode 100644 index 0000000000..244419b062 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagGetResult.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + *  管理企业号应用-测试
    + *  Created by huansinho on 2018/4/16.
    + * 
    + * + * @author huansinho + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WxCpTagGetResult implements Serializable { + + @SerializedName("errcode") + private Integer errcode; + + @SerializedName("errmsg") + private String errmsg; + + /** + * 用户列表. + */ + @SerializedName("userlist") + private List userlist; + + /** + * 部门列表. + */ + @SerializedName("partylist") + private List partylist; + + /** + * 标签名称. + */ + @SerializedName("tagname") + private String tagname; + + public static WxCpTagGetResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTagGetResult.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java new file mode 100644 index 0000000000..c86b255b44 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + *  更新任务卡片消息状态的返回类
    + *  参考文档:https://work.weixin.qq.com/api/doc#90000/90135/91579
    + *  Created by Jeff on 2019-05-16.
    + * 
    + * + * @author Jeff + * @date 2019-05-16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WxCpTaskCardUpdateResult implements Serializable { + + @SerializedName("errcode") + private Integer errcode; + + @SerializedName("errmsg") + private String errmsg; + + /** + * 用户列表 + */ + @SerializedName("invaliduser") + private List invalidUsers; + + public static WxCpTaskCardUpdateResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTaskCardUpdateResult.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAuthInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAuthInfo.java new file mode 100644 index 0000000000..4354865a28 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAuthInfo.java @@ -0,0 +1,202 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * 服务商模式获取授权信息 + * + * @author yuanqixun + */ +@Getter +@Setter +public class WxCpTpAuthInfo extends WxCpBaseResp { + private static final long serialVersionUID = -5028321625140879571L; + + /** + * 服务商信息 + */ + @SerializedName("dealer_corp_info") + private DealerCorpInfo dealerCorpInfo; + + /** + * 授权企业信息 + */ + @SerializedName("auth_corp_info") + private AuthCorpInfo authCorpInfo; + + /** + * 授权信息。如果是通讯录应用,且没开启实体应用,是没有该项的。通讯录应用拥有企业通讯录的全部信息读写权限 + */ + @SerializedName("auth_info") + private AuthInfo authInfo; + + @Getter + @Setter + public static class DealerCorpInfo { + @SerializedName("corpid") + private String corpId; + + @SerializedName("corp_name") + private String corpName; + } + + @Getter + @Setter + public static class AuthCorpInfo { + @SerializedName("corpid") + private String corpId; + + @SerializedName("corp_name") + private String corpName; + + @SerializedName("corp_type") + private String corpType; + + @SerializedName("corp_square_logo_url") + private String corpSquareLogoUrl; + + @SerializedName("corp_round_logo_url") + private String corpRoundLogoUrl; + + @SerializedName("corp_user_max") + private String corpUserMax; + + @SerializedName("corp_agent_max") + private String corpAgentMax; + + /** + * 所绑定的企业微信主体名称(仅认证过的企业有) + */ + @SerializedName("corp_full_name") + private String corpFullName; + + /** + * 认证到期时间 + */ + @SerializedName("verified_end_time") + private Long verifiedEndTime; + + /** + * 企业类型,1. 企业; 2. 政府以及事业单位; 3. 其他组织, 4.团队号 + */ + @SerializedName("subject_type") + private Integer subjectType; + + /** + * 授权企业在微工作台(原企业号)的二维码,可用于关注微工作台 + */ + @SerializedName("corp_wxqrcode") + private String corpWxQrcode; + + @SerializedName("corp_scale") + private String corpScale; + + @SerializedName("corp_industry") + private String corpIndustry; + + @SerializedName("corp_sub_industry") + private String corpSubIndustry; + + @SerializedName("location") + private String location; + + } + + /** + * 授权信息 + */ + @Getter + @Setter + public static class AuthInfo { + + /** + * 授权的应用信息,注意是一个数组,但仅旧的多应用套件授权时会返回多个agent,对新的单应用授权,永远只返回一个agent + */ + @SerializedName("agent") + private List agents; + + } + + @Getter + @Setter + public static class Agent { + @SerializedName("agentid") + private Integer agentId; + + @SerializedName("name") + private String name; + + @SerializedName("round_logo_url") + private String roundLogoUrl; + + @SerializedName("square_logo_url") + private String squareLogoUrl; + + /** + * 旧的多应用套件中的对应应用id,新开发者请忽略 + */ + @SerializedName("appid") + @Deprecated + private String appid; + + /** + * 应用权限 + */ + @SerializedName("privilege") + private Privilege privilege; + + } + + /** + * 应用对应的权限 + */ + @Getter + @Setter + public static class Privilege { + + /** + * 权限等级。 + * 1:通讯录基本信息只读 + * 2:通讯录全部信息只读 + * 3:通讯录全部信息读写 + * 4:单个基本信息只读 + * 5:通讯录全部信息只写 + */ + @SerializedName("level") + private Integer level; + + @SerializedName("allow_party") + private List allowParties; + + @SerializedName("allow_user") + private List allowUsers; + + @SerializedName("allow_tag") + private List allowTags; + + @SerializedName("extra_party") + private List extraParties; + + @SerializedName("extra_user") + private List extraUsers; + + @SerializedName("extra_tag") + private List extraTags; + + } + + + public static WxCpTpAuthInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTpAuthInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java new file mode 100644 index 0000000000..f77fdb78df --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.cp.bean; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; + +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 微信部门. + * + * @author Daniel Qian + */ +@Data +public class WxCpTpCorp implements Serializable { + + private static final long serialVersionUID = -5028321625140879571L; + @SerializedName("corpid") + private String corpId; + @SerializedName("corp_name") + private String corpName; + @SerializedName("corp_full_name") + private String corpFullName; + @SerializedName("corp_type") + private String corpType; + @SerializedName("corp_square_logo_url") + private String corpSquareLogoUrl; + @SerializedName("corp_user_max") + private String corpUserMax; + @SerializedName("permanent_code") + private String permanentCode; + @SerializedName("auth_info") + private String authInfo; + + public static WxCpTpCorp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTpCorp.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPermanentCodeInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPermanentCodeInfo.java new file mode 100644 index 0000000000..cd57119d1e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPermanentCodeInfo.java @@ -0,0 +1,219 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * 服务商模式获取永久授权码信息 + * + * @author yunaqixun + */ +@Getter +@Setter +public class WxCpTpPermanentCodeInfo extends WxCpBaseResp { + + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("access_token") + private String accessToken; + + @SerializedName("expires_in") + private Long expiresIn; + + @SerializedName("permanent_code") + private String permanentCode; + + /** + * 授权企业信息 + */ + @SerializedName("auth_corp_info") + private AuthCorpInfo authCorpInfo; + + /** + * 授权信息。如果是通讯录应用,且没开启实体应用,是没有该项的。通讯录应用拥有企业通讯录的全部信息读写权限 + */ + @SerializedName("auth_info") + private AuthInfo authInfo; + + /** + * 授权用户信息 + */ + @SerializedName("auth_user_info") + private AuthUserInfo authUserInfo; + + + @Getter + @Setter + public static class AuthCorpInfo { + @SerializedName("corpid") + private String corpId; + + @SerializedName("corp_name") + private String corpName; + + @SerializedName("corp_type") + private String corpType; + + @SerializedName("corp_square_logo_url") + private String corpSquareLogoUrl; + + @SerializedName("corp_round_logo_url") + private String corpRoundLogoUrl; + + @SerializedName("corp_user_max") + private String corpUserMax; + + @SerializedName("corp_agent_max") + private String corpAgentMax; + + /** + * 所绑定的企业微信主体名称(仅认证过的企业有) + */ + @SerializedName("corp_full_name") + private String corpFullName; + + /** + * 认证到期时间 + */ + @SerializedName("verified_end_time") + private Long verifiedEndTime; + + /** + * 企业类型,1. 企业; 2. 政府以及事业单位; 3. 其他组织, 4.团队号 + */ + @SerializedName("subject_type") + private Integer subjectType; + + /** + * 授权企业在微工作台(原企业号)的二维码,可用于关注微工作台 + */ + @SerializedName("corp_wxqrcode") + private String corpWxQrcode; + + @SerializedName("corp_scale") + private String corpScale; + + @SerializedName("corp_industry") + private String corpIndustry; + + @SerializedName("corp_sub_industry") + private String corpSubIndustry; + + @SerializedName("location") + private String location; + + } + + /** + * 授权信息 + */ + @Getter + @Setter + public static class AuthInfo { + + /** + * 授权的应用信息,注意是一个数组,但仅旧的多应用套件授权时会返回多个agent,对新的单应用授权,永远只返回一个agent + */ + @SerializedName("agent") + private List agents; + + } + + @Getter + @Setter + public static class Agent { + @SerializedName("agentid") + private Integer agentId; + + @SerializedName("name") + private String name; + + @SerializedName("round_logo_url") + private String roundLogoUrl; + + @SerializedName("square_logo_url") + private String squareLogoUrl; + + /** + * 旧的多应用套件中的对应应用id,新开发者请忽略 + */ + @SerializedName("appid") + @Deprecated + private String appid; + + /** + * 应用权限 + */ + @SerializedName("privilege") + private Privilege privilege; + + } + + /** + * 授权人员信息 + */ + @Getter + @Setter + public static class AuthUserInfo { + @SerializedName("userid") + private String userId; + + @SerializedName("name") + private String name; + + @SerializedName("avatar") + private String avatar; + } + + /** + * 应用对应的权限 + */ + @Getter + @Setter + public static class Privilege { + + /** + * 权限等级。 + * 1:通讯录基本信息只读 + * 2:通讯录全部信息只读 + * 3:通讯录全部信息读写 + * 4:单个基本信息只读 + * 5:通讯录全部信息只写 + */ + @SerializedName("level") + private Integer level; + + @SerializedName("allow_party") + private List allowParties; + + @SerializedName("allow_user") + private List allowUsers; + + @SerializedName("allow_tag") + private List allowTags; + + @SerializedName("extra_party") + private List extraParties; + + @SerializedName("extra_user") + private List extraUsers; + + @SerializedName("extra_tag") + private List extraTags; + + + } + + public static WxCpTpPermanentCodeInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTpPermanentCodeInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPreauthCode.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPreauthCode.java new file mode 100644 index 0000000000..8c102ae4a2 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPreauthCode.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 预授权码返回 + * @author yqx + * @date 2020/3/19 + */ +@Getter +@Setter +public class WxCpTpPreauthCode extends WxCpBaseResp { + + @SerializedName("pre_auth_code") + String preAuthCode; + + @SerializedName("expires_in") + Long expiresIn; + + public static WxCpTpPreauthCode fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTpPreauthCode.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlMessage.java new file mode 100644 index 0000000000..f39b062c08 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlMessage.java @@ -0,0 +1,63 @@ +package me.chanjar.weixin.cp.bean; + +import java.io.Serializable; +import java.util.Map; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.util.XmlUtils; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.cp.util.xml.XStreamTransformer; + +/** + * 回调推送的message + * https://work.weixin.qq.com/api/doc#90001/90143/90612 + * + * @author zhenjun cai + */ +@XStreamAlias("xml") +@Slf4j +@Data +public class WxCpTpXmlMessage implements Serializable { + + private static final long serialVersionUID = 6031833682211475786L; + /** + * 使用dom4j解析的存放所有xml属性和值的map. + */ + private Map allFieldsMap; + + @XStreamAlias("SuiteId") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String suiteId; + + @XStreamAlias("InfoType") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String infoType; + + @XStreamAlias("TimeStamp") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String timeStamp; + + @XStreamAlias("SuiteTicket") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String suiteTicket; + + @XStreamAlias("AuthCode") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String authCode; + + @XStreamAlias("AuthCorpId") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String authCorpId; + + public static WxCpTpXmlMessage fromXml(String xml) { + //修改微信变态的消息内容格式,方便解析 + //xml = xml.replace("", ""); + final WxCpTpXmlMessage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlMessage.class, xml); + xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml)); + return xmlPackage; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java new file mode 100644 index 0000000000..e7af1dd61a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.cp.bean; + +import java.io.Serializable; +import java.util.Map; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import me.chanjar.weixin.common.util.XmlUtils; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.cp.util.xml.XStreamTransformer; + +/** + * 回调消息包. + * https://work.weixin.qq.com/api/doc#90001/90143/91116 + * + * @author zhenjun cai + */ +@XStreamAlias("xml") +@Data +public class WxCpTpXmlPackage implements Serializable { + private static final long serialVersionUID = 6031833682211475786L; + + /** + * 使用dom4j解析的存放所有xml属性和值的map. + */ + private Map allFieldsMap; + + @XStreamAlias("ToUserName") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String toUserName; + + @XStreamAlias("AgentID") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String agentId; + + @XStreamAlias("Encrypt") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String msgEncrypt; + + public static WxCpTpXmlPackage fromXml(String xml) { + //修改微信变态的消息内容格式,方便解析 + //xml = xml.replace("", ""); + final WxCpTpXmlPackage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlPackage.class, xml); + xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml)); + return xmlPackage; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java index 93c3e5feb4..a6ceb4e551 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java @@ -1,171 +1,135 @@ package me.chanjar.weixin.cp.bean; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; - /** - * 微信用户信息 + * 微信用户信息. * * @author Daniel Qian */ +@Data +@Accessors(chain = true) public class WxCpUser implements Serializable { - private static final long serialVersionUID = -5696099236344075582L; - private final List extAttrs = new ArrayList<>(); + private String userId; private String name; - private Integer[] departIds; + private Long[] departIds; + private Integer[] orders; private String position; + private String[] positions; private String mobile; - private String gender; - private String tel; + private Gender gender; private String email; - private String weiXinId; private String avatar; + private String thumbAvatar; + private String mainDepartment; + + /** + * 地址。长度最大128个字符 + */ + private String address; + private String avatarMediaId; private Integer status; private Integer enable; - - public static WxCpUser fromJson(String json) { - return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpUser.class); - } - - public String getUserId() { - return this.userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer[] getDepartIds() { - return this.departIds; - } - - public void setDepartIds(Integer[] departIds) { - this.departIds = departIds; - } - - public String getGender() { - return this.gender; - } - - public void setGender(String gender) { - this.gender = gender; - } - - public String getPosition() { - return this.position; - } - - public void setPosition(String position) { - this.position = position; - } - - public String getMobile() { - return this.mobile; - } - - public void setMobile(String mobile) { - this.mobile = mobile; - } - - public String getTel() { - return this.tel; - } - - public void setTel(String tel) { - this.tel = tel; - } - - public String getEmail() { - return this.email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getWeiXinId() { - return this.weiXinId; - } - - public void setWeiXinId(String weiXinId) { - this.weiXinId = weiXinId; - } - - public String getAvatar() { - return this.avatar; - } - - public void setAvatar(String avatar) { - this.avatar = avatar; - } - - public Integer getStatus() { - return this.status; - } - - public void setStatus(Integer status) { - this.status = status; - } - - public Integer getEnable() { - return this.enable; + /** + * 别名;第三方仅通讯录应用可获取 + */ + private String alias; + private Integer isLeader; + /** + * is_leader_in_dept. + * 个数必须和department一致,表示在所在的部门内是否为上级。1表示为上级,0表示非上级。在审批等应用里可以用来标识上级审批人 + */ + private Integer[] isLeaderInDept; + private final List extAttrs = new ArrayList<>(); + private Integer hideMobile; + private String englishName; + private String telephone; + private String qrCode; + private Boolean toInvite; + /** + * 成员对外信息. + */ + private List externalAttrs = new ArrayList<>(); + private String externalPosition; + private String externalCorpName; + + public void addExternalAttr(ExternalAttribute externalAttr) { + this.externalAttrs.add(externalAttr); } - public void setEnable(Integer enable) { - this.enable = enable; + public void addExtAttr(String name, String value) { + this.extAttrs.add(new Attr().setType(0).setName(name).setTextValue(value)); } - public void addExtAttr(String name, String value) { - this.extAttrs.add(new Attr(name, value)); + public void addExtAttr(Attr attr) { + this.extAttrs.add(attr); } - public List getExtAttrs() { - return this.extAttrs; + public static WxCpUser fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUser.class); } public String toJson() { - return WxCpGsonBuilder.INSTANCE.create().toJson(this); + return WxCpGsonBuilder.create().toJson(this); } + @Data + @Accessors(chain = true) public static class Attr { - + /** + * 属性类型: 0-文本 1-网页 + */ + private Integer type; private String name; + private String textValue; + private String webUrl; + private String webTitle; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ExternalAttribute { + /** + * 属性类型: 0-本文 1-网页 2-小程序. + */ + private Integer type; + /** + * 属性名称: 需要先确保在管理端有创建改属性,否则会忽略. + */ + private String name; + /** + * 文本属性内容,长度限制12个UTF8字符. + */ private String value; - - public Attr(String name, String value) { - this.name = name; - this.value = value; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - + /** + * 网页的url,必须包含http或者https头. + */ + private String url; + /** + * 小程序的展示标题,长度限制12个UTF8字符. + * 或者 网页的展示标题,长度限制12个UTF8字符 + */ + private String title; + /** + * 小程序appid,必须是有在本企业安装授权的小程序,否则会被忽略. + */ + private String appid; + /** + * 小程序的页面路径. + */ + private String pagePath; } - } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserDetail.java new file mode 100644 index 0000000000..1d40e94ae0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserDetail.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + *
    + *  使用user_ticket获取成员详情接口返回类.
    + *  Created by BinaryWang on 2018/4/22.
    + * 
    + * + * @author Binary Wang + */ +@Data +public class WxCpUserDetail { + + /** + * 成员UserID + */ + @SerializedName("userid") + private String userId; + + /** + * 成员姓名 + */ + private String name; + + /** + * 成员手机号,仅在用户同意snsapi_privateinfo授权时返回 + */ + private String mobile; + + /** + * 性别。0表示未定义,1表示男性,2表示女性 + */ + private String gender; + + /** + * 成员邮箱,仅在用户同意snsapi_privateinfo授权时返回 + */ + private String email; + + /** + * 头像url。注:如果要获取小图将url最后的”/0”改成”/100”即可。仅在用户同意snsapi_privateinfo授权时返回 + */ + private String avatar; + + /** + * 员工个人二维码(扫描可添加为外部联系人),仅在用户同意snsapi_privateinfo授权时返回 + */ + @SerializedName("qr_code") + private String qrCode; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java index 93ca45c36e..85c6d99131 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java @@ -1,36 +1,50 @@ package me.chanjar.weixin.cp.bean; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.io.IOUtils; - import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; - +import com.thoughtworks.xstream.annotations.XStreamImplicit; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.util.XmlUtils; +import me.chanjar.weixin.common.util.xml.IntegerArrayConverter; +import me.chanjar.weixin.common.util.xml.LongArrayConverter; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** *
      * 微信推送过来的消息,也是同步回复给用户的消息,xml格式
      * 相关字段的解释看微信开发者文档:
    - * http://mp.weixin.qq.com/wiki/index.php?title=接收普通消息
    - * http://mp.weixin.qq.com/wiki/index.php?title=接收事件推送
    - * http://mp.weixin.qq.com/wiki/index.php?title=接收语音识别结果
    + * https://work.weixin.qq.com/api/doc#12973
    + * https://work.weixin.qq.com/api/doc#12974
      * 
    * * @author Daniel Qian */ +@Data +@Slf4j @XStreamAlias("xml") public class WxCpXmlMessage implements Serializable { private static final long serialVersionUID = -1042994982179476410L; + /** + * 使用dom4j解析的存放所有xml属性和值的map. + */ + private Map allFieldsMap; + /////////////////////// // 以下都是微信推送过来的消息的xml的element所对应的属性 /////////////////////// @@ -49,6 +63,24 @@ public class WxCpXmlMessage implements Serializable { @XStreamAlias("CreateTime") private Long createTime; + /** + *
    +   * 当接受用户消息时,可能会获得以下值:
    +   * {@link WxConsts.XmlMsgType#TEXT}
    +   * {@link WxConsts.XmlMsgType#IMAGE}
    +   * {@link WxConsts.XmlMsgType#VOICE}
    +   * {@link WxConsts.XmlMsgType#VIDEO}
    +   * {@link WxConsts.XmlMsgType#LOCATION}
    +   * {@link WxConsts.XmlMsgType#LINK}
    +   * {@link WxConsts.XmlMsgType#EVENT}
    +   * 当发送消息的时候使用:
    +   * {@link WxConsts.XmlMsgType#TEXT}
    +   * {@link WxConsts.XmlMsgType#IMAGE}
    +   * {@link WxConsts.XmlMsgType#VOICE}
    +   * {@link WxConsts.XmlMsgType#VIDEO}
    +   * {@link WxConsts.XmlMsgType#NEWS}
    +   * 
    + */ @XStreamAlias("MsgType") @XStreamConverter(value = XStreamCDataConverter.class) private String msgType; @@ -126,484 +158,357 @@ public class WxCpXmlMessage implements Serializable { @XStreamConverter(value = XStreamCDataConverter.class) private String recognition; - /////////////////////////////////////// - // 群发消息返回的结果 - /////////////////////////////////////// + @XStreamAlias("TaskId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String taskId; + /** - * 群发的结果 + * 通讯录变更事件. + * 请参考常量 me.chanjar.weixin.cp.constant.WxCpConsts.ContactChangeType */ - @XStreamAlias("Status") + @XStreamAlias("ChangeType") @XStreamConverter(value = XStreamCDataConverter.class) - private String status; + private String changeType; + /** - * group_id下粉丝数;或者openid_list中的粉丝数 + * 变更信息的成员UserID. */ - @XStreamAlias("TotalCount") - private Integer totalCount; + @XStreamAlias("UserID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String userId; + /** - * 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount + * 变更信息的外部联系人的userid,注意不是企业成员的帐号. */ - @XStreamAlias("FilterCount") - private Integer filterCount; + @XStreamAlias("ExternalUserID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String externalUserId; + /** - * 发送成功的粉丝数 + * 添加此用户的「联系我」方式配置的state参数,可用于识别添加此用户的渠道. */ - @XStreamAlias("SentCount") - private Integer sentCount; + @XStreamAlias("State") + @XStreamConverter(value = XStreamCDataConverter.class) + private String state; + /** - * 发送失败的粉丝数 + * 欢迎语code,可用于发送欢迎语. */ - @XStreamAlias("ErrorCount") - private Integer errorCount; - - @XStreamAlias("ScanCodeInfo") - private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); - - @XStreamAlias("SendPicsInfo") - private SendPicsInfo sendPicsInfo = new SendPicsInfo(); - - @XStreamAlias("SendLocationInfo") - private SendLocationInfo sendLocationInfo = new SendLocationInfo(); - - protected static WxCpXmlMessage fromXml(String xml) { - return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml); - } - - protected static WxCpXmlMessage fromXml(InputStream is) { - return XStreamTransformer.fromXml(WxCpXmlMessage.class, is); - } - + @XStreamAlias("WelcomeCode") + @XStreamConverter(value = XStreamCDataConverter.class) + private String welcomeCode; /** - * 从加密字符串转换 - * - * @param encryptedXml - * @param wxCpConfigStorage - * @param timestamp - * @param nonce - * @param msgSignature + * 新的UserID,变更时推送(userid由系统生成时可更改一次). */ - public static WxCpXmlMessage fromEncryptedXml( - String encryptedXml, - WxCpConfigStorage wxCpConfigStorage, - String timestamp, String nonce, String msgSignature) { - WxCpCryptUtil cryptUtil = new WxCpCryptUtil(wxCpConfigStorage); - String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); - return fromXml(plainText); - } - - public static WxCpXmlMessage fromEncryptedXml( - InputStream is, - WxCpConfigStorage wxCpConfigStorage, - String timestamp, String nonce, String msgSignature) { - try { - return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public Integer getAgentId() { - return this.agentId; - } - - public void setAgentId(Integer agentId) { - this.agentId = agentId; - } - - public String getToUserName() { - return this.toUserName; - } - - public void setToUserName(String toUserName) { - this.toUserName = toUserName; - } - - public Long getCreateTime() { - return this.createTime; - } - - public void setCreateTime(Long createTime) { - this.createTime = createTime; - } + @XStreamAlias("NewUserID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String newUserId; /** - *
    -   * 当接受用户消息时,可能会获得以下值:
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VOICE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LOCATION}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LINK}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT}
    -   * 
    + * 成员名称. + * 或者部门名称 */ - public String getMsgType() { - return this.msgType; - } + @XStreamAlias("Name") + @XStreamConverter(value = XStreamCDataConverter.class) + private String name; /** - *
    -   * 当发送消息的时候使用:
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VOICE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_NEWS}
    -   * 
    - * - * @param msgType + * 成员部门列表,变更时推送,仅返回该应用有查看权限的部门id. */ - public void setMsgType(String msgType) { - this.msgType = msgType; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public Long getMsgId() { - return this.msgId; - } - - public void setMsgId(Long msgId) { - this.msgId = msgId; - } - - public String getPicUrl() { - return this.picUrl; - } - - public void setPicUrl(String picUrl) { - this.picUrl = picUrl; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getFormat() { - return this.format; - } - - public void setFormat(String format) { - this.format = format; - } - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public Double getLocationX() { - return this.locationX; - } + @XStreamAlias("Department") + @XStreamConverter(value = LongArrayConverter.class) + private Long[] departments; - public void setLocationX(Double locationX) { - this.locationX = locationX; - } - - public Double getLocationY() { - return this.locationY; - } - - public void setLocationY(Double locationY) { - this.locationY = locationY; - } - - public Double getScale() { - return this.scale; - } - - public void setScale(Double scale) { - this.scale = scale; - } - - public String getLabel() { - return this.label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } + /** + * 手机号码. + */ + @XStreamAlias("Mobile") + @XStreamConverter(value = XStreamCDataConverter.class) + private String mobile; - public void setDescription(String description) { - this.description = description; - } + /** + * 职位信息。长度为0~64个字节. + */ + @XStreamAlias("Position") + @XStreamConverter(value = XStreamCDataConverter.class) + private String position; - public String getUrl() { - return this.url; - } + /** + * 群ID. + */ + @XStreamAlias("ChatId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String chatId; - public void setUrl(String url) { - this.url = url; - } + /** + * 性别,1表示男性,2表示女性. + */ + @XStreamAlias("Gender") + private Integer gender; - public String getEvent() { - return this.event; - } + /** + * 邮箱. + */ + @XStreamAlias("Email") + @XStreamConverter(value = XStreamCDataConverter.class) + private String email; - public void setEvent(String event) { - this.event = event; - } + /** + * 头像url。注:如果要获取小图将url最后的”/0”改成”/100”即可. + */ + @XStreamAlias("Avatar") + @XStreamConverter(value = XStreamCDataConverter.class) + private String avatar; - public String getEventKey() { - return this.eventKey; - } + /** + * 英文名. + */ + @XStreamAlias("EnglishName") + @XStreamConverter(value = XStreamCDataConverter.class) + private String englishName; - public void setEventKey(String eventKey) { - this.eventKey = eventKey; - } + /** + * 上级字段,标识是否为上级。0表示普通成员,1表示上级. + */ + @XStreamAlias("IsLeader") + private Integer isLeader; - public String getTicket() { - return this.ticket; - } + /** + * 表示所在部门是否为上级,0-否,1-是,顺序与Department字段的部门逐一对应. + */ + @XStreamAlias("IsLeaderInDept") + @XStreamConverter(value = IntegerArrayConverter.class) + private Integer[] isLeaderInDept; - public void setTicket(String ticket) { - this.ticket = ticket; - } + /** + * 座机. + */ + @XStreamAlias("Telephone") + @XStreamConverter(value = XStreamCDataConverter.class) + private String telephone; - public Double getLatitude() { - return this.latitude; - } + /** + * 地址. + */ + @XStreamAlias("Address") + @XStreamConverter(value = XStreamCDataConverter.class) + private String address; - public void setLatitude(Double latitude) { - this.latitude = latitude; - } + /** + * 扩展属性. + */ + @XStreamAlias("ExtAttr") + private ExtAttr extAttrs = new ExtAttr(); - public Double getLongitude() { - return this.longitude; - } + /** + * 部门Id. + */ + @XStreamAlias("Id") + private Long id; - public void setLongitude(Double longitude) { - this.longitude = longitude; - } + /** + * 父部门id. + */ + @XStreamAlias("ParentId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String parentId; - public Double getPrecision() { - return this.precision; - } + /** + * 部门排序. + */ + @XStreamAlias("Order") + @XStreamConverter(value = XStreamCDataConverter.class) + private String order; - public void setPrecision(Double precision) { - this.precision = precision; - } + /** + * 标签Id. + */ + @XStreamAlias("TagId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String tagId; - public String getRecognition() { - return this.recognition; - } + /** + * 标签中新增的成员userid列表,用逗号分隔. + */ + @XStreamAlias("AddUserItems") + @XStreamConverter(value = XStreamCDataConverter.class) + private String addUserItems; - public void setRecognition(String recognition) { - this.recognition = recognition; - } + /** + * 标签中删除的成员userid列表,用逗号分隔. + */ + @XStreamAlias("DelUserItems") + @XStreamConverter(value = XStreamCDataConverter.class) + private String delUserItems; - public String getFromUserName() { - return this.fromUserName; - } + /** + * 标签中新增的部门id列表,用逗号分隔. + */ + @XStreamAlias("AddPartyItems") + @XStreamConverter(value = XStreamCDataConverter.class) + private String addPartyItems; - public void setFromUserName(String fromUserName) { - this.fromUserName = fromUserName; - } + /** + * 标签中删除的部门id列表,用逗号分隔. + */ + @XStreamAlias("DelPartyItems") + @XStreamConverter(value = XStreamCDataConverter.class) + private String delPartyItems; - public String getStatus() { - return this.status; - } - public void setStatus(String status) { - this.status = status; - } + /////////////////////////////////////// + // 群发消息返回的结果 + /////////////////////////////////////// + /** + * 多个时间共用字段. + * 1. 群发的结果. + * 2. 通讯录变更事件 + * 激活状态:1=已激活 2=已禁用 4=未激活 已激活代表已激活企业微信或已关注微工作台(原企业号). + */ + @XStreamAlias("Status") + @XStreamConverter(value = XStreamCDataConverter.class) + private String status; - public Integer getTotalCount() { - return this.totalCount; - } + /** + * group_id下粉丝数;或者openid_list中的粉丝数. + */ + @XStreamAlias("TotalCount") + private Integer totalCount; - public void setTotalCount(Integer totalCount) { - this.totalCount = totalCount; - } + /** + * 过滤. + * (过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount + */ + @XStreamAlias("FilterCount") + private Integer filterCount; - public Integer getFilterCount() { - return this.filterCount; - } + /** + * 发送成功的粉丝数. + */ + @XStreamAlias("SentCount") + private Integer sentCount; - public void setFilterCount(Integer filterCount) { - this.filterCount = filterCount; - } + /** + * 发送失败的粉丝数. + */ + @XStreamAlias("ErrorCount") + private Integer errorCount; - public Integer getSentCount() { - return this.sentCount; - } + @XStreamAlias("ScanCodeInfo") + private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); - public void setSentCount(Integer sentCount) { - this.sentCount = sentCount; - } + @XStreamAlias("SendPicsInfo") + private SendPicsInfo sendPicsInfo = new SendPicsInfo(); - public Integer getErrorCount() { - return this.errorCount; - } + @XStreamAlias("SendLocationInfo") + private SendLocationInfo sendLocationInfo = new SendLocationInfo(); - public void setErrorCount(Integer errorCount) { - this.errorCount = errorCount; - } - public WxCpXmlMessage.ScanCodeInfo getScanCodeInfo() { - return this.scanCodeInfo; - } + @XStreamAlias("ApprovalInfo") + private ApprovalInfo approvalInfo = new ApprovalInfo(); - public void setScanCodeInfo(WxCpXmlMessage.ScanCodeInfo scanCodeInfo) { - this.scanCodeInfo = scanCodeInfo; - } - public WxCpXmlMessage.SendPicsInfo getSendPicsInfo() { - return this.sendPicsInfo; + protected static WxCpXmlMessage fromXml(String xml) { + //修改微信变态的消息内容格式,方便解析 + xml = xml.replace("
    ", ""); + final WxCpXmlMessage xmlMessage = XStreamTransformer.fromXml(WxCpXmlMessage.class, xml); + xmlMessage.setAllFieldsMap(XmlUtils.xml2Map(xml)); + return xmlMessage; } - public void setSendPicsInfo(WxCpXmlMessage.SendPicsInfo sendPicsInfo) { - this.sendPicsInfo = sendPicsInfo; + protected static WxCpXmlMessage fromXml(InputStream is) { + return XStreamTransformer.fromXml(WxCpXmlMessage.class, is); } - public WxCpXmlMessage.SendLocationInfo getSendLocationInfo() { - return this.sendLocationInfo; + /** + * 从加密字符串转换. + */ + public static WxCpXmlMessage fromEncryptedXml(String encryptedXml, WxCpConfigStorage wxCpConfigStorage, + String timestamp, String nonce, String msgSignature) { + WxCpCryptUtil cryptUtil = new WxCpCryptUtil(wxCpConfigStorage); + String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); + log.debug("解密后的原始xml消息内容:{}", plainText); + return fromXml(plainText); } - public void setSendLocationInfo(WxCpXmlMessage.SendLocationInfo sendLocationInfo) { - this.sendLocationInfo = sendLocationInfo; + public static WxCpXmlMessage fromEncryptedXml(InputStream is, WxCpConfigStorage wxCpConfigStorage, + String timestamp, String nonce, String msgSignature) { + try { + return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxCpConfigStorage, timestamp, nonce, msgSignature); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override public String toString() { - return "WxCpXmlMessage{" + - "agentId=" + this.agentId + - ", toUserName='" + this.toUserName + '\'' + - ", fromUserName='" + this.fromUserName + '\'' + - ", createTime=" + this.createTime + - ", msgType='" + this.msgType + '\'' + - ", content='" + this.content + '\'' + - ", msgId=" + this.msgId + - ", picUrl='" + this.picUrl + '\'' + - ", mediaId='" + this.mediaId + '\'' + - ", format='" + this.format + '\'' + - ", thumbMediaId='" + this.thumbMediaId + '\'' + - ", locationX=" + this.locationX + - ", locationY=" + this.locationY + - ", scale=" + this.scale + - ", label='" + this.label + '\'' + - ", title='" + this.title + '\'' + - ", description='" + this.description + '\'' + - ", url='" + this.url + '\'' + - ", event='" + this.event + '\'' + - ", eventKey='" + this.eventKey + '\'' + - ", ticket='" + this.ticket + '\'' + - ", latitude=" + this.latitude + - ", longitude=" + this.longitude + - ", precision=" + this.precision + - ", recognition='" + this.recognition + '\'' + - ", status='" + this.status + '\'' + - ", totalCount=" + this.totalCount + - ", filterCount=" + this.filterCount + - ", sentCount=" + this.sentCount + - ", errorCount=" + this.errorCount + - ", scanCodeInfo=" + this.scanCodeInfo + - ", sendPicsInfo=" + this.sendPicsInfo + - ", sendLocationInfo=" + this.sendLocationInfo + - '}'; + return WxCpGsonBuilder.create().toJson(this); } + @Data @XStreamAlias("ScanCodeInfo") - public static class ScanCodeInfo { + public static class ScanCodeInfo implements Serializable { + private static final long serialVersionUID = 7420078330239763395L; + /** + * 扫描类型,一般是qrcode. + */ @XStreamAlias("ScanType") @XStreamConverter(value = XStreamCDataConverter.class) private String scanType; + /** + * 扫描结果,即二维码对应的字符串信息. + */ @XStreamAlias("ScanResult") @XStreamConverter(value = XStreamCDataConverter.class) private String scanResult; + } - /** - * 扫描类型,一般是qrcode - */ - public String getScanType() { - - return this.scanType; - } + @Data + public static class ExtAttr implements Serializable { + private static final long serialVersionUID = -3418685294606228837L; - public void setScanType(String scanType) { - this.scanType = scanType; - } + @XStreamImplicit(itemFieldName = "Item") + protected final List items = new ArrayList<>(); - /** - * 扫描结果,即二维码对应的字符串信息 - */ - public String getScanResult() { - return this.scanResult; - } + @XStreamAlias("Item") + @Data + public static class Item { + @XStreamAlias("Name") + @XStreamConverter(value = XStreamCDataConverter.class) + private String name; - public void setScanResult(String scanResult) { - this.scanResult = scanResult; + @XStreamAlias("Value") + @XStreamConverter(value = XStreamCDataConverter.class) + private String value; } - } + @Data @XStreamAlias("SendPicsInfo") - public static class SendPicsInfo { + public static class SendPicsInfo implements Serializable { + private static final long serialVersionUID = -6549728838848064881L; @XStreamAlias("PicList") protected final List picList = new ArrayList<>(); + @XStreamAlias("Count") private Long count; - public Long getCount() { - return this.count; - } - - public void setCount(Long count) { - this.count = count; - } - - public List getPicList() { - return this.picList; - } - @XStreamAlias("item") + @Data public static class Item { - @XStreamAlias("PicMd5Sum") @XStreamConverter(value = XStreamCDataConverter.class) - private String PicMd5Sum; - - public String getPicMd5Sum() { - return this.PicMd5Sum; - } - - public void setPicMd5Sum(String picMd5Sum) { - this.PicMd5Sum = picMd5Sum; - } + private String picMd5Sum; } } + @Data @XStreamAlias("SendLocationInfo") - public static class SendLocationInfo { + public static class SendLocationInfo implements Serializable { + private static final long serialVersionUID = 6319921071637597406L; @XStreamAlias("Location_X") @XStreamConverter(value = XStreamCDataConverter.class) @@ -623,47 +528,64 @@ public static class SendLocationInfo { @XStreamAlias("Poiname") @XStreamConverter(value = XStreamCDataConverter.class) - private String poiname; - - public String getLocationX() { - return this.locationX; - } - - public void setLocationX(String locationX) { - this.locationX = locationX; - } - - public String getLocationY() { - return this.locationY; - } - - public void setLocationY(String locationY) { - this.locationY = locationY; - } + private String poiName; - public String getScale() { - return this.scale; - } + } - public void setScale(String scale) { - this.scale = scale; - } + @XStreamAlias("ApprovalInfo") + @Data + public static class ApprovalInfo implements Serializable { + private static final long serialVersionUID = 8136329462880646091L; - public String getLabel() { - return this.label; - } + /** + * 审批编号 + */ + @XStreamAlias("SpNo") + private String spNo; + /** + * 审批申请类型名称(审批模板名称) + */ + @XStreamAlias("SpName") + private String spName; + /** + * 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付 + */ + @XStreamAlias("SpStatus") + private Integer spStatus; - public void setLabel(String label) { - this.label = label; - } + /** + * 审批模板id。 + */ + @XStreamAlias("TemplateId") + private String templateId; + /** + * 审批申请提交时间,Unix时间戳 + */ + @XStreamAlias("ApplyTime") + private Integer applyTime; - public String getPoiname() { - return this.poiname; + /** + * 申请人信息 + */ + @XStreamAlias("Applyer") + private Applier applier; + /** + * 审批申请单变化类型 + */ + @XStreamAlias("StatuChangeEvent") + private Integer statusChangeEvent; + + @XStreamAlias("Applyer") + @Data + public static class Applier implements Serializable { + private static final long serialVersionUID = -979255011922209018L; + + @XStreamAlias("Applyer") + private String userId; + @XStreamAlias("Party") + private String party; } - public void setPoiname(String poiname) { - this.poiname = poiname; - } } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java index 35cbe75424..cbef3f8766 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java @@ -2,11 +2,14 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; - +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = false) public class WxCpXmlOutImageMessage extends WxCpXmlOutMessage { private static final long serialVersionUID = -1099446240667237313L; @@ -15,15 +18,7 @@ public class WxCpXmlOutImageMessage extends WxCpXmlOutMessage { private String mediaId; public WxCpXmlOutImageMessage() { - this.msgType = WxConsts.XML_MSG_IMAGE; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; + this.msgType = WxConsts.XmlMsgType.IMAGE; } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java index 0d90a012c7..a053a5460e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java @@ -1,18 +1,29 @@ package me.chanjar.weixin.cp.bean; +import java.io.Serializable; + import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.bean.outxmlbuilder.*; +import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder; +import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder; +import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder; +import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder; +import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; -import java.io.Serializable; - +/** + * 被动回复消息. + * https://work.weixin.qq.com/api/doc#12975 + * + * @author Daniel Qian + */ @XStreamAlias("xml") +@Data public abstract class WxCpXmlOutMessage implements Serializable { - private static final long serialVersionUID = 1418629839964153110L; @XStreamAlias("ToUserName") @@ -31,78 +42,46 @@ public abstract class WxCpXmlOutMessage implements Serializable { protected String msgType; /** - * 获得文本消息builder + * 获得文本消息builder. */ public static TextBuilder TEXT() { return new TextBuilder(); } /** - * 获得图片消息builder + * 获得图片消息builder. */ public static ImageBuilder IMAGE() { return new ImageBuilder(); } /** - * 获得语音消息builder + * 获得语音消息builder. */ public static VoiceBuilder VOICE() { return new VoiceBuilder(); } /** - * 获得视频消息builder + * 获得视频消息builder. */ public static VideoBuilder VIDEO() { return new VideoBuilder(); } /** - * 获得图文消息builder + * 获得图文消息builder. */ public static NewsBuilder NEWS() { return new NewsBuilder(); } - public String getToUserName() { - return this.toUserName; - } - - public void setToUserName(String toUserName) { - this.toUserName = toUserName; - } - - public String getFromUserName() { - return this.fromUserName; - } - - public void setFromUserName(String fromUserName) { - this.fromUserName = fromUserName; - } - - public Long getCreateTime() { - return this.createTime; - } - - public void setCreateTime(Long createTime) { - this.createTime = createTime; - } - - public String getMsgType() { - return this.msgType; - } - - public void setMsgType(String msgType) { - this.msgType = msgType; - } - protected String toXml() { return XStreamTransformer.toXml((Class) this.getClass(), this); } /** - * 转换成加密的xml格式 + * 转换成加密的xml格式. */ public String toEncryptedXml(WxCpConfigStorage wxCpConfigStorage) { String plainXml = toXml(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java index 8910f03fa6..c8149cabfa 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java @@ -1,93 +1,57 @@ package me.chanjar.weixin.cp.bean; -import java.util.ArrayList; -import java.util.List; - import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; - +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import java.util.ArrayList; +import java.util.List; + @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage { private static final long serialVersionUID = -5796178637883178826L; @XStreamAlias("Articles") protected final List articles = new ArrayList<>(); + @XStreamAlias("ArticleCount") protected int articleCount; public WxCpXmlOutNewsMessage() { - this.msgType = WxConsts.XML_MSG_NEWS; + this.msgType = WxConsts.XmlMsgType.NEWS; } - public int getArticleCount() { - return this.articleCount; - } public void addArticle(Item item) { this.articles.add(item); this.articleCount = this.articles.size(); } - public List getArticles() { - return this.articles; - } - - @XStreamAlias("item") + @Data public static class Item { @XStreamAlias("Title") @XStreamConverter(value = XStreamCDataConverter.class) - private String Title; + private String title; @XStreamAlias("Description") @XStreamConverter(value = XStreamCDataConverter.class) - private String Description; + private String description; @XStreamAlias("PicUrl") @XStreamConverter(value = XStreamCDataConverter.class) - private String PicUrl; + private String picUrl; @XStreamAlias("Url") @XStreamConverter(value = XStreamCDataConverter.class) - private String Url; - - public String getTitle() { - return this.Title; - } - - public void setTitle(String title) { - this.Title = title; - } - - public String getDescription() { - return this.Description; - } - - public void setDescription(String description) { - this.Description = description; - } - - public String getPicUrl() { - return this.PicUrl; - } - - public void setPicUrl(String picUrl) { - this.PicUrl = picUrl; - } - - public String getUrl() { - return this.Url; - } - - public void setUrl(String url) { - this.Url = url; - } + private String url; } - } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java index 532e06c341..6589b0b3b6 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java @@ -2,11 +2,14 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; - +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = false) public class WxCpXmlOutTextMessage extends WxCpXmlOutMessage { private static final long serialVersionUID = 2569239617185930232L; @@ -15,16 +18,7 @@ public class WxCpXmlOutTextMessage extends WxCpXmlOutMessage { private String content; public WxCpXmlOutTextMessage() { - this.msgType = WxConsts.XML_MSG_TEXT; - } - - public String getContent() { - return this.content; + this.msgType = WxConsts.XmlMsgType.TEXT; } - public void setContent(String content) { - this.content = content; - } - - } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java index 73c56d4c2c..f4aabd182b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java @@ -2,11 +2,14 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; - +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = false) public class WxCpXmlOutVideoMessage extends WxCpXmlOutMessage { private static final long serialVersionUID = -8672761162722733622L; @@ -14,7 +17,7 @@ public class WxCpXmlOutVideoMessage extends WxCpXmlOutMessage { protected final Video video = new Video(); public WxCpXmlOutVideoMessage() { - this.msgType = WxConsts.XML_MSG_VIDEO; + this.msgType = WxConsts.XmlMsgType.VIDEO; } public String getMediaId() { @@ -41,7 +44,7 @@ public void setDescription(String description) { this.video.setDescription(description); } - + @Data @XStreamAlias("Video") public static class Video { @@ -57,30 +60,6 @@ public static class Video { @XStreamConverter(value = XStreamCDataConverter.class) private String description; - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java index bba95cfdbf..efe4a86fcf 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java @@ -2,11 +2,14 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; - +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = false) public class WxCpXmlOutVoiceMessage extends WxCpXmlOutMessage { private static final long serialVersionUID = -7947384031546099340L; @@ -15,15 +18,7 @@ public class WxCpXmlOutVoiceMessage extends WxCpXmlOutMessage { private String mediaId; public WxCpXmlOutVoiceMessage() { - this.msgType = WxConsts.XML_MSG_VOICE; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; + this.msgType = WxConsts.XmlMsgType.VOICE; } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java new file mode 100644 index 0000000000..2b2a9fe051 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.cp.bean.article; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + *  Created by BinaryWang on 2017/3/27.
    + * 
    + * + * @author Binary Wang + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder(builderMethodName = "newBuilder") +public class MpnewsArticle implements Serializable { + private static final long serialVersionUID = 6985871812170756481L; + + /** + * 标题,不超过128个字节,超过会自动截断 + */ + private String title; + /** + * 图文消息缩略图的media_id, 可以通过素材管理接口获得。此处thumb_media_id即上传接口返回的media_id + */ + private String thumbMediaId; + /** + * 图文消息的作者,不超过64个字节 + */ + private String author; + /** + * 图文消息点击“阅读原文”之后的页面链接 + */ + private String contentSourceUrl; + /** + * 图文消息的内容,支持html标签,不超过666 K个字节 + */ + private String content; + /** + * 图文消息的描述,不超过512个字节,超过会自动截断 + */ + private String digest; + /** + * 可能已经废弃了,官方文档里已经看不到了 + */ + @Deprecated + private String showCoverPic; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java new file mode 100644 index 0000000000..5eede5fa53 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.cp.bean.article; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + *  Created by BinaryWang on 2017/3/27.
    + * 
    + * + * @author Binary Wang + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class NewArticle implements Serializable { + private static final long serialVersionUID = 4087852055781140659L; + /** + * 标题,不超过128个字节,超过会自动截断 + */ + private String title; + /** + * 描述,不超过512个字节,超过会自动截断 + */ + private String description; + /** + * 点击后跳转的链接。 + */ + private String url; + /** + * 图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图1068*455,小图150*150。 + */ + private String picUrl; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java new file mode 100644 index 0000000000..21c2696b6f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java @@ -0,0 +1,225 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpConclusionAdapter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * 「联系我」方式 对象 + * + * @author element + */ +@Data +@NoArgsConstructor +public class WxCpContactWayInfo { + + @SerializedName("contact_way") + private ContactWay contactWay; + + @Getter + @Setter + public static class ContactWay { + /** + * 联系方式的配置id + */ + @SerializedName("config_id") + private String configId; + + /** + *
    +     * 必填
    +     * 联系方式类型,1-单人, 2-多人
    +     * 
    + */ + private TYPE type; + + /** + *
    +     * 必填
    +     * 场景,1-在小程序中联系,2-通过二维码联系
    +     * 
    + */ + private SCENE scene; + + /** + *
    +     * 非必填
    +     * 在小程序中联系时使用的控件样式
    +     * 单人样式(type=1)时可选1,2,3
    +     * 多人样式(type=2)时可选1,2
    +     * 
    + */ + private Integer style; + + /** + *
    +     * 非必填
    +     * 联系方式的备注信息,用于助记,不超过30个字符
    +     * 
    + */ + private String remark; + + /** + *
    +     * 非必填
    +     * 外部客户添加时是否无需验证,默认为true
    +     * 
    + */ + @SerializedName("skip_verify") + private Boolean skipVerify = Boolean.TRUE; + + /** + *
    +     * 非必填
    +     * 企业自定义的state参数,用于区分不同的添加渠道,在调用“获取外部联系人详情(WxCpExternalContactService.getContactDetail)”  时会返回该参数值,不超过30个字符
    +     * 
    + */ + private String state; + + /** + *
    +     * 联系二维码的URL,仅在scene为2时返回
    +     * 
    + */ + @SerializedName("qr_code") + private String qrCode; + + /** + *
    +     * 使用该联系方式的用户userID列表,在type为1时为必填,且只能有一个
    +     * 
    + */ + @SerializedName("user") + private List users; + + + /** + *
    +     * 非必填
    +     * 使用该联系方式的部门id列表,只在type为2时有效
    +     * 
    + */ + @SerializedName("party") + private List parties; + + /** + *
    +     * 非必填
    +     * 是否临时会话模式,true表示使用临时会话模式,默认为false
    +     * 
    + */ + @SerializedName("is_temp") + private Boolean isTemp = Boolean.FALSE; + + /** + *
    +     * 非必填
    +     * 临时会话二维码有效期,以秒为单位。该参数仅在is_temp为true时有效,默认7天
    +     * 
    + */ + @SerializedName("expires_in") + private Integer expiresIn; + + /** + *
    +     * 非必填
    +     * 临时会话有效期,以秒为单位。该参数仅在is_temp为true时有效,默认为添加好友后24小时
    +     * 
    + */ + @SerializedName("chat_expires_in") + private Integer chatExpiresIn; + + /** + *
    +     * 非必填
    +     * 可进行临时会话的客户unionid,该参数仅在is_temp为true时有效,如不指定则不进行限制
    +     * 
    + */ + @SerializedName("unionid") + private String unionId; + + /** + *
    +     * 非必填
    +     * 结束语,会话结束时自动发送给客户,可参考“结束语定义”,仅在is_temp为true时有效
    +     * 
    + */ + private Conclusion conclusions; + + public static WxCpContactWayInfo.ContactWay fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpContactWayInfo.ContactWay.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + /** + * 结束语定义 + */ + @Data + @JsonAdapter(WxCpConclusionAdapter.class) + public static class Conclusion { + private String textContent; + private String imgMediaId; + private String imgPicUrl; + private String linkTitle; + private String linkPicUrl; + private String linkDesc; + private String linkUrl; + private String miniProgramTitle; + private String miniProgramPicMediaId; + private String miniProgramAppId; + private String miniProgramPage; + } + + } + + + public static WxCpContactWayInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpContactWayInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + public enum TYPE { + /** + * 单人 + */ + @SerializedName("1") + SINGLE, + + /** + * 多人 + */ + @SerializedName("2") + MULTI; + + } + + public enum SCENE { + + /** + * 在小程序中联系 + */ + @SerializedName("1") + MINIPROGRAM, + + /** + * 通过二维码联系 + */ + @SerializedName("2") + QRCODE; + + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayResult.java new file mode 100644 index 0000000000..609673a193 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayResult.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 「联系我」方式 处理结果 + */ +@Data +public class WxCpContactWayResult extends WxCpBaseResp { + @SerializedName("config_id") + private String configId; + + public static WxCpContactWayResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpContactWayResult.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java new file mode 100644 index 0000000000..295eecb636 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.external.msg.Image; +import me.chanjar.weixin.cp.bean.external.msg.Link; +import me.chanjar.weixin.cp.bean.external.msg.MiniProgram; +import me.chanjar.weixin.cp.bean.external.msg.Text; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 企业群发消息任务 + *

    + * Created by songfan on 2020/7/14. + * + * @author songfan + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxCpMsgTemplate implements Serializable { + private static final long serialVersionUID = 3172331565173474358L; + + @SerializedName("chat_type") + private String chatType; + + @SerializedName("external_userid") + private List externalUserid; + + private String sender; + + private Text text; + + private Image image; + + private Link link; + + private MiniProgram miniprogram; + + public static WxCpMsgTemplate fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpMsgTemplate.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplateAddResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplateAddResult.java new file mode 100644 index 0000000000..87e0be4467 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplateAddResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * Created by songfan on 2020/7/14. + * + * @author songfan + */ +@Data +public class WxCpMsgTemplateAddResult implements Serializable { + private static final long serialVersionUID = -5166048319463473188L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("fail_list") + private List failList; + + @SerializedName("msgid") + private String msgId; + + public static WxCpMsgTemplateAddResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpMsgTemplateAddResult.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactInfo.java new file mode 100644 index 0000000000..c28326e849 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactInfo.java @@ -0,0 +1,144 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.*; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + *

    + * 外部联系人详情
    + * Created by Binary Wang on 2018/9/16.
    + * 参考文档:https://work.weixin.qq.com/api/doc#13878
    + * 
    + * + * @author Binary Wang + */ +@Getter +@Setter +public class WxCpUserExternalContactInfo { + @SerializedName("external_contact") + private ExternalContact externalContact; + + @SerializedName("follow_user") + private List followedUsers; + + @Getter + @Setter + public static class ExternalContact { + @SerializedName("external_userid") + private String externalUserId; + + @SerializedName("position") + private String position; + + @SerializedName("name") + private String name; + + @SerializedName("avatar") + private String avatar; + + @SerializedName("corp_name") + private String corpName; + + @SerializedName("corp_full_name") + private String corpFullName; + + @SerializedName("type") + private Integer type; + + @SerializedName("gender") + private Integer gender; + + @SerializedName("unionid") + private String unionId; + + @SerializedName("external_profile") + private ExternalProfile externalProfile; + } + + @Setter + @Getter + public static class ExternalProfile { + @SerializedName("external_attr") + private List externalAttrs; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ExternalAttribute { + @Setter + @Getter + public static class Text { + private String value; + } + + @Setter + @Getter + public static class Web { + private String title; + private String url; + } + + @Setter + @Getter + public static class MiniProgram { + @SerializedName("pagepath") + private String pagePath; + private String appid; + private String title; + } + + private int type; + + private String name; + + private Text text; + + private Web web; + + @SerializedName("miniprogram") + private MiniProgram miniProgram; + } + + @Setter + @Getter + public static class FollowedUser { + @SerializedName("userid") + private String userId; + private String remark; + private String description; + @SerializedName("createtime") + private Long createTime; + private String state; + @SerializedName("remark_company") + private String remarkCompany; + @SerializedName("remark_mobiles") + private String[] remarkMobiles; + private Tag[] tags; + @SerializedName("remark_corp_name") + private String remarkCorpName; + @SerializedName("add_way") + private String addWay; + @SerializedName("oper_userid") + private String operUserId; + + } + + public static WxCpUserExternalContactInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactInfo.class); + } + + @Setter + @Getter + public static class Tag { + @SerializedName("group_name") + private String groupName; + @SerializedName("tag_name") + private String tagName; + private int type; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactList.java new file mode 100644 index 0000000000..1320e38942 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactList.java @@ -0,0 +1,58 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + *
    + * 外部联系人列表
    + * Created by Joe Cao on 2019/6/16.
    + * 参考文档:https://work.weixin.qq.com/api/doc#90001/90143/91570
    + * 
    + * + * @author Joe Cao + */ +public class WxCpUserExternalContactList { + @SerializedName("errcode") + @Expose + private Long errcode; + @SerializedName("errmsg") + @Expose + private String errmsg; + + @SerializedName("external_userid") + @Expose + private List externalUserId = null; + + public Long getErrcode() { + return errcode; + } + + public void setErrcode(Long errcode) { + this.errcode = errcode; + } + + public String getErrmsg() { + return errmsg; + } + + public void setErrmsg(String errmsg) { + this.errmsg = errmsg; + } + + + public List getExternalUserId() { + return externalUserId; + } + + public void setExternalUserId(List externalUserId) { + this.externalUserId = externalUserId; + } + + public static WxCpUserExternalContactList fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactList.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatInfo.java new file mode 100644 index 0000000000..0c33309bca --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatInfo.java @@ -0,0 +1,76 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * @author yqx + * @date 2020/3/116 + */ +@Getter +@Setter +public class WxCpUserExternalGroupChatInfo extends WxCpBaseResp { + + @SerializedName("group_chat") + private GroupChat groupChat; + + @Getter + @Setter + public static class GroupChat { + @SerializedName("chat_id") + private String chatId; + + @SerializedName("name") + private String name; + + @SerializedName("owner") + private String owner; + + @SerializedName("create_time") + private Long createTime; + + @SerializedName("notice") + private String notice; + + @SerializedName("member_list") + private List memberList; + + } + + @Getter + @Setter + public static class GroupMember { + @SerializedName("userid") + private String userId; + + /** + * 成员类型。 + * 1 - 企业成员 + * 2 - 外部联系人 + */ + @SerializedName("type") + private int type; + + @SerializedName("join_time") + private Long joinTime; + + /** + * 入群方式。 + * 1 - 由成员邀请入群(直接邀请入群) + * 2 - 由成员邀请入群(通过邀请链接入群) + * 3 - 通过扫描群二维码入群 + */ + @SerializedName("join_scene") + private int joinScene; + + } + + public static WxCpUserExternalGroupChatInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalGroupChatInfo.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatList.java new file mode 100644 index 0000000000..8215377e4f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatList.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * @author yqx + * @date 2020/3/116 + */ +@Getter +@Setter +public class WxCpUserExternalGroupChatList extends WxCpBaseResp { + + @SerializedName("group_chat_list") + private List groupChatList; + + @Getter + @Setter + public static class ChatStatus { + + /** + * 客户群ID + */ + @SerializedName("chat_id") + private String chatId; + + /** + * 客户群状态 + * 0 - 正常 + * 1 - 跟进人离职 + * 2 - 离职继承中 + * 3 - 离职继承完成 + */ + @SerializedName("status") + private int status; + + } + + public static WxCpUserExternalGroupChatList fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalGroupChatList.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatStatistic.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatStatistic.java new file mode 100644 index 0000000000..dd57565321 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatStatistic.java @@ -0,0 +1,95 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 联系客户群统计数据 + * + * @author yqx + * @date 2020/3/16 + */ +@Getter +@Setter +public class WxCpUserExternalGroupChatStatistic extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = -3548998672207956622L; + + @SerializedName("total") + private int total; + + @SerializedName("next_offset") + private int nextOffset; + + @SerializedName("items") + private List itemList; + + @Getter + @Setter + public static class StatisticItem implements Serializable { + private static final long serialVersionUID = -7272935708787587856L; + + @SerializedName("owner") + private String owner; + + @SerializedName("data") + private ItemData itemData; + } + + @Getter + @Setter + public static class ItemData implements Serializable { + private static final long serialVersionUID = 354382008606856587L; + + /** + * 新增客户群数量 + */ + @SerializedName("new_chat_cnt") + private int newChatCnt; + + /** + * 截至当天客户群总数量 + */ + @SerializedName("chat_total") + private int chatTotal; + + /** + * 截至当天有发过消息的客户群数量 + */ + @SerializedName("chat_has_msg") + private int chatHasMsg; + + /** + * 客户群新增群人数。 + */ + @SerializedName("new_member_cnt") + private int newMemberCnt; + + /** + * 截至当天客户群总人数 + */ + @SerializedName("member_total") + private int memberTotal; + + /** + * 截至当天有发过消息的群成员数 + */ + @SerializedName("member_has_msg") + private int memberHasMsg; + + /** + * 截至当天客户群消息总数 + */ + @SerializedName("msg_total") + private int msgTotal; + } + + public static WxCpUserExternalGroupChatStatistic fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalGroupChatStatistic.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupInfo.java new file mode 100644 index 0000000000..97ff3f2571 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupInfo.java @@ -0,0 +1,81 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * + */ +@Getter +@Setter +public class WxCpUserExternalTagGroupInfo extends WxCpBaseResp { + + @SerializedName("tag_group") + private TagGroup tagGroup; + + @Getter + @Setter + public static class TagGroup { + + @SerializedName("group_id") + private String groupId; + + @SerializedName("group_name") + private String groupName; + + @SerializedName("create_time") + private Long createTime; + + @SerializedName("order") + private Integer order; + + @SerializedName("deleted") + private Boolean deleted; + + @SerializedName("tag") + private List tag; + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + } + + @Getter + @Setter + public static class Tag { + + /** + * 客户群ID + */ + @SerializedName("id") + private String id; + + + @SerializedName("name") + private String name; + + @SerializedName("create_time") + private Long createTime; + + @SerializedName("order") + private Integer order; + + @SerializedName("deleted") + private Boolean deleted; + + } + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + + public static WxCpUserExternalTagGroupInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalTagGroupInfo.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupList.java new file mode 100644 index 0000000000..bb15565161 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalTagGroupList.java @@ -0,0 +1,76 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * + */ +@Getter +@Setter +public class WxCpUserExternalTagGroupList extends WxCpBaseResp { + + @SerializedName("tag_group") + private List tagGroupList; + + @Getter + @Setter + public static class TagGroup{ + @SerializedName("group_id") + private String groupId; + + @SerializedName("group_name") + private String groupName; + + @SerializedName("create_time") + private Long createTime; + + @SerializedName("order") + private Integer order; + + @SerializedName("deleted") + private Boolean deleted; + + + @SerializedName("tag") + private List tag; + + @Getter + @Setter + public static class Tag { + + /** + * 客户群ID + */ + @SerializedName("id") + private String id; + + @SerializedName("name") + private String name; + + @SerializedName("create_time") + private Long createTime; + + @SerializedName("order") + private Integer order; + + @SerializedName("deleted") + private Boolean deleted; + + } + } + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + + public static WxCpUserExternalTagGroupList fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalTagGroupList.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java new file mode 100644 index 0000000000..2870eb15bc --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * 离职员工外部联系人列表 + * @author yqx + * @date 2020/3/15 + */ +@Getter +@Setter +public class WxCpUserExternalUnassignList extends WxCpBaseResp { + + @SerializedName("info") + private List unassignInfos; + + @SerializedName("is_last") + private boolean isLast; + + @Getter + @Setter + public static class UnassignInfo { + + /** + * 离职成员userid + */ + @SerializedName("handover_userid") + private String handoverUserid; + + /** + * 外部联系人userid + */ + @SerializedName("external_userid") + private String externalUserid; + + /** + * 成员离职时间 + */ + @SerializedName("dimission_time") + private Long dimissionTime; + } + + public static WxCpUserExternalUnassignList fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalUnassignList.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUserBehaviorStatistic.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUserBehaviorStatistic.java new file mode 100644 index 0000000000..48fcbcba30 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUserBehaviorStatistic.java @@ -0,0 +1,79 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * 联系客户统计数据 + * @author yqx + * @date 2020/3/16 + */ +@Getter +@Setter +public class WxCpUserExternalUserBehaviorStatistic extends WxCpBaseResp { + + @SerializedName("behavior_data") + private List behaviorList; + + @Getter + @Setter + public static class Behavior { + + /** + * 数据日期,为当日0点的时间戳 + */ + @SerializedName("stat_time") + private Long statTime; + + /** + * 聊天总数, 成员有主动发送过消息的聊天数,包括单聊和群聊。 + */ + @SerializedName("chat_cnt") + private int chatCnt; + + /** + * 发送消息数,成员在单聊和群聊中发送的消息总数。 + */ + @SerializedName("message_cnt") + private int messageCnt; + + /** + * 已回复聊天占比,客户主动发起聊天后,成员在一个自然日内有回复过消息的聊天数/客户主动发起的聊天数比例,不包括群聊,仅在确有回复时返回。 + */ + @SerializedName("reply_percentage") + private double replyPercentage; + + /** + * 平均首次回复时长,单位为分钟,即客户主动发起聊天后,成员在一个自然日内首次回复的时长间隔为首次回复时长,所有聊天的首次回复总时长/已回复的聊天总数即为平均首次回复时长,不包括群聊,仅在确有回复时返回。 + */ + @SerializedName("avg_reply_time") + private int avgReplyTime; + + /** + * 删除/拉黑成员的客户数,即将成员删除或加入黑名单的客户数。 + */ + @SerializedName("negative_feedback_cnt") + private int negativeFeedbackCnt; + + /** + * 发起申请数,成员通过「搜索手机号」、「扫一扫」、「从微信好友中添加」、「从群聊中添加」、「添加共享、分配给我的客户」、「添加单向、双向删除好友关系的好友」、「从新的联系人推荐中添加」等渠道主动向客户发起的好友申请数量。 + */ + @SerializedName("new_apply_cnt") + private int newApplyCnt; + + /** + * 新增客户数,成员新添加的客户数量。 + */ + @SerializedName("new_contact_cnt") + private int newContactCnt; + } + + public static WxCpUserExternalUserBehaviorStatistic fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalUserBehaviorStatistic.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserWithExternalPermission.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserWithExternalPermission.java new file mode 100644 index 0000000000..3a5c366d76 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserWithExternalPermission.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * @author 曹祖鹏 + */ +@Data +public class WxCpUserWithExternalPermission { + @SerializedName("errcode") + @Expose + private Long errCode; + @SerializedName("errmsg") + @Expose + private String errMsg; + + @SerializedName("follow_user") + @Expose + private List followers = null; + + public static WxCpUserWithExternalPermission fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpUserWithExternalPermission.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpWelcomeMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpWelcomeMsg.java new file mode 100644 index 0000000000..ce744b9f24 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpWelcomeMsg.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.*; +import me.chanjar.weixin.cp.bean.external.msg.Image; +import me.chanjar.weixin.cp.bean.external.msg.Link; +import me.chanjar.weixin.cp.bean.external.msg.MiniProgram; +import me.chanjar.weixin.cp.bean.external.msg.Text; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 新客户欢迎语. + * + * @author Binary Wang + * @date 2020-08-16 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxCpWelcomeMsg implements Serializable { + private static final long serialVersionUID = 4170843890468921757L; + + @SerializedName("welcome_code") + private String welcomeCode; + + private Text text; + + private Image image; + + private Link link; + + private MiniProgram miniprogram; + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Image.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Image.java new file mode 100644 index 0000000000..084de7fcf2 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Image.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.cp.bean.external.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 图片消息. + * + * @author Binary Wang + * @date 2020-08-16 + */ +@Data +public class Image implements Serializable { + private static final long serialVersionUID = -606286372867787121L; + + @SerializedName("media_id") + private String mediaId; + + @SerializedName("pic_url") + private String picUrl; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Link.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Link.java new file mode 100644 index 0000000000..a949a1a0f1 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Link.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.cp.bean.external.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 图文消息. + * + * @author Binary Wang + * @date 2020-08-16 + */ +@Data +public class Link implements Serializable { + private static final long serialVersionUID = -8041816740881163875L; + private String title; + @SerializedName("picurl") + private String picUrl; + private String desc; + private String url; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/MiniProgram.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/MiniProgram.java new file mode 100644 index 0000000000..1c5940c7d6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/MiniProgram.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.cp.bean.external.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 小程序消息. + * + * @author Binary Wang + * @date 2020-08-16 + */ +@Data +public class MiniProgram implements Serializable { + private static final long serialVersionUID = 4242074162638170679L; + + private String title; + @SerializedName("pic_media_id") + private String picMediaId; + private String appid; + private String page; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Text.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Text.java new file mode 100644 index 0000000000..2b5ae5fc63 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Text.java @@ -0,0 +1,17 @@ +package me.chanjar.weixin.cp.bean.external.msg; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 消息文本消息. + * + * @author Binary Wang + * @date 2020-08-16 + */ +@Data +public class Text implements Serializable { + private static final long serialVersionUID = 6608288753719551600L; + private String content; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java index 7800f1c61e..1064f00526 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java @@ -2,6 +2,7 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.cp.bean.WxCpMessage; +import org.apache.commons.lang3.StringUtils; public class BaseBuilder { protected String msgType; @@ -43,8 +44,7 @@ public WxCpMessage build() { m.setToUser(this.toUser); m.setToParty(this.toParty); m.setToTag(this.toTag); - m.setSafe( - (this.safe == null || "".equals(this.safe)) ? WxConsts.CUSTOM_MSG_SAFE_NO : this.safe); + m.setSafe(StringUtils.defaultIfBlank(this.safe, WxConsts.KefuMsgSafe.NO)); return m; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/FileBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/FileBuilder.java index 1da7816926..f67cf6e50d 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/FileBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/FileBuilder.java @@ -15,7 +15,7 @@ public final class FileBuilder extends BaseBuilder { private String mediaId; public FileBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_FILE; + this.msgType = WxConsts.KefuMsgType.FILE; } public FileBuilder mediaId(String media_id) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/ImageBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/ImageBuilder.java index a74f5016e4..ddf3b7373b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/ImageBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/ImageBuilder.java @@ -15,7 +15,7 @@ public final class ImageBuilder extends BaseBuilder { private String mediaId; public ImageBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_IMAGE; + this.msgType = WxConsts.KefuMsgType.IMAGE; } public ImageBuilder mediaId(String media_id) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MarkdownMsgBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MarkdownMsgBuilder.java new file mode 100644 index 0000000000..6e0a4a3302 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MarkdownMsgBuilder.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.cp.bean.messagebuilder; + +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.cp.bean.WxCpMessage; + +/** + *
    + * markdown类型的消息builder
    + * Created by Binary Wang on 2019/1/20.
    + * 
    + * + * @author Binary Wang + */ +public class MarkdownMsgBuilder extends BaseBuilder { + private String content; + + public MarkdownMsgBuilder() { + this.msgType = WxConsts.KefuMsgType.MARKDOWN; + } + + public MarkdownMsgBuilder content(String content) { + this.content = content; + return this; + } + + @Override + public WxCpMessage build() { + WxCpMessage m = super.build(); + m.setContent(this.content); + return m; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MiniProgramNoticeMsgBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MiniProgramNoticeMsgBuilder.java new file mode 100644 index 0000000000..cf44c0f0f7 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MiniProgramNoticeMsgBuilder.java @@ -0,0 +1,69 @@ +package me.chanjar.weixin.cp.bean.messagebuilder; + +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.cp.bean.WxCpMessage; + +import java.util.Map; + +/** + *
    + * miniprogram_notice 类型的消息 builder
    + * Created by Binary Wang on 2019/6/16.
    + * 
    + * + * @author Binary Wang + */ +public class MiniProgramNoticeMsgBuilder extends BaseBuilder { + private String title; + private String description; + private String appId; + private String page; + private Boolean emphasisFirstItem; + private Map contentItems; + + public MiniProgramNoticeMsgBuilder() { + this.msgType = WxConsts.KefuMsgType.MINIPROGRAM_NOTICE; + } + + public MiniProgramNoticeMsgBuilder appId(String appId) { + this.appId = appId; + return this; + } + + public MiniProgramNoticeMsgBuilder page(String page) { + this.page = page; + return this; + } + + public MiniProgramNoticeMsgBuilder title(String title) { + this.title = title; + return this; + } + + public MiniProgramNoticeMsgBuilder description(String description) { + this.description = description; + return this; + } + + public MiniProgramNoticeMsgBuilder contentItems(Map contentItems) { + this.contentItems = contentItems; + return this; + } + + public MiniProgramNoticeMsgBuilder emphasisFirstItem(Boolean emphasisFirstItem) { + this.emphasisFirstItem = emphasisFirstItem; + return this; + } + + @Override + public WxCpMessage build() { + WxCpMessage m = super.build(); + m.setContentItems(this.contentItems); + m.setAppId(this.appId); + m.setDescription(this.description); + m.setTitle(this.title); + m.setEmphasisFirstItem(this.emphasisFirstItem); + m.setPage(this.page); + return m; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java index 21ab2549a7..75739803f4 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java @@ -5,6 +5,7 @@ import me.chanjar.weixin.cp.bean.article.MpnewsArticle; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -22,7 +23,7 @@ public final class MpnewsBuilder extends BaseBuilder { private String mediaId; public MpnewsBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_MPNEWS; + this.msgType = WxConsts.KefuMsgType.MPNEWS; } public MpnewsBuilder mediaId(String mediaId) { @@ -30,8 +31,13 @@ public MpnewsBuilder mediaId(String mediaId) { return this; } - public MpnewsBuilder addArticle(MpnewsArticle article) { - this.articles.add(article); + public MpnewsBuilder addArticle(MpnewsArticle... articles) { + Collections.addAll(this.articles, articles); + return this; + } + + public MpnewsBuilder articles(List articles) { + this.articles = articles; return this; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java index e0df095e3d..9d0d2f603a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java @@ -5,6 +5,7 @@ import me.chanjar.weixin.cp.bean.article.NewArticle; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -21,11 +22,16 @@ public final class NewsBuilder extends BaseBuilder { private List articles = new ArrayList<>(); public NewsBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_NEWS; + this.msgType = WxConsts.KefuMsgType.NEWS; } - public NewsBuilder addArticle(NewArticle article) { - this.articles.add(article); + public NewsBuilder addArticle(NewArticle... articles) { + Collections.addAll(this.articles, articles); + return this; + } + + public NewsBuilder articles(List articles) { + this.articles = articles; return this; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java new file mode 100644 index 0000000000..3c2d77924d --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.cp.bean.messagebuilder; + +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.cp.bean.WxCpMessage; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; + +import java.util.List; + +/** + *
    + * 任务卡片消息Builder
    + * 用法: WxCustomMessage m = WxCustomMessage.TASKCARD().title(...)....toUser(...).build();
    + * 
    + * + * @author Jeff + * @date 2019-05-16 + */ +public class TaskCardBuilder extends BaseBuilder { + private String title; + private String description; + private String url; + private String taskId; + /** + * 按钮个数为1~2个 + */ + private List buttons; + + public TaskCardBuilder() { + this.msgType = WxConsts.KefuMsgType.TASKCARD; + } + + public TaskCardBuilder title(String title) { + this.title = title; + return this; + } + + public TaskCardBuilder description(String description) { + this.description = description; + return this; + } + + public TaskCardBuilder url(String url) { + this.url = url; + return this; + } + + public TaskCardBuilder taskId(String taskId) { + this.taskId = taskId; + return this; + } + + public TaskCardBuilder buttons(List buttons) { + this.buttons = buttons; + return this; + } + + @Override + public WxCpMessage build() { + WxCpMessage m = super.build(); + m.setSafe(null); + m.setTitle(this.title); + m.setDescription(this.description); + m.setUrl(this.url); + m.setTaskId(this.taskId); + m.setTaskButtons(this.buttons); + return m; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextBuilder.java index 8c1ee1d014..5079b5f846 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextBuilder.java @@ -15,7 +15,7 @@ public final class TextBuilder extends BaseBuilder { private String content; public TextBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_TEXT; + this.msgType = WxConsts.KefuMsgType.TEXT; } public TextBuilder content(String content) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextCardBuilder.java new file mode 100644 index 0000000000..6cae763d19 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextCardBuilder.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.cp.bean.messagebuilder; + +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.cp.bean.WxCpMessage; + +/** + *
    + * 文本卡片消息Builder
    + * 用法: WxCustomMessage m = WxCustomMessage.TEXTCARD().title(...)....toUser(...).build();
    + * Created by Binary Wang on 2017-7-2.
    + * 
    + * + * @author Binary Wang + */ +public class TextCardBuilder extends BaseBuilder { + private String title; + private String description; + private String url; + private String btnTxt; + + public TextCardBuilder() { + this.msgType = WxConsts.KefuMsgType.TEXTCARD; + } + + public TextCardBuilder title(String title) { + this.title = title; + return this; + } + + public TextCardBuilder description(String description) { + this.description = description; + return this; + } + + public TextCardBuilder url(String url) { + this.url = url; + return this; + } + + public TextCardBuilder btnTxt(String btnTxt) { + this.btnTxt = btnTxt; + return this; + } + + @Override + public WxCpMessage build() { + WxCpMessage m = super.build(); + m.setTitle(this.title); + m.setDescription(this.description); + m.setUrl(this.url); + m.setBtnTxt(this.btnTxt); + return m; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VideoBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VideoBuilder.java index 52cba28703..8d47399407 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VideoBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VideoBuilder.java @@ -24,7 +24,7 @@ public final class VideoBuilder extends BaseBuilder { private String thumbMediaId; public VideoBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_VIDEO; + this.msgType = WxConsts.KefuMsgType.VIDEO; } public VideoBuilder mediaId(String mediaId) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VoiceBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VoiceBuilder.java index 0960fe8f0c..33c36abcbe 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VoiceBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VoiceBuilder.java @@ -15,7 +15,7 @@ public final class VoiceBuilder extends BaseBuilder { private String mediaId; public VoiceBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_VOICE; + this.msgType = WxConsts.KefuMsgType.VOICE; } public VoiceBuilder mediaId(String media_id) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java new file mode 100644 index 0000000000..49222cd0a5 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.bean.oa.WxCpOaApplyEventRequest; + +import java.io.Serializable; +import java.util.List; + +/** + * 摘要行信息,用于定义某一行摘要显示的内容. + * + * @author Binary Wang + * @date 2020-07-19 + */ +@Data +@Accessors(chain = true) +public class SummaryInfo implements Serializable { + private static final long serialVersionUID = 8262265774851382414L; + /** + * 摘要行信息,用于定义某一行摘要显示的内容 + */ + @SerializedName("summary_info") + private List summaryInfoData; + + @Data + @Accessors(chain = true) + public static class SummaryInfoData implements Serializable { + private static final long serialVersionUID = 5314161929610113856L; + /** + * 摘要行显示文字,用于记录列表和消息通知的显示,不要超过20个字符 + */ + private String text; + + /** + * 摘要行显示语言 + */ + private String lang; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplier.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplier.java new file mode 100644 index 0000000000..b9b1af3c3a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplier.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 申请人信息 + * @author element + */ +@Data +public class WxCpApprovalApplier extends WxCpOperator implements Serializable { + + private static final long serialVersionUID = -8974662568286821271L; + + /** + * 申请人所在部门id + */ + @SerializedName("partyid") + private String partyId; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyData.java new file mode 100644 index 0000000000..75427b02ed --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyData.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.cp.bean.oa; + +import lombok.Data; +import me.chanjar.weixin.cp.bean.oa.applydata.ApplyDataContent; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批申请数据 + * + * @author element + */ +@Data +public class WxCpApprovalApplyData implements Serializable { + + private static final long serialVersionUID = 4061352949894274704L; + + private List contents; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalComment.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalComment.java new file mode 100644 index 0000000000..1bf06ec51b --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalComment.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批申请备注信息 + * + * @author element + */ +@Data +public class WxCpApprovalComment implements Serializable { + private static final long serialVersionUID = -5430367411926856292L; + + /** + * 备注人信息 + */ + private WxCpOperator commentUserInfo; + + /** + * 备注提交时间戳,Unix时间戳 + */ + @SerializedName("commenttime") + private Long commentTime; + + /** + * 备注id + */ + @SerializedName("commentid") + private String commentId; + + /** + * 备注文本内容 + */ + @SerializedName("commentcontent") + private String commentContent; + + /** + * 备注附件id,可能有多个 + */ + @SerializedName("media_id") + private List mediaIds; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java new file mode 100644 index 0000000000..cba31fc27c --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java @@ -0,0 +1,78 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批申请详情 + * + * @author element + */ +@Data +public class WxCpApprovalDetail implements Serializable { + private static final long serialVersionUID = 1353393306564207170L; + + /** + * 审批编号 + */ + @SerializedName("sp_no") + private String spNo; + + /** + * 审批申请类型名称(审批模板名称) + */ + @SerializedName("sp_name") + private String spName; + + /** + * 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付 + */ + @SerializedName("sp_status") + private WxCpSpStatus spStatus; + + /** + * 审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。 + */ + @SerializedName("template_id") + private String templateId; + + /** + * 审批申请提交时间,Unix时间戳 + */ + @SerializedName("apply_time") + private Long applyTime; + + /** + * 申请人信息 + */ + @SerializedName("applyer") + private WxCpApprovalApplier applier; + + /** + * 审批流程信息,可能有多个审批节点 + */ + @SerializedName("sp_record") + private WxCpApprovalRecord[] spRecords; + + /** + * 抄送信息,可能有多个抄送节点 + */ + @SerializedName("notifyer") + private WxCpOperator[] notifiers; + + /** + * 审批申请数据 + */ + @SerializedName("apply_data") + private WxCpApprovalApplyData applyData; + + /** + * 审批申请备注信息,可能有多个备注节点 + */ + @SerializedName("comments") + private List comments; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java new file mode 100644 index 0000000000..fdbc096a5c --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 审批申请详情响应结果 + * + * @author element + */ +@Data +public class WxCpApprovalDetailResult implements Serializable { + private static final long serialVersionUID = 3909779949756252918L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("info") + private WxCpApprovalDetail info; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java new file mode 100644 index 0000000000..4856af4194 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.cp.bean.oa; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + * @author element + */ +@Data +public class WxCpApprovalInfo implements Serializable { + private static final long serialVersionUID = 7387181805254287167L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("sp_no_list") + private List spNoList; + + @SerializedName("next_cursor") + private Integer nextCursor; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java new file mode 100644 index 0000000000..5271312081 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + *
    + *  批量获取审批单号的筛选条件,可对批量拉取的审批申请设置约束条件,支持设置多个条件
    + *  注意:
    + *  仅“部门”支持同时配置多个筛选条件。
    + *  不同类型的筛选条件之间为“与”的关系,同类型筛选条件之间为“或”的关系
    + * 
    + * + * @author element + */ +@Data +public class WxCpApprovalInfoQueryFilter implements Serializable { + + private static final long serialVersionUID = 3318064927980231802L; + + private WxCpApprovalInfoQueryFilter.KEY key; + + private Object value; + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + + public static enum KEY { + + /** + * template_id - 模板类型/模板id; + */ + @SerializedName("template_id") + TEMPLATE_ID("template_id"), + /** + * creator - 申请人; + */ + @SerializedName("creator") + CREATOR("creator"), + /** + * department - 审批单提单者所在部门; + */ + @SerializedName("department") + DEPARTMENT("department"), + /** + * sp_status - 审批状态。 + */ + @SerializedName("sp_status") + SP_STATUS("sp_status"); + + private String value; + + private KEY(String value) { + this.value = value; + } + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java new file mode 100644 index 0000000000..3325eaa4ac --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批流程信息 + * @author element + */ +@Data +public class WxCpApprovalRecord implements Serializable { + + private static final long serialVersionUID = -327230786004105887L; + + @SerializedName("sp_status") + private WxCpRecordSpStatus status; + + @SerializedName("approverattr") + private WxCpApproverAttr approverAttr; + + private List details; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java new file mode 100644 index 0000000000..4c966c9d6f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批节点详情 + * @author element + */ +@Data +public class WxCpApprovalRecordDetail implements Serializable { + + private static final long serialVersionUID = -9142079764088495301L; + + /** + * 分支审批人 + */ + @SerializedName("approver") + private WxCpOperator approver; + + /** + * 审批意见 + */ + @SerializedName("speech") + private String speech; + + /** + * 分支审批人审批状态 + */ + @SerializedName("sp_status") + private WxCpRecordSpStatus spStatus; + + /** + * 节点分支审批人审批操作时间戳,0表示未操作 + */ + @SerializedName("sptime") + private Long spTime; + + /** + * 节点分支审批人审批意见附件 + */ + @SerializedName("media_id") + private List mediaIds; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java new file mode 100644 index 0000000000..15e55f2f72 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; + +/** + * 审批方式 + * + * @author element + */ +public enum WxCpApproverAttr { + /** + * 或签 + */ + @SerializedName("1") + ONE_SIGN(1), + /** + * 会签 + */ + @SerializedName("2") + ALL_SIGN(2); + + private Integer attr; + + private WxCpApproverAttr(Integer attr) { + this.attr = attr; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java new file mode 100644 index 0000000000..4ab801de1a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java @@ -0,0 +1,99 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 企业微信打卡数据. + * + * @author Element + * @date 2019-04-06 11:01 + */ +@Data +public class WxCpCheckinData implements Serializable { + private static final long serialVersionUID = 1915820330847799605L; + + /** + * userid 用户id + */ + @SerializedName("userid") + private String userId; + + /** + * groupname 打卡规则名称 + */ + @SerializedName("groupname") + private String groupName; + + /** + * checkin_type 打卡类型。字符串,目前有:上班打卡,下班打卡,外出打卡 + */ + @SerializedName("checkin_type") + private String checkinType; + + /** + * exception_type 异常类型,字符串,包括:时间异常,地点异常,未打卡,wifi异常,非常用设备。如果有多个异常,以分号间隔 + */ + @SerializedName("exception_type") + private String exceptionType; + + /** + * checkin_time 打卡时间。Unix时间戳 + */ + @SerializedName("checkin_time") + private Long checkinTime; + + /** + * location_title 打卡地点title + */ + @SerializedName("location_title") + private String locationTitle; + + /** + * location_detail 打卡地点详情 + */ + @SerializedName("location_detail") + private String locationDetail; + + /** + * wifiname 打卡wifi名称 + */ + @SerializedName("wifiname") + private String wifiName; + + /** + * wifimac 打卡的MAC地址/bssid + */ + @SerializedName("wifimac") + private String wifiMac; + + /** + * notes 打卡备注 + */ + private String notes; + + /** + * mediaids 打卡的附件media_id,可使用media/get获取附件 + */ + @SerializedName("mediaids") + private List mediaIds; + + /** + * lat 位置打卡地点纬度,是实际纬度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准 + */ + private Integer lat; + + /** + * lng 位置打卡地点经度,是实际经度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准 + */ + private Integer lng; + + /** + * deviceid 打卡设备id + */ + @SerializedName("deviceid") + private String deviceId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinOption.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinOption.java new file mode 100644 index 0000000000..70cd4b202a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinOption.java @@ -0,0 +1,145 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 企业微信打卡规则. + * + * @author Element + * @date 2019-04-06 13:22 + */ +@Data +public class WxCpCheckinOption implements Serializable { + private static final long serialVersionUID = -1964233697990417482L; + + @SerializedName("userid") + private String userId; + + private Group group; + + @Data + public static class CheckinDate implements Serializable { + private static final long serialVersionUID = -5601722383347110974L; + + private List workdays; + + @SerializedName("checkintime") + private CheckinTime[] checkinTime; + + @SerializedName("flex_time") + private Long flexTime; + + @SerializedName("noneed_offwork") + private Boolean noNeedOffwork; + + @SerializedName("limit_aheadtime") + private Long limitAheadTime; + } + + @Data + public static class CheckinTime implements Serializable { + private static final long serialVersionUID = -8579954143265336276L; + + @SerializedName("work_sec") + private Long workSec; + + @SerializedName("off_work_sec") + private Long offWorkSec; + + @SerializedName("remind_work_sec") + private Long remindWorkSec; + + @SerializedName("remind_off_work_sec") + private Long remindOffWorkSec; + } + + @Data + public static class Group implements Serializable { + + private static final long serialVersionUID = -5888406969613403044L; + + @SerializedName("groupid") + private Long id; + + @SerializedName("groupname") + private String name; + + @SerializedName("grouptype") + private Integer type; + + @SerializedName("checkindate") + private List checkinDate; + + @SerializedName("spe_workdays") + private List speWorkdays; + + @SerializedName("spe_offdays") + private List speOffdays; + + @SerializedName("sync_holidays") + private Boolean syncHolidays; + + @SerializedName("need_photo") + private Boolean needPhoto; + + @SerializedName("note_can_use_local_pic") + private Boolean noteCanUseLocalPic; + + @SerializedName("allow_checkin_offworkday") + private Boolean allowCheckinOffWorkday; + + @SerializedName("allow_apply_offworkday") + private Boolean allowApplyOffWorkday; + + @SerializedName("wifimac_infos") + private List wifiMacInfos; + + @SerializedName("loc_infos") + private List locInfos; + + } + + @Data + public static class WifiMacInfo implements Serializable { + private static final long serialVersionUID = -4657809185716627368L; + + @SerializedName("wifiname") + private String name; + + @SerializedName("wifimac") + private String mac; + } + + @Data + public static class LocInfo implements Serializable { + private static final long serialVersionUID = -618965280668099608L; + + private Long lat; + private Long lng; + + @SerializedName("loc_title") + private String title; + + @SerializedName("loc_detail") + private String detail; + + private Long distance; + } + + @Data + public static class SpeDay implements Serializable { + private static final long serialVersionUID = -3538818921359212748L; + + private Long timestamp; + private String notes; + + @SerializedName("checkintime") + private List checkinTime; + + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpDialRecord.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpDialRecord.java new file mode 100644 index 0000000000..f3cf7d9881 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpDialRecord.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 公费电话拨打记录. + * + * @author Element + * @date 2019-04-06 15:38 + */ +@Data +public class WxCpDialRecord implements Serializable { + + private static final long serialVersionUID = 4178886812949929116L; + @SerializedName("call_time") + private Long callTime; + + /** + * 总通话时长,单位为分钟 + */ + @SerializedName("total_duration") + private Integer totalDuration; + + /** + * 通话类型,1-单人通话 2-多人通话 + */ + @SerializedName("call_type") + private Integer callType; + + private Caller caller; + + private List callee; + + /** + * 主叫信息 + */ + @Data + public static class Caller implements Serializable { + + private static final long serialVersionUID = 4792200404338145607L; + + @SerializedName("userid") + private String userId; + + private Integer duration; + } + + /** + * 被叫信息 + */ + @Data + public static class Callee implements Serializable { + + private static final long serialVersionUID = 2390963671336179550L; + + /** + * 被叫用户的userid,当被叫用户为企业内用户时返回 + */ + @SerializedName("userid") + private String userId; + + /** + * 被叫用户的号码,当被叫用户为外部用户时返回 + */ + private String phone; + + private Integer duration; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequest.java new file mode 100644 index 0000000000..81dd6b45b0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequest.java @@ -0,0 +1,105 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.bean.oa.applydata.ApplyDataContent; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 提交审批申请 请求对象类. + * + * @author Binary Wang + * @date 2020-07-18 + */ +@Data +@Accessors(chain = true) +public class WxCpOaApplyEventRequest implements Serializable { + private static final long serialVersionUID = 3362660678938569341L; + + /** + * 申请人userid,此审批申请将以此员工身份提交,申请人需在应用可见范围内 + */ + @SerializedName("creator_userid") + private String creatorUserId; + + /** + * 模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。暂不支持通过接口提交[打卡补卡][调班]模板审批单。 + */ + @SerializedName("template_id") + private String templateId; + + /** + * 审批人模式:0-通过接口指定审批人、抄送人(此时approver、notifyer等参数可用); 1-使用此模板在管理后台设置的审批流程,支持条件审批。默认为0 + */ + @SerializedName("use_template_approver") + private Integer useTemplateApprover; + + /** + * 审批流程信息,用于指定审批申请的审批流程,支持单人审批、多人会签、多人或签,可能有多个审批节点,仅use_template_approver为0时生效。 + */ + @SerializedName("approver") + private List approvers; + + /** + * 抄送人节点userid列表,仅use_template_approver为0时生效。 + */ + @SerializedName("notifyer") + private String[] notifiers; + + /** + * 抄送方式:1-提单时抄送(默认值); 2-单据通过后抄送;3-提单和单据通过后抄送。仅use_template_approver为0时生效。 + */ + @SerializedName("notify_type") + private Integer notifyType; + + /** + * 审批申请数据,可定义审批申请中各个控件的值,其中必填项必须有值,选填项可为空,数据结构同“获取审批申请详情”接口返回值中同名参数“apply_data” + */ + @SerializedName("apply_data") + private ApplyData applyData; + + /** + * 摘要信息,用于显示在审批通知卡片、审批列表的摘要信息,最多3行 + */ + @SerializedName("summary_list") + private List summaryList; + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + @Data + @Accessors(chain = true) + public static class Approver implements Serializable { + private static final long serialVersionUID = 7625206971546930988L; + + /** + * 节点审批方式:1-或签;2-会签,仅在节点为多人审批时有效 + */ + private Integer attr; + + /** + * 审批节点审批人userid列表,若为多人会签、多人或签,需填写每个人的userid + */ + @SerializedName("userid") + private String[] userIds; + } + + @Data + @Accessors(chain = true) + public static class ApplyData implements Serializable { + private static final long serialVersionUID = -2462732405265306981L; + + /** + * 审批申请数据,可定义审批申请中各个控件的值,其中必填项必须有值,选填项可为空, + * 数据结构同“获取审批申请详情”接口返回值中同名参数“apply_data” + */ + @SerializedName("contents") + private List contents; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOperator.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOperator.java new file mode 100644 index 0000000000..063234dac2 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOperator.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + + +/** + * 企业微信操作人 + * + * @author element + */ +@Data +public class WxCpOperator implements Serializable { + + private static final long serialVersionUID = 5797144853574346736L; + + /** + * 企业微信userid + */ + @SerializedName("userid") + private String userId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java new file mode 100644 index 0000000000..206a0aa04e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; + +/** + * 审批记录(节点)分支审批状态 + * + * 1-审批中;2-已同意;3-已驳回;4-已转审 + * + * @author element + */ +public enum WxCpRecordSpStatus { + + /** + * 审批中 + */ + @SerializedName("1") + AUDITING(1), + /** + * 已同意 + */ + @SerializedName("2") + PASSED(2), + /** + * 已驳回 + */ + @SerializedName("3") + REJECTED(3), + /** + * 已转审 + */ + @SerializedName("4") + TURNED(4); + + private Integer status; + + private WxCpRecordSpStatus(Integer status) { + this.status = status; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java new file mode 100644 index 0000000000..c3d8005f50 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; + +/** + * 审批单状态 + * (1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付) + * + * @author element + */ +public enum WxCpSpStatus { + + /** + * 审批中 + */ + @SerializedName("1") + AUDITING(1), + /** + * 已通过 + */ + @SerializedName("2") + PASSED(2), + /** + * 已驳回 + */ + @SerializedName("3") + REJECTED(3), + /** + * 已撤销 + */ + @SerializedName("4") + UNDONE(4), + /** + * 通过后撤销 + */ + @SerializedName("6") + PASS_UNDONE(6), + /** + * 已删除 + */ + @SerializedName("7") + DELETED(7), + /** + * 已支付 + */ + @SerializedName("10") + ALREADY_PAY(10); + + private Integer status; + + private WxCpSpStatus(Integer status) { + this.status = status; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpTemplateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpTemplateResult.java new file mode 100644 index 0000000000..b8dd2dbe91 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpTemplateResult.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.bean.oa.templatedata.TemplateContent; +import me.chanjar.weixin.cp.bean.oa.templatedata.TemplateTitle; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批模板详情 + * + * @author gyv12345@163.com + */ +@Data +public class WxCpTemplateResult implements Serializable { + private static final long serialVersionUID = 6690547131189343887L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("template_names") + private List templateNames; + + @SerializedName("template_content") + private TemplateContent templateContent; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ApplyDataContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ApplyDataContent.java new file mode 100644 index 0000000000..f86ab966e3 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ApplyDataContent.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * @author element + */ +@Data +@Accessors(chain = true) +public class ApplyDataContent implements Serializable { + private static final long serialVersionUID = 8456821731930526935L; + /** + * 控件类型:Text-文本;Textarea-多行文本;Number-数字;Money-金额;Date-日期/日期+时间; + * Selector-单选/多选;;Contact-成员/部门;Tips-说明文字;File-附件;Table-明细; + */ + private String control; + + /** + * 控件id:控件的唯一id,可通过“获取审批模板详情”接口获取 + */ + private String id; + + @SerializedName("title") + private List titles; + + /** + * 控件值 ,需在此为申请人在各个控件中填写内容不同控件有不同的赋值参数,具体说明详见附录。模板配置的控件属性为必填时,对应value值需要有值。 + */ + private ContentValue value; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentTitle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentTitle.java new file mode 100644 index 0000000000..24d5fafc2d --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentTitle.java @@ -0,0 +1,18 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author element + */ +@Data +public class ContentTitle implements Serializable { + + private static final long serialVersionUID = -4501999157383517007L; + + private String text; + private String lang; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java new file mode 100644 index 0000000000..54e19a4bf9 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java @@ -0,0 +1,120 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * @author element + */ +@Data +@Accessors(chain = true) +public class ContentValue implements Serializable { + private static final long serialVersionUID = -5607678965965065261L; + + private String text; + + @SerializedName("new_number") + private Double newNumber; + + @SerializedName("new_money") + private Double newMoney; + + private ContentValue.Date date; + + private ContentValue.Selector selector; + + private List members; + + private List departments; + + private List files; + + private List children; + + private Attendance attendance; + + @Data + public static class Date implements Serializable { + private static final long serialVersionUID = -6181554080062231138L; + private String type; + + @SerializedName("s_timestamp") + private Double timestamp; + } + + @Data + public static class Selector implements Serializable { + private static final long serialVersionUID = 7305458759126951773L; + private String type; + private List
    - * - * @param messageDuplicateChecker */ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { this.messageDuplicateChecker = messageDuplicateChecker; @@ -101,8 +100,6 @@ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicat * 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager} * - * - * @param sessionManager */ public void setSessionManager(WxSessionManager sessionManager) { this.sessionManager = sessionManager; @@ -113,8 +110,6 @@ public void setSessionManager(WxSessionManager sessionManager) { * 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} * - * - * @param exceptionHandler */ public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; @@ -125,19 +120,17 @@ List getRules() { } /** - * 开始一个新的Route规则 + * 开始一个新的Route规则. */ public WxCpMessageRouterRule rule() { return new WxCpMessageRouterRule(this); } /** - * 处理微信消息 - * - * @param wxMessage + * 处理微信消息. */ - public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { - if (isDuplicateMessage(wxMessage)) { + public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage, final Map context) { + if (isMsgDuplicated(wxMessage)) { // 如果是重复消息,那么就不做处理 return null; } @@ -166,12 +159,12 @@ public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { this.executorService.submit(new Runnable() { @Override public void run() { - rule.service(wxMessage, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler); + rule.service(wxMessage, context, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler); } }) ); } else { - res = rule.service(wxMessage, this.wxCpService, this.sessionManager, this.exceptionHandler); + res = rule.service(wxMessage, context, this.wxCpService, this.sessionManager, this.exceptionHandler); // 在同步操作结束,session访问结束 this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUserName()); sessionEndAccess(wxMessage); @@ -190,6 +183,7 @@ public void run() { sessionEndAccess(wxMessage); } catch (InterruptedException e) { WxCpMessageRouter.this.log.error("Error happened when wait task finish", e); + Thread.currentThread().interrupt(); } catch (ExecutionException e) { WxCpMessageRouter.this.log.error("Error happened when wait task finish", e); } @@ -200,37 +194,46 @@ public void run() { return res; } - protected boolean isDuplicateMessage(WxCpXmlMessage wxMessage) { + /** + * 处理微信消息. + */ + public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { + return this.route(wxMessage, new HashMap(2)); + } - String messageId = ""; + private boolean isMsgDuplicated(WxCpXmlMessage wxMessage) { + StringBuilder messageId = new StringBuilder(); if (wxMessage.getMsgId() == null) { - messageId = String.valueOf(wxMessage.getCreateTime()) - + "-" + String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId()) - + "-" + wxMessage.getFromUserName() - + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) - + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) - ; + messageId.append(wxMessage.getCreateTime()) + .append("-").append(StringUtils.trimToEmpty(String.valueOf(wxMessage.getAgentId()))) + .append("-").append(wxMessage.getFromUserName()) + .append("-").append(StringUtils.trimToEmpty(wxMessage.getEventKey())) + .append("-").append(StringUtils.trimToEmpty(wxMessage.getEvent())); } else { - messageId = String.valueOf(wxMessage.getMsgId()); + messageId.append(wxMessage.getMsgId()) + .append("-").append(wxMessage.getCreateTime()) + .append("-").append(wxMessage.getFromUserName()); + } + + if (StringUtils.isNotEmpty(wxMessage.getUserId())) { + messageId.append("-").append(wxMessage.getUserId()); } - return this.messageDuplicateChecker.isDuplicate(messageId); + if (StringUtils.isNotEmpty(wxMessage.getChangeType())) { + messageId.append("-").append(wxMessage.getChangeType()); + } + return this.messageDuplicateChecker.isDuplicate(messageId.toString()); } /** - * 对session的访问结束 - * - * @param wxMessage + * 对session的访问结束. */ - protected void sessionEndAccess(WxCpXmlMessage wxMessage) { - + private void sessionEndAccess(WxCpXmlMessage wxMessage) { InternalSession session = ((InternalSessionManager) this.sessionManager).findSession(wxMessage.getFromUserName()); if (session != null) { session.endAccess(); } } - - } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java similarity index 85% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java index 7bd332ad9b..8f3766a160 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java @@ -1,10 +1,12 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.message; import me.chanjar.weixin.common.api.WxErrorExceptionHandler; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.HashMap; @@ -26,6 +28,8 @@ public class WxCpMessageRouterRule { private String eventKey; + private String eventKeyRegex; + private String content; private String rContent; @@ -94,6 +98,14 @@ public WxCpMessageRouterRule eventKey(String eventKey) { return this; } + /** + * 如果eventKey匹配该正则表达式 + */ + public WxCpMessageRouterRule eventKeyRegex(String regex) { + this.eventKeyRegex = regex; + return this; + } + /** * 如果content等于某值 * @@ -206,17 +218,17 @@ protected boolean test(WxCpXmlMessage wxMessage) { && (this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) && - (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) + (this.msgType == null || this.msgType.equalsIgnoreCase(wxMessage.getMsgType())) + && + (this.event == null || this.event.equalsIgnoreCase(wxMessage.getEvent())) && - (this.event == null || this.event.equals(wxMessage.getEvent())) + (this.eventKey == null || this.eventKey.equalsIgnoreCase(wxMessage.getEventKey())) && - (this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey())) + (this.eventKeyRegex == null || Pattern.matches(this.eventKeyRegex, StringUtils.trimToEmpty(wxMessage.getEventKey()))) && - (this.content == null || this.content - .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) + (this.content == null || this.content.equals(StringUtils.trimToNull(wxMessage.getContent()))) && - (this.rContent == null || Pattern - .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) + (this.rContent == null || Pattern.matches(this.rContent, StringUtils.trimToEmpty(wxMessage.getContent()))) && (this.matcher == null || this.matcher.match(wxMessage)) ; @@ -229,13 +241,16 @@ protected boolean test(WxCpXmlMessage wxMessage) { * @return true 代表继续执行别的router,false 代表停止执行别的router */ protected WxCpXmlOutMessage service(WxCpXmlMessage wxMessage, + Map context, WxCpService wxCpService, WxSessionManager sessionManager, WxErrorExceptionHandler exceptionHandler) { - try { + if (context == null) { + context = new HashMap<>(); + } - Map context = new HashMap<>(); + try { // 如果拦截器不通过 for (WxCpMessageInterceptor interceptor : this.interceptors) { if (!interceptor.intercept(wxMessage, context, wxCpService, sessionManager)) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java index 104b124377..2914945b89 100755 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java @@ -1,47 +1,24 @@ -/** - * 对公众平台发送给公众账号的消息加解密示例代码. - * - * @copyright Copyright (c) 1998-2014 Tencent Inc. - *

    - * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - */ - -// ------------------------------------------------------------------------ - -/** - * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - */ -package me.chanjar.weixin.cp.util.crypto; - -import me.chanjar.weixin.common.util.crypto.WxCryptUtil; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import org.apache.commons.codec.binary.Base64; - -public class WxCpCryptUtil extends WxCryptUtil { - - /** - * 构造函数 - * - * @param wxCpConfigStorage - */ - public WxCpCryptUtil(WxCpConfigStorage wxCpConfigStorage) { - /* - * @param token 公众平台上,开发者设置的token - * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey - * @param appidOrCorpid 公众平台appid - */ - String encodingAesKey = wxCpConfigStorage.getAesKey(); - String token = wxCpConfigStorage.getToken(); - String corpId = wxCpConfigStorage.getCorpId(); - - this.token = token; - this.appidOrCorpid = corpId; - this.aesKey = Base64.decodeBase64(encodingAesKey + "="); - } - - -} +package me.chanjar.weixin.cp.util.crypto; + +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; +import me.chanjar.weixin.common.util.crypto.WxCryptUtil; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; + +public class WxCpCryptUtil extends WxCryptUtil { + public WxCpCryptUtil(WxCpConfigStorage wxCpConfigStorage) { + /* + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appidOrCorpid 公众平台appid + */ + String encodingAesKey = wxCpConfigStorage.getAesKey(); + String token = wxCpConfigStorage.getToken(); + String corpId = wxCpConfigStorage.getCorpId(); + + this.token = token; + this.appidOrCorpid = corpId; + this.aesKey = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey)); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpTpCryptUtil.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpTpCryptUtil.java new file mode 100644 index 0000000000..900f7ea8aa --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpTpCryptUtil.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.cp.util.crypto; + +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; +import me.chanjar.weixin.common.util.crypto.WxCryptUtil; +import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; + +/** + * @author someone + */ +public class WxCpTpCryptUtil extends WxCryptUtil { + /** + * 构造函数. + */ + public WxCpTpCryptUtil(WxCpTpConfigStorage wxCpTpConfigStorage) { + /* + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appidOrCorpid 公众平台corpId + */ + String encodingAesKey = wxCpTpConfigStorage.getAesKey(); + String token = wxCpTpConfigStorage.getToken(); + String corpId = wxCpTpConfigStorage.getCorpId(); + + this.token = token; + this.appidOrCorpid = corpId; + this.aesKey = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey)); + } + + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpChatGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpChatGsonAdapter.java new file mode 100644 index 0000000000..0e97181a3a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpChatGsonAdapter.java @@ -0,0 +1,77 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.cp.util.json; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.cp.bean.WxCpChat; + +/** + * 群聊适配器 + * + * @author gaigeshen + */ +public class WxCpChatGsonAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public JsonElement serialize(WxCpChat chat, Type typeOfSrc, JsonSerializationContext context) { + JsonObject json = new JsonObject(); + if (chat.getId() != null) { + json.addProperty("chatid", chat.getId()); + } + if (chat.getName() != null) { + json.addProperty("name", chat.getName()); + } + if (chat.getOwner() != null) { + json.addProperty("owner", chat.getOwner()); + } + if (chat.getUsers() != null) { + JsonArray users = new JsonArray(); + for (String user : chat.getUsers()) { + users.add(user); + } + json.add("userlist", users); + } + return json; + } + + @Override + public WxCpChat deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject chatJson = json.getAsJsonObject(); + + WxCpChat chat = new WxCpChat(); + chat.setId(GsonHelper.getAsString(chatJson.get("chatid"))); + chat.setName(GsonHelper.getAsString(chatJson.get("name"))); + chat.setOwner(GsonHelper.getAsString(chatJson.get("owner"))); + + JsonArray usersJson = chatJson.getAsJsonArray("userlist"); + if (usersJson != null) { + List users = new ArrayList<>(usersJson.size()); + chat.setUsers(users); + for (JsonElement userJson : usersJson) { + users.add(userJson.getAsString()); + } + } + + return chat; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpConclusionAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpConclusionAdapter.java new file mode 100644 index 0000000000..fd159d1755 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpConclusionAdapter.java @@ -0,0 +1,123 @@ +package me.chanjar.weixin.cp.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.cp.bean.external.WxCpContactWayInfo; +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Type; + +/** + * 结束语序列化转换器 + * + * @author element + */ +public class WxCpConclusionAdapter implements JsonSerializer, JsonDeserializer { + @Override + public WxCpContactWayInfo.ContactWay.Conclusion deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + + WxCpContactWayInfo.ContactWay.Conclusion conclusion = new WxCpContactWayInfo.ContactWay.Conclusion(); + + if (jsonObject.get("text") != null) { + JsonObject jsonText = jsonObject.get("text").getAsJsonObject(); + + if (jsonText.get("content") != null) { + conclusion.setTextContent(jsonText.get("content").getAsString()); + } + } + + if (jsonObject.get("image") != null) { + JsonObject jsonImage = jsonObject.get("image").getAsJsonObject(); + + if (jsonImage.get("media_id") != null) { + conclusion.setImgMediaId(jsonImage.get("media_id").getAsString()); + } + + if (jsonImage.get("pic_url") != null) { + conclusion.setImgPicUrl(jsonImage.get("pic_url").getAsString()); + } + } + + if (jsonObject.get("link") != null) { + JsonObject jsonLink = jsonObject.get("link").getAsJsonObject(); + + if (jsonLink.get("title") != null) { + conclusion.setLinkTitle(jsonLink.get("title").getAsString()); + } + if (jsonLink.get("picurl") != null) { + conclusion.setLinkPicUrl(jsonLink.get("picurl").getAsString()); + } + if (jsonLink.get("desc") != null) { + conclusion.setLinkDesc(jsonLink.get("desc").getAsString()); + } + if (jsonLink.get("url") != null) { + conclusion.setLinkUrl(jsonLink.get("url").getAsString()); + } + } + + if (jsonObject.get("miniprogram") != null) { + + JsonObject jsonMiniProgram = jsonObject.get("miniprogram").getAsJsonObject(); + if (jsonMiniProgram.get("title") != null) { + conclusion.setMiniProgramTitle(jsonMiniProgram.get("title").getAsString()); + } + if (jsonMiniProgram.get("pic_media_id") != null) { + conclusion.setMiniProgramPicMediaId(jsonMiniProgram.get("pic_media_id").getAsString()); + } + if (jsonMiniProgram.get("appid") != null) { + conclusion.setMiniProgramAppId(jsonMiniProgram.get("appid").getAsString()); + } + if (jsonMiniProgram.get("page") != null) { + conclusion.setMiniProgramPage(jsonMiniProgram.get("page").getAsString()); + } + + } + + return conclusion; + } + + @Override + public JsonElement serialize(WxCpContactWayInfo.ContactWay.Conclusion src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject json = new JsonObject(); + if (StringUtils.isNotBlank(src.getTextContent())) { + JsonObject jsonText = new JsonObject(); + jsonText.addProperty("content", src.getTextContent()); + json.add("text", jsonText); + } + + if (StringUtils.isNotBlank(src.getImgMediaId()) || StringUtils.isNotBlank(src.getImgPicUrl())) { + JsonObject jsonImg = new JsonObject(); + jsonImg.addProperty("media_id", src.getImgMediaId()); + jsonImg.addProperty("pic_url", src.getImgPicUrl()); + json.add("image", jsonImg); + } + + if (StringUtils.isNotBlank(src.getLinkTitle()) + || StringUtils.isNotBlank(src.getLinkPicUrl()) + || StringUtils.isNotBlank(src.getLinkDesc()) + || StringUtils.isNotBlank(src.getLinkUrl()) + ) { + JsonObject jsonLink = new JsonObject(); + jsonLink.addProperty("title", src.getLinkTitle()); + jsonLink.addProperty("picurl", src.getLinkPicUrl()); + jsonLink.addProperty("desc", src.getLinkDesc()); + jsonLink.addProperty("url", src.getLinkUrl()); + json.add("link", jsonLink); + } + + if (StringUtils.isNotBlank(src.getMiniProgramTitle()) + || StringUtils.isNotBlank(src.getMiniProgramPicMediaId()) + || StringUtils.isNotBlank(src.getMiniProgramAppId()) + || StringUtils.isNotBlank(src.getMiniProgramPage()) + ) { + JsonObject jsonMiniProgram = new JsonObject(); + jsonMiniProgram.addProperty("title", src.getMiniProgramTitle()); + jsonMiniProgram.addProperty("pic_media_id", src.getMiniProgramPicMediaId()); + jsonMiniProgram.addProperty("appid", src.getMiniProgramAppId()); + jsonMiniProgram.addProperty("page", src.getMiniProgramPage()); + json.add("miniprogram", jsonMiniProgram); + } + + return json; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java index a547ce020d..4340855fda 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java @@ -8,51 +8,70 @@ */ package me.chanjar.weixin.cp.util.json; -import com.google.gson.*; +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.cp.bean.WxCpDepart; -import java.lang.reflect.Type; - /** + * WxCpDepart的gson适配器. + * * @author Daniel Qian */ public class WxCpDepartGsonAdapter implements JsonSerializer, JsonDeserializer { + private static final String ID = "id"; + private static final String NAME = "name"; + private static final String EN_NAME = "name_en"; + private static final String PARENT_ID = "parentid"; + private static final String ORDER = "order"; @Override public JsonElement serialize(WxCpDepart group, Type typeOfSrc, JsonSerializationContext context) { JsonObject json = new JsonObject(); if (group.getId() != null) { - json.addProperty("id", group.getId()); + json.addProperty(ID, group.getId()); } if (group.getName() != null) { - json.addProperty("name", group.getName()); + json.addProperty(NAME, group.getName()); + } + if (group.getEnName() != null) { + json.addProperty(EN_NAME, group.getEnName()); } if (group.getParentId() != null) { - json.addProperty("parentid", group.getParentId()); + json.addProperty(PARENT_ID, group.getParentId()); } if (group.getOrder() != null) { - json.addProperty("order", String.valueOf(group.getOrder())); + json.addProperty(ORDER, String.valueOf(group.getOrder())); } return json; } @Override public WxCpDepart deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { WxCpDepart depart = new WxCpDepart(); JsonObject departJson = json.getAsJsonObject(); - if (departJson.get("id") != null && !departJson.get("id").isJsonNull()) { - depart.setId(GsonHelper.getAsInteger(departJson.get("id"))); + if (departJson.get(ID) != null && !departJson.get(ID).isJsonNull()) { + depart.setId(GsonHelper.getAsLong(departJson.get(ID))); + } + if (departJson.get(NAME) != null && !departJson.get(NAME).isJsonNull()) { + depart.setName(GsonHelper.getAsString(departJson.get(NAME))); } - if (departJson.get("name") != null && !departJson.get("name").isJsonNull()) { - depart.setName(GsonHelper.getAsString(departJson.get("name"))); + if (departJson.get(EN_NAME) != null && !departJson.get(EN_NAME).isJsonNull()) { + depart.setEnName(GsonHelper.getAsString(departJson.get(EN_NAME))); } - if (departJson.get("order") != null && !departJson.get("order").isJsonNull()) { - depart.setOrder(GsonHelper.getAsInteger(departJson.get("order"))); + if (departJson.get(ORDER) != null && !departJson.get(ORDER).isJsonNull()) { + depart.setOrder(GsonHelper.getAsLong(departJson.get(ORDER))); } - if (departJson.get("parentid") != null && !departJson.get("parentid").isJsonNull()) { - depart.setParentId(GsonHelper.getAsInteger(departJson.get("parentid"))); + if (departJson.get(PARENT_ID) != null && !departJson.get(PARENT_ID).isJsonNull()) { + depart.setParentId(GsonHelper.getAsLong(departJson.get(PARENT_ID))); } return depart; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java index 3235864811..16f0108e09 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java @@ -2,23 +2,28 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.util.json.WxErrorAdapter; +import me.chanjar.weixin.cp.bean.WxCpChat; import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpMessage; import me.chanjar.weixin.cp.bean.WxCpTag; import me.chanjar.weixin.cp.bean.WxCpUser; +/** + * @author Daniel Qian + */ public class WxCpGsonBuilder { - public static final GsonBuilder INSTANCE = new GsonBuilder(); + private static final GsonBuilder INSTANCE = new GsonBuilder(); static { INSTANCE.disableHtmlEscaping(); - INSTANCE.registerTypeAdapter(WxCpMessage.class, new WxCpMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxCpChat.class, new WxCpChatGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter()); INSTANCE.registerTypeAdapter(WxError.class, new WxErrorAdapter()); + INSTANCE.registerTypeAdapter(WxMenu.class, new WxCpMenuGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpTag.class, new WxCpTagGsonAdapter()); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMenuGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMenuGsonAdapter.java new file mode 100644 index 0000000000..c7cb05cc62 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMenuGsonAdapter.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.cp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.util.json.WxMenuGsonAdapter; + +import java.lang.reflect.Type; + +/** + *

    + * 企业号菜单json转换适配器
    + * Created by Binary Wang on 2017-6-25.
    + * @author Binary Wang
    + * 
    + */ +public class WxCpMenuGsonAdapter extends WxMenuGsonAdapter { + + @Override + public WxMenu deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return this.buildMenuFromJson(json.getAsJsonObject().get("button").getAsJsonArray()); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java deleted file mode 100644 index 949709abec..0000000000 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ -package me.chanjar.weixin.cp.util.json; - -import com.google.gson.*; -import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.cp.bean.WxCpMessage; -import me.chanjar.weixin.cp.bean.article.MpnewsArticle; -import me.chanjar.weixin.cp.bean.article.NewArticle; -import org.apache.commons.lang3.StringUtils; - -import java.lang.reflect.Type; - -/** - * @author Daniel Qian - */ -public class WxCpMessageGsonAdapter implements JsonSerializer { - - @Override - public JsonElement serialize(WxCpMessage message, Type typeOfSrc, JsonSerializationContext context) { - JsonObject messageJson = new JsonObject(); - messageJson.addProperty("agentid", message.getAgentId()); - if (StringUtils.isNotBlank(message.getToUser())) { - messageJson.addProperty("touser", message.getToUser()); - } - messageJson.addProperty("msgtype", message.getMsgType()); - - if (StringUtils.isNotBlank(message.getToParty())) { - messageJson.addProperty("toparty", message.getToParty()); - } - if (StringUtils.isNotBlank(message.getToTag())) { - messageJson.addProperty("totag", message.getToTag()); - } - if (WxConsts.CUSTOM_MSG_TEXT.equals(message.getMsgType())) { - JsonObject text = new JsonObject(); - text.addProperty("content", message.getContent()); - messageJson.add("text", text); - } - - if (WxConsts.CUSTOM_MSG_IMAGE.equals(message.getMsgType())) { - JsonObject image = new JsonObject(); - image.addProperty("media_id", message.getMediaId()); - messageJson.add("image", image); - } - - if (WxConsts.CUSTOM_MSG_FILE.equals(message.getMsgType())) { - JsonObject image = new JsonObject(); - image.addProperty("media_id", message.getMediaId()); - messageJson.add("file", image); - } - - if (WxConsts.CUSTOM_MSG_VOICE.equals(message.getMsgType())) { - JsonObject voice = new JsonObject(); - voice.addProperty("media_id", message.getMediaId()); - messageJson.add("voice", voice); - } - - if (WxConsts.CUSTOM_MSG_VIDEO.equals(message.getMsgType())) { - JsonObject video = new JsonObject(); - video.addProperty("media_id", message.getMediaId()); - video.addProperty("thumb_media_id", message.getThumbMediaId()); - video.addProperty("title", message.getTitle()); - video.addProperty("description", message.getDescription()); - messageJson.add("video", video); - } - - if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { - JsonObject newsJsonObject = new JsonObject(); - JsonArray articleJsonArray = new JsonArray(); - for (NewArticle article : message.getArticles()) { - JsonObject articleJson = new JsonObject(); - articleJson.addProperty("title", article.getTitle()); - articleJson.addProperty("description", article.getDescription()); - articleJson.addProperty("url", article.getUrl()); - articleJson.addProperty("picurl", article.getPicUrl()); - articleJsonArray.add(articleJson); - } - newsJsonObject.add("articles", articleJsonArray); - messageJson.add("news", newsJsonObject); - } - - if (WxConsts.CUSTOM_MSG_MPNEWS.equals(message.getMsgType())) { - JsonObject newsJsonObject = new JsonObject(); - if (message.getMediaId() != null) { - newsJsonObject.addProperty("media_id", message.getMediaId()); - }else { - JsonArray articleJsonArray = new JsonArray(); - for (MpnewsArticle article : message.getMpnewsArticles()) { - JsonObject articleJson = new JsonObject(); - articleJson.addProperty("title", article.getTitle()); - articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); - articleJson.addProperty("author", article.getAuthor()); - articleJson.addProperty("content_source_url", article.getContentSourceUrl()); - articleJson.addProperty("content", article.getContent()); - articleJson.addProperty("digest", article.getDigest()); - articleJson.addProperty("show_cover_pic", article.getShowCoverPic()); - articleJsonArray.add(articleJson); - } - - newsJsonObject.add("articles", articleJsonArray); - } - messageJson.add("mpnews", newsJsonObject); - } - - return messageJson; - } - -} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpTagGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpTagGsonAdapter.java index 43e84a00fb..872190688c 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpTagGsonAdapter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpTagGsonAdapter.java @@ -29,7 +29,7 @@ public JsonElement serialize(WxCpTag tag, Type typeOfSrc, JsonSerializationConte @Override public WxCpTag deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); return new WxCpTag(GsonHelper.getString(jsonObject, "tagid"), GsonHelper.getString(jsonObject, "tagname")); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java index bbabf054e1..e721b33acf 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java @@ -6,59 +6,180 @@ * arose from modification of the original source, or other redistribution of this source * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. */ + package me.chanjar.weixin.cp.util.json; import com.google.gson.*; import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.cp.bean.Gender; import me.chanjar.weixin.cp.bean.WxCpUser; import java.lang.reflect.Type; /** + * cp user gson adapter. + * * @author Daniel Qian */ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSerializer { + private static final String EXTERNAL_PROFILE = "external_profile"; + private static final String EXTERNAL_ATTR = "external_attr"; + private static final String EXTRA_ATTR = "extattr"; + private static final String EXTERNAL_POSITION = "external_position"; + private static final String DEPARTMENT = "department"; + private static final String EXTERNAL_CORP_NAME = "external_corp_name"; @Override - public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject o = json.getAsJsonObject(); WxCpUser user = new WxCpUser(); - user.setUserId(GsonHelper.getString(o, "userid")); - user.setName(GsonHelper.getString(o, "name")); - if (o.get("department") != null) { - JsonArray departJsonArray = o.get("department").getAsJsonArray(); - Integer[] departIds = new Integer[departJsonArray.size()]; + if (o.get(DEPARTMENT) != null) { + JsonArray departJsonArray = o.get(DEPARTMENT).getAsJsonArray(); + Long[] departIds = new Long[departJsonArray.size()]; int i = 0; for (JsonElement jsonElement : departJsonArray) { - departIds[i++] = jsonElement.getAsInt(); + departIds[i++] = jsonElement.getAsLong(); } user.setDepartIds(departIds); } + if (o.get("order") != null) { + JsonArray departJsonArray = o.get("order").getAsJsonArray(); + Integer[] orders = new Integer[departJsonArray.size()]; + int i = 0; + for (JsonElement jsonElement : departJsonArray) { + orders[i++] = jsonElement.getAsInt(); + } + user.setOrders(orders); + } + + if (o.get("positions") != null) { + JsonArray positionJsonArray = o.get("positions").getAsJsonArray(); + String[] positions = new String[positionJsonArray.size()]; + int i = 0; + for (JsonElement jsonElement : positionJsonArray) { + positions[i++] = jsonElement.getAsString(); + } + user.setPositions(positions); + } + + user.setUserId(GsonHelper.getString(o, "userid")); + user.setName(GsonHelper.getString(o, "name")); user.setPosition(GsonHelper.getString(o, "position")); user.setMobile(GsonHelper.getString(o, "mobile")); - user.setGender(GsonHelper.getString(o, "gender")); - user.setTel(GsonHelper.getString(o, "tel")); + user.setGender(Gender.fromCode(GsonHelper.getString(o, "gender"))); user.setEmail(GsonHelper.getString(o, "email")); - user.setWeiXinId(GsonHelper.getString(o, "weixinid")); user.setAvatar(GsonHelper.getString(o, "avatar")); + user.setThumbAvatar(GsonHelper.getString(o, "thumb_avatar")); + user.setAddress(GsonHelper.getString(o, "address")); + user.setAvatarMediaId(GsonHelper.getString(o, "avatar_mediaid")); user.setStatus(GsonHelper.getInteger(o, "status")); + user.setEnable(GsonHelper.getInteger(o, "enable")); + user.setAlias(GsonHelper.getString(o, "alias")); + user.setIsLeader(GsonHelper.getInteger(o, "isleader")); + user.setIsLeaderInDept(GsonHelper.getIntArray(o, "is_leader_in_dept")); + user.setHideMobile(GsonHelper.getInteger(o, "hide_mobile")); + user.setEnglishName(GsonHelper.getString(o, "english_name")); + user.setTelephone(GsonHelper.getString(o, "telephone")); + user.setQrCode(GsonHelper.getString(o, "qr_code")); + user.setToInvite(GsonHelper.getBoolean(o, "to_invite")); + user.setMainDepartment(GsonHelper.getString(o, "main_department")); - if (GsonHelper.isNotNull(o.get("extattr"))) { - JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray(); - for (JsonElement attrJsonElement : attrJsonElements) { - WxCpUser.Attr attr = new WxCpUser.Attr( - GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"), - GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value") - ); - user.getExtAttrs().add(attr); - } + if (GsonHelper.isNotNull(o.get(EXTRA_ATTR))) { + this.buildExtraAttrs(o, user); } + + if (GsonHelper.isNotNull(o.get(EXTERNAL_PROFILE))) { + user.setExternalCorpName(GsonHelper.getString(o.getAsJsonObject().get(EXTERNAL_PROFILE).getAsJsonObject(), EXTERNAL_CORP_NAME)); + this.buildExternalAttrs(o, user); + } + + user.setExternalPosition(GsonHelper.getString(o, EXTERNAL_POSITION)); + return user; } + private void buildExtraAttrs(JsonObject o, WxCpUser user) { + JsonArray attrJsonElements = o.get(EXTRA_ATTR).getAsJsonObject().get("attrs").getAsJsonArray(); + for (JsonElement attrJsonElement : attrJsonElements) { + final Integer type = GsonHelper.getInteger(attrJsonElement.getAsJsonObject(), "type"); + final WxCpUser.Attr attr = new WxCpUser.Attr().setType(type) + .setName(GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name")); + user.getExtAttrs().add(attr); + + if (type == null) { + attr.setTextValue(GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value")); + continue; + } + + switch (type) { + case 0: { + attr.setTextValue(GsonHelper.getString(attrJsonElement.getAsJsonObject().get("text").getAsJsonObject(), "value")); + break; + } + case 1: { + final JsonObject web = attrJsonElement.getAsJsonObject().get("web").getAsJsonObject(); + attr.setWebTitle(GsonHelper.getString(web, "title")) + .setWebUrl(GsonHelper.getString(web, "url")); + break; + } + default://ignored + } + } + } + + private void buildExternalAttrs(JsonObject o, WxCpUser user) { + JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray(); + for (JsonElement element : attrJsonElements) { + final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type"); + final String name = GsonHelper.getString(element.getAsJsonObject(), "name"); + + if (type == null) { + continue; + } + + switch (type) { + case 0: { + user.getExternalAttrs() + .add(WxCpUser.ExternalAttribute.builder() + .type(type) + .name(name) + .value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value")) + .build() + ); + break; + } + case 1: { + final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject(); + user.getExternalAttrs() + .add(WxCpUser.ExternalAttribute.builder() + .type(type) + .name(name) + .url(GsonHelper.getString(web, "url")) + .title(GsonHelper.getString(web, "title")) + .build() + ); + break; + } + case 2: { + final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject(); + user.getExternalAttrs() + .add(WxCpUser.ExternalAttribute.builder() + .type(type) + .name(name) + .appid(GsonHelper.getString(miniprogram, "appid")) + .pagePath(GsonHelper.getString(miniprogram, "pagepath")) + .title(GsonHelper.getString(miniprogram, "title")) + .build() + ); + break; + } + default://ignored + } + } + } + @Override public JsonElement serialize(WxCpUser user, Type typeOfSrc, JsonSerializationContext context) { JsonObject o = new JsonObject(); @@ -70,50 +191,179 @@ public JsonElement serialize(WxCpUser user, Type typeOfSrc, JsonSerializationCon } if (user.getDepartIds() != null) { JsonArray jsonArray = new JsonArray(); - for (Integer departId : user.getDepartIds()) { + for (Long departId : user.getDepartIds()) { jsonArray.add(new JsonPrimitive(departId)); } o.add("department", jsonArray); } + + if (user.getOrders() != null) { + JsonArray jsonArray = new JsonArray(); + for (Integer order : user.getOrders()) { + jsonArray.add(new JsonPrimitive(order)); + } + o.add("order", jsonArray); + } + if (user.getPosition() != null) { o.addProperty("position", user.getPosition()); } + + if (user.getPositions() != null) { + JsonArray jsonArray = new JsonArray(); + for (String position : user.getPositions()) { + jsonArray.add(new JsonPrimitive(position)); + } + o.add("positions", jsonArray); + } + if (user.getMobile() != null) { o.addProperty("mobile", user.getMobile()); } if (user.getGender() != null) { - o.addProperty("gender", user.getGender()); - } - if (user.getTel() != null) { - o.addProperty("tel", user.getTel()); + o.addProperty("gender", user.getGender().getCode()); } if (user.getEmail() != null) { o.addProperty("email", user.getEmail()); } - if (user.getWeiXinId() != null) { - o.addProperty("weixinid", user.getWeiXinId()); - } if (user.getAvatar() != null) { o.addProperty("avatar", user.getAvatar()); } + if (user.getThumbAvatar() != null) { + o.addProperty("thumb_avatar", user.getThumbAvatar()); + } + if (user.getAddress() != null) { + o.addProperty("address", user.getAddress()); + } + if (user.getAvatarMediaId() != null) { + o.addProperty("avatar_mediaid", user.getAvatarMediaId()); + } if (user.getStatus() != null) { o.addProperty("status", user.getStatus()); } if (user.getEnable() != null) { o.addProperty("enable", user.getEnable()); } + if (user.getAlias() != null) { + o.addProperty("alias", user.getAlias()); + } + if (user.getIsLeader() != null) { + o.addProperty("isleader", user.getIsLeader()); + } + if (user.getIsLeaderInDept() != null && user.getIsLeaderInDept().length > 0) { + JsonArray ary = new JsonArray(); + for (int item : user.getIsLeaderInDept()) { + ary.add(item); + } + o.add("is_leader_in_dept", ary); + } + if (user.getHideMobile() != null) { + o.addProperty("hide_mobile", user.getHideMobile()); + } + if (user.getEnglishName() != null) { + o.addProperty("english_name", user.getEnglishName()); + } + if (user.getTelephone() != null) { + o.addProperty("telephone", user.getTelephone()); + } + if (user.getQrCode() != null) { + o.addProperty("qr_code", user.getQrCode()); + } + if (user.getToInvite() != null) { + o.addProperty("to_invite", user.getToInvite()); + } + if (user.getMainDepartment() != null) { + o.addProperty("main_department", user.getMainDepartment()); + } + if (user.getExtAttrs().size() > 0) { JsonArray attrsJsonArray = new JsonArray(); for (WxCpUser.Attr attr : user.getExtAttrs()) { JsonObject attrJson = new JsonObject(); - attrJson.addProperty("name", attr.getName()); - attrJson.addProperty("value", attr.getValue()); + attrsJsonArray.add(attrJson); + + if (attr.getType() == null) { + attrJson.addProperty("name", attr.getName()); + attrJson.addProperty("value", attr.getTextValue()); + continue; + } + + switch (attr.getType()) { + case 0: { + JsonObject text = new JsonObject(); + text.addProperty("value", attr.getTextValue()); + attrJson.add("text", text); + break; + } + case 1: { + JsonObject web = new JsonObject(); + web.addProperty("url", attr.getWebUrl()); + web.addProperty("title", attr.getWebTitle()); + attrJson.add("web", web); + break; + } + default: //ignored + } } JsonObject attrsJson = new JsonObject(); attrsJson.add("attrs", attrsJsonArray); - o.add("extattr", attrsJson); + o.add(EXTRA_ATTR, attrsJson); + } + + if (user.getExternalPosition() != null) { + o.addProperty(EXTERNAL_POSITION, user.getExternalPosition()); + } + + JsonObject attrsJson = new JsonObject(); + o.add(EXTERNAL_PROFILE, attrsJson); + + if (user.getExternalCorpName() != null) { + attrsJson.addProperty(EXTERNAL_CORP_NAME, user.getExternalCorpName()); + } + + if (user.getExternalAttrs().size() > 0) { + JsonArray attrsJsonArray = new JsonArray(); + for (WxCpUser.ExternalAttribute attr : user.getExternalAttrs()) { + JsonObject attrJson = new JsonObject(); + attrJson.addProperty("type", attr.getType()); + attrJson.addProperty("name", attr.getName()); + + attrsJsonArray.add(attrJson); + + if (attr.getType() == null) { + continue; + } + + switch (attr.getType()) { + case 0: { + JsonObject text = new JsonObject(); + text.addProperty("value", attr.getValue()); + attrJson.add("text", text); + break; + } + case 1: { + JsonObject web = new JsonObject(); + web.addProperty("url", attr.getUrl()); + web.addProperty("title", attr.getTitle()); + attrJson.add("web", web); + break; + } + case 2: { + JsonObject miniprogram = new JsonObject(); + miniprogram.addProperty("appid", attr.getAppid()); + miniprogram.addProperty("pagepath", attr.getPagePath()); + miniprogram.addProperty("title", attr.getTitle()); + attrJson.add("miniprogram", miniprogram); + break; + } + default://忽略 + } + } + + attrsJson.add(EXTERNAL_ATTR, attrsJsonArray); } + return o; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java index 6b58063062..3c6174c9d8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java @@ -1,105 +1,135 @@ -package me.chanjar.weixin.cp.util.xml; - -import com.thoughtworks.xstream.XStream; -import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.cp.bean.*; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -public class XStreamTransformer { - - protected static final Map CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); - - /** - * xml -> pojo - */ - @SuppressWarnings("unchecked") - public static T fromXml(Class clazz, String xml) { - T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); - return object; - } - - @SuppressWarnings("unchecked") - public static T fromXml(Class clazz, InputStream is) { - T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); - return object; - } - - /** - * 注册扩展消息的解析器 - * - * @param clz 类型 - * @param xStream xml解析器 - */ - public static void register(Class clz, XStream xStream) { - CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); - } - - /** - * pojo -> xml - */ - public static String toXml(Class clazz, T object) { - return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); - } - - private static Map configXStreamInstance() { - Map map = new HashMap<>(); - map.put(WxCpXmlMessage.class, config_WxCpXmlMessage()); - map.put(WxCpXmlOutNewsMessage.class, config_WxCpXmlOutNewsMessage()); - map.put(WxCpXmlOutTextMessage.class, config_WxCpXmlOutTextMessage()); - map.put(WxCpXmlOutImageMessage.class, config_WxCpXmlOutImageMessage()); - map.put(WxCpXmlOutVideoMessage.class, config_WxCpXmlOutVideoMessage()); - map.put(WxCpXmlOutVoiceMessage.class, config_WxCpXmlOutVoiceMessage()); - return map; - } - - private static XStream config_WxCpXmlMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxCpXmlMessage.class); - xstream.processAnnotations(WxCpXmlMessage.ScanCodeInfo.class); - xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class); - xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class); - xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class); - return xstream; - } - - private static XStream config_WxCpXmlOutImageMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxCpXmlOutMessage.class); - xstream.processAnnotations(WxCpXmlOutImageMessage.class); - return xstream; - } - - private static XStream config_WxCpXmlOutNewsMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxCpXmlOutMessage.class); - xstream.processAnnotations(WxCpXmlOutNewsMessage.class); - xstream.processAnnotations(WxCpXmlOutNewsMessage.Item.class); - return xstream; - } - - private static XStream config_WxCpXmlOutTextMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxCpXmlOutMessage.class); - xstream.processAnnotations(WxCpXmlOutTextMessage.class); - return xstream; - } - - private static XStream config_WxCpXmlOutVideoMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxCpXmlOutMessage.class); - xstream.processAnnotations(WxCpXmlOutVideoMessage.class); - xstream.processAnnotations(WxCpXmlOutVideoMessage.Video.class); - return xstream; - } - - private static XStream config_WxCpXmlOutVoiceMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxCpXmlOutMessage.class); - xstream.processAnnotations(WxCpXmlOutVoiceMessage.class); - return xstream; - } - -} +package me.chanjar.weixin.cp.util.xml; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import com.thoughtworks.xstream.XStream; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import me.chanjar.weixin.cp.bean.WxCpTpXmlMessage; +import me.chanjar.weixin.cp.bean.WxCpTpXmlPackage; +import me.chanjar.weixin.cp.bean.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.WxCpXmlOutImageMessage; +import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; +import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; +import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; +import me.chanjar.weixin.cp.bean.WxCpXmlOutVideoMessage; +import me.chanjar.weixin.cp.bean.WxCpXmlOutVoiceMessage; + +public class XStreamTransformer { + + protected static final Map CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); + + /** + * xml -> pojo + */ + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, String xml) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); + return object; + } + + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, InputStream is) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); + return object; + } + + /** + * 注册扩展消息的解析器. + * + * @param clz 类型 + * @param xStream xml解析器 + */ + public static void register(Class clz, XStream xStream) { + CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); + } + + /** + * pojo -> xml. + */ + public static String toXml(Class clazz, T object) { + return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); + } + + private static Map configXStreamInstance() { + Map map = new HashMap<>(); + map.put(WxCpXmlMessage.class, configWxCpXmlMessage()); + map.put(WxCpXmlOutNewsMessage.class, configWxCpXmlOutNewsMessage()); + map.put(WxCpXmlOutTextMessage.class, configWxCpXmlOutTextMessage()); + map.put(WxCpXmlOutImageMessage.class, configWxCpXmlOutImageMessage()); + map.put(WxCpXmlOutVideoMessage.class, configWxCpXmlOutVideoMessage()); + map.put(WxCpXmlOutVoiceMessage.class, configWxCpXmlOutVoiceMessage()); + map.put(WxCpTpXmlPackage.class, configWxCpTpXmlPackage()); + map.put(WxCpTpXmlMessage.class, configWxCpTpXmlMessage()); + return map; + } + + private static XStream configWxCpXmlMessage() { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(WxCpXmlMessage.class); + xstream.processAnnotations(WxCpXmlMessage.ScanCodeInfo.class); + xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class); + xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class); + xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class); + return xstream; + } + + private static XStream configWxCpXmlOutImageMessage() { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(WxCpXmlOutMessage.class); + xstream.processAnnotations(WxCpXmlOutImageMessage.class); + return xstream; + } + + private static XStream configWxCpXmlOutNewsMessage() { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(WxCpXmlOutMessage.class); + xstream.processAnnotations(WxCpXmlOutNewsMessage.class); + xstream.processAnnotations(WxCpXmlOutNewsMessage.Item.class); + return xstream; + } + + private static XStream configWxCpXmlOutTextMessage() { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(WxCpXmlOutMessage.class); + xstream.processAnnotations(WxCpXmlOutTextMessage.class); + return xstream; + } + + private static XStream configWxCpXmlOutVideoMessage() { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(WxCpXmlOutMessage.class); + xstream.processAnnotations(WxCpXmlOutVideoMessage.class); + xstream.processAnnotations(WxCpXmlOutVideoMessage.Video.class); + return xstream; + } + + private static XStream configWxCpXmlOutVoiceMessage() { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(WxCpXmlOutMessage.class); + xstream.processAnnotations(WxCpXmlOutVoiceMessage.class); + return xstream; + } + + private static XStream configWxCpTpXmlPackage() { + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(WxCpTpXmlPackage.class); + + return xstream; + } + + private static XStream configWxCpTpXmlMessage() { + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(WxCpTpXmlMessage.class); + + return xstream; + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java index 2a694ff32f..c15e3af4d1 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java @@ -3,16 +3,22 @@ import java.io.IOException; import java.io.InputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.inject.Binder; import com.google.inject.Module; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; - import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; public class ApiTestModule implements Module { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + private static final String TEST_CONFIG_XML = "test-config.xml"; - public static T fromXml(Class clazz, InputStream is) { + private static T fromXml(Class clazz, InputStream is) { XStream xstream = XStreamInitializer.getInstance(); xstream.alias("xml", clazz); xstream.processAnnotations(clazz); @@ -21,22 +27,24 @@ public static T fromXml(Class clazz, InputStream is) { @Override public void configure(Binder binder) { - try (InputStream is1 = ClassLoader - .getSystemResourceAsStream("test-config.xml")) { - WxXmlCpInMemoryConfigStorage config = fromXml( - WxXmlCpInMemoryConfigStorage.class, is1); - WxCpServiceImpl wxService = new WxCpServiceImpl(); + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(TEST_CONFIG_XML)) { + if (inputStream == null) { + throw new RuntimeException("测试配置文件【" + TEST_CONFIG_XML + "】未找到,请参照test-config-sample.xml文件生成"); + } + + WxXmlCpInMemoryConfigStorage config = fromXml(WxXmlCpInMemoryConfigStorage.class, inputStream); + WxCpService wxService = new WxCpServiceImpl(); wxService.setWxCpConfigStorage(config); - binder.bind(WxCpServiceImpl.class).toInstance(wxService); - binder.bind(WxCpConfigStorage.class).toInstance(config); + binder.bind(WxCpService.class).toInstance(wxService); + binder.bind(WxXmlCpInMemoryConfigStorage.class).toInstance(config); } catch (IOException e) { - e.printStackTrace(); + this.log.error(e.getMessage(), e); } } @XStreamAlias("xml") - public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStorage { + public static class WxXmlCpInMemoryConfigStorage extends WxCpDefaultConfigImpl { protected String userId; @@ -44,6 +52,8 @@ public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStora protected String tagId; + protected String externalUserId; + public String getUserId() { return this.userId; } @@ -68,13 +78,23 @@ public void setTagId(String tagId) { this.tagId = tagId; } + public String getExternalUserId() { + return externalUserId; + } + + public void setExternalUserId(String externalUserId) { + this.externalUserId = externalUserId; + } + @Override public String toString() { return super.toString() + " > WxXmlCpConfigStorage{" + - "userId='" + this.userId + '\'' + - ", departmentId='" + this.departmentId + '\'' + - ", tagId='" + this.tagId + '\'' + - '}'; + "userId='" + this.userId + '\'' + + ", departmentId='" + this.departmentId + '\'' + + ", tagId='" + this.tagId + '\'' + + ", externalUserId='" + this.externalUserId + '\'' + + + '}'; } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java index 894bb3b67d..58ba1a9791 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java @@ -1,7 +1,9 @@ package me.chanjar.weixin.cp.api; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import org.apache.commons.lang3.StringUtils; import org.testng.Assert; import org.testng.annotations.Guice; @@ -20,7 +22,7 @@ public class WxCpBaseAPITest { protected WxCpServiceImpl wxService; public void testRefreshAccessToken() throws WxErrorException { - WxCpConfigStorage configStorage = this.wxService.configStorage; + WxCpConfigStorage configStorage = this.wxService.getWxCpConfigStorage(); String before = configStorage.getAccessToken(); this.wxService.getAccessToken(false); diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java index e1b005bd83..0bd8a24de3 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java @@ -1,8 +1,10 @@ package me.chanjar.weixin.cp.api; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -12,27 +14,25 @@ import java.util.concurrent.Future; @Test +@Slf4j public class WxCpBusyRetryTest { - @DataProvider(name = "getService") public Object[][] getService() { WxCpService service = new WxCpServiceImpl() { @Override - protected synchronized T executeInternal( - RequestExecutor executor, String uri, E data) - throws WxErrorException { - this.log.info("Executed"); - WxError error = new WxError(); - error.setErrorCode(-1); - throw new WxErrorException(error); + public synchronized T executeInternal( + RequestExecutor executor, String uri, E data) + throws WxErrorException { + log.info("Executed"); + throw new WxErrorException(WxError.builder().errorCode(-1).build()); } }; service.setMaxRetryTimes(3); service.setRetrySleepMillis(500); return new Object[][]{ - new Object[]{service} + new Object[]{service} }; } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java deleted file mode 100644 index c0cc3251ae..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java +++ /dev/null @@ -1,64 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import java.util.List; - -import org.testng.Assert; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; - -import com.google.inject.Inject; - -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.bean.WxCpDepart; - -/** - * 测试部门接口 - * - * @author Daniel Qian - */ -@Test(groups = "departAPI", dependsOnGroups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxCpDepartAPITest { - - @Inject - protected WxCpServiceImpl wxCpService; - - protected WxCpDepart depart; - - public void testDepartCreate() throws WxErrorException { - WxCpDepart cpDepart = new WxCpDepart(); - cpDepart.setName("子部门" + System.currentTimeMillis()); - cpDepart.setParentId(1); - cpDepart.setOrder(1); - Integer departId = this.wxCpService.departCreate(cpDepart); - System.out.println(departId); - } - - @Test(dependsOnMethods = "testDepartCreate") - public void testDepartGet() throws WxErrorException { - System.out.println("=================获取部门"); - List departList = this.wxCpService.departGet(); - Assert.assertNotNull(departList); - Assert.assertTrue(departList.size() > 0); - for (WxCpDepart g : departList) { - this.depart = g; - System.out.println(this.depart.getId() + ":" + this.depart.getName()); - Assert.assertNotNull(g.getName()); - } - } - - @Test(dependsOnMethods = {"testDepartGet", "testDepartCreate"}) - public void testDepartUpdate() throws WxErrorException { - System.out.println("=================更新部门"); - this.depart.setName("子部门改名" + System.currentTimeMillis()); - this.wxCpService.departUpdate(this.depart); - } - - @Test(dependsOnMethods = "testDepartUpdate") - public void testDepartDelete() throws WxErrorException { - System.out.println("=================删除部门"); - System.out.println(this.depart.getId() + ":" + this.depart.getName()); - this.wxCpService.departDelete(this.depart.getId()); - } - -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java deleted file mode 100644 index d9fb2605d0..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java +++ /dev/null @@ -1,78 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; - -import com.google.inject.Inject; - -import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; - -/** - * 测试多媒体文件上传下载 - * - * @author Daniel Qian - */ -//@Test(groups="mediaAPI", dependsOnGroups="baseAPI") -@Test -@Guice(modules = ApiTestModule.class) -public class WxCpMediaAPITest { - - @Inject - protected WxCpServiceImpl wxService; - - private List media_ids = new ArrayList<>(); - - @Test(dataProvider = "uploadMedia") - public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { - try (InputStream inputStream = ClassLoader - .getSystemResourceAsStream(fileName);) { - WxMediaUploadResult res = this.wxService.mediaUpload(mediaType, fileType, - inputStream); - Assert.assertNotNull(res.getType()); - Assert.assertNotNull(res.getCreatedAt()); - Assert.assertTrue( - res.getMediaId() != null || res.getThumbMediaId() != null); - - if (res.getMediaId() != null) { - this.media_ids.add(res.getMediaId()); - } - if (res.getThumbMediaId() != null) { - this.media_ids.add(res.getThumbMediaId()); - } - } - } - - @DataProvider - public Object[][] uploadMedia() { - return new Object[][]{ - new Object[]{WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, - new Object[]{WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, "mm.mp3"}, - new Object[]{WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, - new Object[]{WxConsts.MEDIA_FILE, TestConstants.FILE_JPG, "mm.jpeg"} - }; - } - - @Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadMedia") - public void testDownloadMedia(String media_id) throws WxErrorException { - this.wxService.mediaDownload(media_id); - } - - @DataProvider - public Object[][] downloadMedia() { - Object[][] params = new Object[this.media_ids.size()][]; - for (int i = 0; i < this.media_ids.size(); i++) { - params[i] = new Object[]{this.media_ids.get(i)}; - } - return params; - } - -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java index b7bcf7a377..d0984565aa 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java @@ -1,43 +1,134 @@ package me.chanjar.weixin.cp.api; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.*; import com.google.inject.Inject; - import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.cp.bean.WxCpMessage; +import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; + +import static org.testng.Assert.*; /*** * 测试发送消息 * @author Daniel Qian * */ -@Test(groups = "customMessageAPI", dependsOnGroups = "baseAPI") +@Test @Guice(modules = ApiTestModule.class) public class WxCpMessageAPITest { @Inject - protected WxCpServiceImpl wxService; - - public void testSendCustomMessage() throws WxErrorException { - ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage = (ApiTestModule.WxXmlCpInMemoryConfigStorage) this.wxService.configStorage; - WxCpMessage message1 = new WxCpMessage(); - message1.setAgentId(configStorage.getAgentId()); - message1.setMsgType(WxConsts.CUSTOM_MSG_TEXT); - message1.setToUser(configStorage.getUserId()); - message1.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); - this.wxService.messageSend(message1); - - WxCpMessage message2 = WxCpMessage - .TEXT() - .agentId(configStorage.getAgentId()) - .toUser(configStorage.getUserId()) - .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World") - .build(); - this.wxService.messageSend(message2); + protected WxCpService wxService; + + private ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; + + @BeforeTest + public void setup() { + configStorage = (ApiTestModule.WxXmlCpInMemoryConfigStorage) this.wxService.getWxCpConfigStorage(); + } + + public void testSendMessage() throws WxErrorException { + WxCpMessage message = new WxCpMessage(); +// message.setAgentId(configStorage.getAgentId()); + message.setMsgType(WxConsts.KefuMsgType.TEXT); + message.setToUser(configStorage.getUserId()); + message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } + + @Test + public void testSendMessage1() throws WxErrorException { + WxCpMessage message = WxCpMessage + .TEXT() +// .agentId(configStorage.getAgentId()) + .toUser(configStorage.getUserId()) + .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World") + .build(); + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); } + @Test + public void testSendMessage_markdown() throws WxErrorException { + WxCpMessage message = WxCpMessage + .MARKDOWN() + .toUser(configStorage.getUserId()) + .content("您的会议室已经预定,稍后会同步到`邮箱` \n" + + " >**事项详情** \n" + + " >事 项:开会 \n" + + " >组织者:@miglioguan \n" + + " >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" + + " > \n" + + " >会议室:广州TIT 1楼 301 \n" + + " >日 期:2018年5月18日 \n" + + " >时 间:上午9:00-11:00 \n" + + " > \n" + + " >请准时参加会议。 \n" + + " > \n" + + " >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)") + .build(); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } + + @Test + public void testSendMessage_textCard() throws WxErrorException { + WxCpMessage message = WxCpMessage + .TEXTCARD() + .toUser(configStorage.getUserId()) + .btnTxt("更多") + .description( "
    2016年9月26日
    恭喜你抽中iPhone 7一台,领奖码:xxxx
    请于2016年10月10日前联系行政同事领取
    ") + .url("URL") + .title("领奖通知") + .build(); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } + + @Test + public void testSendMessage_miniprogram_notice() throws WxErrorException { + WxCpMessage message = WxCpMessage + .newMiniProgramNoticeBuilder() + .toUser(configStorage.getUserId()) + .appId("wx123123123123123") + .page("pages/index?userid=zhangsan&orderid=123123123") + .title("会议室预订成功通知") + .description("4月27日 16:16") + .emphasisFirstItem(true) + .contentItems(ImmutableMap.of("会议室","402", + "会议地点","广州TIT-402会议室", + "会议时间","2018年8月1日 09:00-09:30")) + .build(); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java index 4b0fc56a92..a213488953 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java @@ -3,6 +3,9 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.session.StandardSessionManager; import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.message.WxCpMessageHandler; +import me.chanjar.weixin.cp.message.WxCpMessageMatcher; +import me.chanjar.weixin.cp.message.WxCpMessageRouter; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; import org.testng.Assert; @@ -22,33 +25,33 @@ public class WxCpMessageRouterTest { @Test(enabled = false) public void prepare(boolean async, StringBuffer sb, WxCpMessageRouter router) { router - .rule() - .async(async) - .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1") - .handler(new WxEchoCpMessageHandler(sb, "COMBINE_4")) - .end() - .rule() - .async(async) - .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1") - .handler(new WxEchoCpMessageHandler(sb, "COMBINE_3")) - .end() - .rule() - .async(async) - .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK) - .handler(new WxEchoCpMessageHandler(sb, "COMBINE_2")) - .end() - .rule().async(async).msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoCpMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end() - .rule().async(async).event(WxConsts.EVT_CLICK).handler(new WxEchoCpMessageHandler(sb, WxConsts.EVT_CLICK)).end() - .rule().async(async).eventKey("KEY_1").handler(new WxEchoCpMessageHandler(sb, "KEY_1")).end() - .rule().async(async).content("CONTENT_1").handler(new WxEchoCpMessageHandler(sb, "CONTENT_1")).end() - .rule().async(async).rContent(".*bc.*").handler(new WxEchoCpMessageHandler(sb, "abcd")).end() - .rule().async(async).matcher(new WxCpMessageMatcher() { + .rule() + .async(async) + .msgType(WxConsts.XmlMsgType.TEXT).event(WxConsts.EventType.CLICK).eventKey("KEY_1").content("CONTENT_1") + .handler(new WxEchoCpMessageHandler(sb, "COMBINE_4")) + .end() + .rule() + .async(async) + .msgType(WxConsts.XmlMsgType.TEXT).event(WxConsts.EventType.CLICK).eventKey("KEY_1") + .handler(new WxEchoCpMessageHandler(sb, "COMBINE_3")) + .end() + .rule() + .async(async) + .msgType(WxConsts.XmlMsgType.TEXT).event(WxConsts.EventType.CLICK) + .handler(new WxEchoCpMessageHandler(sb, "COMBINE_2")) + .end() + .rule().async(async).msgType(WxConsts.XmlMsgType.TEXT).handler(new WxEchoCpMessageHandler(sb, WxConsts.XmlMsgType.TEXT)).end() + .rule().async(async).event(WxConsts.EventType.CLICK).handler(new WxEchoCpMessageHandler(sb, WxConsts.EventType.CLICK)).end() + .rule().async(async).eventKey("KEY_1").handler(new WxEchoCpMessageHandler(sb, "KEY_1")).end() + .rule().async(async).content("CONTENT_1").handler(new WxEchoCpMessageHandler(sb, "CONTENT_1")).end() + .rule().async(async).rContent(".*bc.*").handler(new WxEchoCpMessageHandler(sb, "abcd")).end() + .rule().async(async).matcher(new WxCpMessageMatcher() { @Override public boolean match(WxCpXmlMessage message) { return "strangeformat".equals(message.getFormat()); } }).handler(new WxEchoCpMessageHandler(sb, "matcher")).end() - .rule().async(async).handler(new WxEchoCpMessageHandler(sb, "ALL")).end(); + .rule().async(async).handler(new WxEchoCpMessageHandler(sb, "ALL")).end(); } @Test(dataProvider = "messages-1") @@ -66,7 +69,7 @@ public void testAsync(WxCpXmlMessage message, String expected) throws Interrupte WxCpMessageRouter router = new WxCpMessageRouter(null); prepare(true, sb, router); router.route(message); - Thread.sleep(500l); + Thread.sleep(500); Assert.assertEquals(sb.toString(), expected); } @@ -86,7 +89,7 @@ public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map co public void run() { router.route(m); try { - Thread.sleep(1000l); + Thread.sleep(1000); } catch (InterruptedException e) { } } @@ -95,16 +98,16 @@ public void run() { new Thread(r).start(); } - Thread.sleep(1000l * 2); + Thread.sleep(2000); } @DataProvider(name = "messages-1") public Object[][] messages2() { WxCpXmlMessage message1 = new WxCpXmlMessage(); - message1.setMsgType(WxConsts.XML_MSG_TEXT); + message1.setMsgType(WxConsts.XmlMsgType.TEXT); WxCpXmlMessage message2 = new WxCpXmlMessage(); - message2.setEvent(WxConsts.EVT_CLICK); + message2.setEvent(WxConsts.EventType.CLICK); WxCpXmlMessage message3 = new WxCpXmlMessage(); message3.setEventKey("KEY_1"); @@ -122,32 +125,32 @@ public Object[][] messages2() { message7.setFormat("strangeformat"); WxCpXmlMessage c2 = new WxCpXmlMessage(); - c2.setMsgType(WxConsts.XML_MSG_TEXT); - c2.setEvent(WxConsts.EVT_CLICK); + c2.setMsgType(WxConsts.XmlMsgType.TEXT); + c2.setEvent(WxConsts.EventType.CLICK); WxCpXmlMessage c3 = new WxCpXmlMessage(); - c3.setMsgType(WxConsts.XML_MSG_TEXT); - c3.setEvent(WxConsts.EVT_CLICK); + c3.setMsgType(WxConsts.XmlMsgType.TEXT); + c3.setEvent(WxConsts.EventType.CLICK); c3.setEventKey("KEY_1"); WxCpXmlMessage c4 = new WxCpXmlMessage(); - c4.setMsgType(WxConsts.XML_MSG_TEXT); - c4.setEvent(WxConsts.EVT_CLICK); + c4.setMsgType(WxConsts.XmlMsgType.TEXT); + c4.setEvent(WxConsts.EventType.CLICK); c4.setEventKey("KEY_1"); c4.setContent("CONTENT_1"); return new Object[][]{ - new Object[]{message1, WxConsts.XML_MSG_TEXT + ","}, - new Object[]{message2, WxConsts.EVT_CLICK + ","}, - new Object[]{message3, "KEY_1,"}, - new Object[]{message4, "CONTENT_1,"}, - new Object[]{message5, "ALL,"}, - new Object[]{message6, "abcd,"}, - new Object[]{message7, "matcher,"}, - new Object[]{c2, "COMBINE_2,"}, - new Object[]{c3, "COMBINE_3,"}, - new Object[]{c4, "COMBINE_4,"} + new Object[]{message1, WxConsts.XmlMsgType.TEXT + ","}, + new Object[]{message2, WxConsts.EventType.CLICK + ","}, + new Object[]{message3, "KEY_1,"}, + new Object[]{message4, "CONTENT_1,"}, + new Object[]{message5, "ALL,"}, + new Object[]{message6, "abcd,"}, + new Object[]{message7, "matcher,"}, + new Object[]{c2, "COMBINE_2,"}, + new Object[]{c3, "COMBINE_3,"}, + new Object[]{c4, "COMBINE_4,"} }; } @@ -162,7 +165,7 @@ public Object[][] standardSessionManager() { ism.setBackgroundProcessorDelay(1); return new Object[][]{ - new Object[]{ism} + new Object[]{ism} }; } @@ -174,14 +177,14 @@ public void testSessionClean1(StandardSessionManager ism) throws InterruptedExce final WxCpMessageRouter router = new WxCpMessageRouter(null); router.setSessionManager(ism); router - .rule().async(false).handler(new WxSessionMessageHandler()).next() - .rule().async(false).handler(new WxSessionMessageHandler()).end(); + .rule().async(false).handler(new WxSessionMessageHandler()).next() + .rule().async(false).handler(new WxSessionMessageHandler()).end(); WxCpXmlMessage msg = new WxCpXmlMessage(); msg.setFromUserName("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -194,28 +197,28 @@ public void testSessionClean2(StandardSessionManager ism) throws InterruptedExce final WxCpMessageRouter router = new WxCpMessageRouter(null); router.setSessionManager(ism); router - .rule().async(false).handler(new WxSessionMessageHandler()).next() - .rule().async(true).handler(new WxSessionMessageHandler()).end(); + .rule().async(false).handler(new WxSessionMessageHandler()).next() + .rule().async(true).handler(new WxSessionMessageHandler()).end(); WxCpXmlMessage msg = new WxCpXmlMessage(); msg.setFromUserName("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } { final WxCpMessageRouter router = new WxCpMessageRouter(null); router.setSessionManager(ism); router - .rule().async(true).handler(new WxSessionMessageHandler()).next() - .rule().async(false).handler(new WxSessionMessageHandler()).end(); + .rule().async(true).handler(new WxSessionMessageHandler()).next() + .rule().async(false).handler(new WxSessionMessageHandler()).end(); WxCpXmlMessage msg = new WxCpXmlMessage(); msg.setFromUserName("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -228,14 +231,14 @@ public void testSessionClean3(StandardSessionManager ism) throws InterruptedExce final WxCpMessageRouter router = new WxCpMessageRouter(null); router.setSessionManager(ism); router - .rule().async(true).handler(new WxSessionMessageHandler()).next() - .rule().async(true).handler(new WxSessionMessageHandler()).end(); + .rule().async(true).handler(new WxSessionMessageHandler()).next() + .rule().async(true).handler(new WxSessionMessageHandler()).end(); WxCpXmlMessage msg = new WxCpXmlMessage(); msg.setFromUserName("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -248,13 +251,13 @@ public void testSessionClean4(StandardSessionManager ism) throws InterruptedExce final WxCpMessageRouter router = new WxCpMessageRouter(null); router.setSessionManager(ism); router - .rule().async(false).handler(new WxSessionMessageHandler()).end(); + .rule().async(false).handler(new WxSessionMessageHandler()).end(); WxCpXmlMessage msg = new WxCpXmlMessage(); msg.setFromUserName("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -262,13 +265,13 @@ public void testSessionClean4(StandardSessionManager ism) throws InterruptedExce final WxCpMessageRouter router = new WxCpMessageRouter(null); router.setSessionManager(ism); router - .rule().async(true).handler(new WxSessionMessageHandler()).end(); + .rule().async(true).handler(new WxSessionMessageHandler()).end(); WxCpXmlMessage msg = new WxCpXmlMessage(); msg.setFromUserName("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java deleted file mode 100644 index aa26e9620a..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.cp.bean.WxCpTag; -import me.chanjar.weixin.cp.bean.WxCpUser; -import org.testng.Assert; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; - -import java.util.ArrayList; -import java.util.List; - -@Test(groups = "departAPI", dependsOnGroups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxCpTagAPITest { - - @Inject - protected WxCpServiceImpl wxService; - - @Inject - protected WxCpConfigStorage configStorage; - - protected String tagId; - - public void testTagCreate() throws Exception { - this.tagId = this.wxService.tagCreate("测试标签4"); - System.out.println(this.tagId); - } - - @Test(dependsOnMethods = "testTagCreate") - public void testTagUpdate() throws Exception { - this.wxService.tagUpdate(this.tagId, "测试标签-改名"); - } - - @Test(dependsOnMethods = "testTagUpdate") - public void testTagGet() throws Exception { - List tags = this.wxService.tagGet(); - Assert.assertNotEquals(tags.size(), 0); - } - - @Test(dependsOnMethods = "testTagGet") - public void testTagAddUsers() throws Exception { - List userIds = new ArrayList<>(); - userIds.add(((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.configStorage).getUserId()); - this.wxService.tagAddUsers(this.tagId, userIds, null); - } - - @Test(dependsOnMethods = "testTagAddUsers") - public void testTagGetUsers() throws Exception { - List users = this.wxService.tagGetUsers(this.tagId); - Assert.assertNotEquals(users.size(), 0); - } - - @Test(dependsOnMethods = "testTagGetUsers") - public void testTagRemoveUsers() throws Exception { - List userIds = new ArrayList<>(); - userIds.add(((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.configStorage).getUserId()); - this.wxService.tagRemoveUsers(this.tagId, userIds); - } - - @Test(dependsOnMethods = "testTagRemoveUsers") - public void testTagDelete() throws Exception { - this.wxService.tagDelete(this.tagId); - } - -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java deleted file mode 100644 index 4d5a7a91f0..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpUser; -import org.testng.Assert; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; - -import java.util.List; - -/** - * 测试用户接口 - * - * @author Daniel Qian - */ -@Test(groups = "userAPI", dependsOnGroups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxCpUserAPITest { - - @Inject - protected WxCpServiceImpl wxCpService; - - protected WxCpDepart depart; - - public void testUserCreate() throws WxErrorException { - WxCpUser user = new WxCpUser(); - user.setUserId("some.woman"); - user.setName("Some Woman"); - user.setDepartIds(new Integer[]{9, 8}); - user.setEmail("none@none.com"); - user.setGender("女"); - user.setMobile("13560084979"); - user.setPosition("woman"); - user.setTel("3300393"); - user.addExtAttr("爱好", "table"); - this.wxCpService.userCreate(user); - } - - @Test(dependsOnMethods = "testUserCreate") - public void testUserUpdate() throws WxErrorException { - WxCpUser user = new WxCpUser(); - user.setUserId("some.woman"); - user.setName("Some Woman"); - user.addExtAttr("爱好", "table2"); - this.wxCpService.userUpdate(user); - } - - @Test(dependsOnMethods = "testUserUpdate") - public void testUserGet() throws WxErrorException { - WxCpUser user = this.wxCpService.userGet("some.woman"); - Assert.assertNotNull(user); - } - - @Test(dependsOnMethods = "testUserGet") - public void testDepartGetUsers() throws WxErrorException { - List users = this.wxCpService.departGetUsers(1, true, 0); - Assert.assertNotEquals(users.size(), 0); - } - - @Test(dependsOnMethods = "testDepartGetUsers") - public void testUserDelete() throws WxErrorException { - this.wxCpService.userDelete("some.woman"); - } -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java new file mode 100644 index 0000000000..22cee8f405 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *  Created by BinaryWang on 2019/3/31.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class BaseWxCpServiceImplTest { + @Inject + protected WxCpService wxService; + + @Test + public void testGetAgentJsapiTicket() throws WxErrorException { + assertThat(this.wxService.getAgentJsapiTicket()).isNotEmpty(); + assertThat(this.wxService.getAgentJsapiTicket(true)).isNotEmpty(); + } + + @Test + public void testJsCode2Session() throws WxErrorException { + assertThat(this.wxService.jsCode2Session("111")).isNotNull(); + } + + @Test + public void testGetProviderToken() throws WxErrorException { + assertThat(this.wxService.getProviderToken("111","123")).isNotNull(); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImplTest.java new file mode 100644 index 0000000000..9f79735612 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImplTest.java @@ -0,0 +1,307 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpTpService; +import me.chanjar.weixin.cp.bean.WxCpTpAuthInfo; +import me.chanjar.weixin.cp.bean.WxCpTpCorp; +import me.chanjar.weixin.cp.bean.WxCpTpPermanentCodeInfo; +import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; +import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl; +import org.testng.annotations.Test; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tp.GET_AUTH_INFO; +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Tp.GET_PERMANENT_CODE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; +import static org.testng.Assert.*; + +/** + * 测试代码. + * + * @author Binary Wang + * @date 2019-08-18 + */ +public class BaseWxCpTpServiceImplTest { + private WxCpTpService tpService = spy(new WxCpTpServiceImpl()); + + @Test + public void testCheckSignature() { + } + + @Test + public void testGetSuiteAccessToken() { + } + + @Test + public void testGetSuiteTicket() { + } + + @Test + public void testTestGetSuiteTicket() { + } + + @Test + public void testJsCode2Session() { + } + + @Test + public void testGetCorpToken() { + } + + @Test + public void testGetPermanentCode() throws WxErrorException { + String returnJson = "{\n" + + " \"errcode\":0 ,\n" + + " \"errmsg\":\"ok\" ,\n" + + " \"access_token\": \"xxxxxx\", \n" + + " \"expires_in\": 7200, \n" + + " \"permanent_code\": \"xxxx\", \n" + + " \"dealer_corp_info\": \n" + + " {\n" + + " \"corpid\": \"xxxx\",\n" + + " \"corp_name\": \"name\"\n" + + " },\n" + + " \"auth_corp_info\": \n" + + " {\n" + + " \"corpid\": \"xxxx\",\n" + + " \"corp_name\": \"name\",\n" + + " \"corp_type\": \"verified\",\n" + + " \"corp_square_logo_url\": \"yyyyy\",\n" + + " \"corp_user_max\": 50,\n" + + " \"corp_agent_max\": 30,\n" + + " \"corp_full_name\":\"full_name\",\n" + + " \"verified_end_time\":1431775834,\n" + + " \"subject_type\": 1,\n" + + " \"corp_wxqrcode\": \"zzzzz\",\n" + + " \"corp_scale\": \"1-50人\",\n" + + " \"corp_industry\": \"IT服务\",\n" + + " \"corp_sub_industry\": \"计算机软件/硬件/信息服务\",\n" + + " \"location\":\"广东省广州市\"\n" + + " },\n" + + " \"auth_info\":\n" + + " {\n" + + " \"agent\" :\n" + + " [\n" + + " {\n" + + " \"agentid\":1,\n" + + " \"name\":\"NAME\",\n" + + " \"round_logo_url\":\"xxxxxx\",\n" + + " \"square_logo_url\":\"yyyyyy\",\n" + + " \"appid\":1,\n" + + " \"privilege\":\n" + + " {\n" + + " \"level\":1,\n" + + " \"allow_party\":[1,2,3],\n" + + " \"allow_user\":[\"zhansan\",\"lisi\"],\n" + + " \"allow_tag\":[1,2,3],\n" + + " \"extra_party\":[4,5,6],\n" + + " \"extra_user\":[\"wangwu\"],\n" + + " \"extra_tag\":[4,5,6]\n" + + " }\n" + + " },\n" + + " {\n" + + " \"agentid\":2,\n" + + " \"name\":\"NAME2\",\n" + + " \"round_logo_url\":\"xxxxxx\",\n" + + " \"square_logo_url\":\"yyyyyy\",\n" + + " \"appid\":5\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"auth_user_info\":\n" + + " {\n" + + " \"userid\":\"aa\",\n" + + " \"name\":\"xxx\",\n" + + " \"avatar\":\"http://xxx\"\n" + + " }\n" + + "}\n"; + + final WxCpTpConfigStorage configStorage = new WxCpTpDefaultConfigImpl(); + tpService.setWxCpTpConfigStorage(configStorage); + + JsonObject jsonObject = new JsonObject(); + String authCode = ""; + jsonObject.addProperty("auth_code", authCode); + doReturn(returnJson).when(tpService).post(configStorage.getApiUrl(GET_PERMANENT_CODE), jsonObject.toString()); + + final WxCpTpCorp tpCorp = tpService.getPermanentCode(authCode); + assertThat(tpCorp.getPermanentCode()).isEqualTo("xxxx"); + + final WxCpTpPermanentCodeInfo tpPermanentCodeInfo = tpService.getPermanentCodeInfo(authCode); + assertThat(tpPermanentCodeInfo.getAuthInfo().getAgents().get(0).getAgentId()).isEqualTo(1); + + } + + @Test + public void testGetPermanentCodeInfo() throws WxErrorException{ + String returnJson = "{\n" + + " \"access_token\": \"u6SoEWyrEmworJ1uNzddbiXh42mCLNU_mdd6b01Afo2LKmyi-WdaaYqhEGFZjB1RGZ-rhjLcAJ86ger7b7Q0gowSw9iIDR8dm49aVH_MztzmQttP3XFG7np1Dxs_VQkVwhhRmfRpEonAmK1_JWIFqayJXXiPUS3LsFd3tWpE7rxmsRa7Ev2ml2htbRp_qGUjtFTErKoDsnNGSka6_RqFPA\", \n" + + " \"expires_in\": 7200, \n" + + " \"permanent_code\": \"lMLlxss77ntxzuEl1i1_AQ3-6-cvqMLYs209YNWVruk\", \n" + + " \"auth_corp_info\": {\n" + + " \"corpid\": \"xxxcorpid\", \n" + + " \"corp_name\": \"xxxx有限公司\", \n" + + " \"corp_type\": \"unverified\", \n" + + " \"corp_round_logo_url\": \"http://p.qpic.cn/pic_wework/3777001839/4046834be7a5f2711feaaa3cc4e691e1bcb1e526cb4544b5/0\", \n" + + " \"corp_square_logo_url\": \"https://p.qlogo.cn/bizmail/EsvsszIt9hJrjrx8QKXuIs0iczdnV4icaPibLIViaukn1iazCay8L1UXtibA/0\", \n" + + " \"corp_user_max\": 200, \n" + + " \"corp_agent_max\": 300, \n" + + " \"corp_wxqrcode\": \"http://p.qpic.cn/pic_wework/211781738/a9af41a60af7519775dd7ac846a4942979dc4a14b8bb2c72/0\", \n" + + " \"corp_full_name\": \"xxxx有限公司\", \n" + + " \"subject_type\": 1, \n" + + " \"corp_scale\": \"1-50人\", \n" + + " \"corp_industry\": \"生活服务\", \n" + + " \"corp_sub_industry\": \"租赁和商务服务\", \n" + + " \"location\": \"北京市\"\n" + + " }, \n" + + " \"auth_info\": {\n" + + " \"agent\": [\n" + + " {\n" + + " \"agentid\": 1000012, \n" + + " \"name\": \"xxxxx\", \n" + + " \"square_logo_url\": \"http://wx.qlogo.cn/mmhead/Q3auHgzwzM4ZCtdxicN8ghMOtTv7M7rLPKmeZ3amic00btdwbNmicaW3Q/0\", \n" + + " \"privilege\": {\n" + + " \"level\": 1, \n" + + " \"allow_party\": [ ], \n" + + " \"allow_user\": [\n" + + " \"yuanqixun\"\n" + + " ], \n" + + " \"allow_tag\": [ ], \n" + + " \"extra_party\": [ ], \n" + + " \"extra_user\": [ ], \n" + + " \"extra_tag\": [ ]\n" + + " }\n" + + " }\n" + + " ]\n" + + " }, \n" + + " \"auth_user_info\": {\n" + + " \"userid\": \"yuanqixun\", \n" + + " \"name\": \"袁启勋\", \n" + + " \"avatar\": \"http://wework.qpic.cn/bizmail/ZYqy8EswiaFyPnk7gy7eiafoicz3TL35f4bAvCf2eSe6RVYSK7aPDFxcw/0\"\n" + + " }\n" + + "}"; + + final WxCpTpConfigStorage configStorage = new WxCpTpDefaultConfigImpl(); + tpService.setWxCpTpConfigStorage(configStorage); + JsonObject jsonObject = new JsonObject(); + String authCode = ""; + jsonObject.addProperty("auth_code", authCode); + doReturn(returnJson).when(tpService).post(configStorage.getApiUrl(GET_PERMANENT_CODE), jsonObject.toString()); + final WxCpTpPermanentCodeInfo tpPermanentCodeInfo = tpService.getPermanentCodeInfo(authCode); + assertThat(tpPermanentCodeInfo.getAuthInfo().getAgents().get(0).getAgentId()).isEqualTo(1000012); + assertNotNull(tpPermanentCodeInfo.getAuthInfo().getAgents().get(0).getSquareLogoUrl()); + assertNotNull(tpPermanentCodeInfo.getAuthCorpInfo().getCorpSquareLogoUrl()); + } + + @Test + public void testGetAuthInfo() throws WxErrorException{ + String returnJson = "{\n" + + " \"errcode\":0 ,\n" + + " \"errmsg\":\"ok\" ,\n" + + " \"dealer_corp_info\": \n" + + " {\n" + + " \"corpid\": \"xxxx\",\n" + + " \"corp_name\": \"name\"\n" + + " },\n" + + " \"auth_corp_info\": \n" + + " {\n" + + " \"corpid\": \"xxxx\",\n" + + " \"corp_name\": \"name\",\n" + + " \"corp_type\": \"verified\",\n" + + " \"corp_square_logo_url\": \"yyyyy\",\n" + + " \"corp_user_max\": 50,\n" + + " \"corp_agent_max\": 30,\n" + + " \"corp_full_name\":\"full_name\",\n" + + " \"verified_end_time\":1431775834,\n" + + " \"subject_type\": 1,\n" + + " \"corp_wxqrcode\": \"zzzzz\",\n" + + " \"corp_scale\": \"1-50人\",\n" + + " \"corp_industry\": \"IT服务\",\n" + + " \"corp_sub_industry\": \"计算机软件/硬件/信息服务\",\n" + + " \"location\":\"广东省广州市\"\n" + + " },\n" + + " \"auth_info\":\n" + + " {\n" + + " \"agent\" :\n" + + " [\n" + + " {\n" + + " \"agentid\":1,\n" + + " \"name\":\"NAME\",\n" + + " \"round_logo_url\":\"xxxxxx\",\n" + + " \"square_logo_url\":\"yyyyyy\",\n" + + " \"appid\":1,\n" + + " \"privilege\":\n" + + " {\n" + + " \"level\":1,\n" + + " \"allow_party\":[1,2,3],\n" + + " \"allow_user\":[\"zhansan\",\"lisi\"],\n" + + " \"allow_tag\":[1,2,3],\n" + + " \"extra_party\":[4,5,6],\n" + + " \"extra_user\":[\"wangwu\"],\n" + + " \"extra_tag\":[4,5,6]\n" + + " }\n" + + " },\n" + + " {\n" + + " \"agentid\":2,\n" + + " \"name\":\"NAME2\",\n" + + " \"round_logo_url\":\"xxxxxx\",\n" + + " \"square_logo_url\":\"yyyyyy\",\n" + + " \"appid\":5\n" + + " }\n" + + " ]\n" + + " }\n" + + "}\n"; + + final WxCpTpConfigStorage configStorage = new WxCpTpDefaultConfigImpl(); + tpService.setWxCpTpConfigStorage(configStorage); + JsonObject jsonObject = new JsonObject(); + String authCorpId = "xxxxx"; + String permanentCode = "xxxxx"; + jsonObject.addProperty("auth_corpid", authCorpId); + jsonObject.addProperty("permanent_code", permanentCode); + doReturn(returnJson).when(tpService).post(configStorage.getApiUrl(GET_AUTH_INFO), jsonObject.toString()); + WxCpTpAuthInfo authInfo = tpService.getAuthInfo(authCorpId,permanentCode); + assertNotNull(authInfo.getAuthCorpInfo().getCorpId()); + } + + @Test + public void testGet() { + } + + @Test + public void testPost() { + } + + @Test + public void testExecute() { + } + + @Test + public void testExecuteInternal() { + } + + @Test + public void testSetWxCpTpConfigStorage() { + } + + @Test + public void testSetRetrySleepMillis() { + } + + @Test + public void testSetMaxRetryTimes() { + } + + @Test + public void testGetTmpDirFile() { + } + + @Test + public void testSetTmpDirFile() { + } + + @Test + public void testGetRequestHttp() { + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java new file mode 100644 index 0000000000..97c7d40fe5 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java @@ -0,0 +1,94 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpAgentService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpAgent; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; +import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + + +/** + *
    + *  管理企业号应用-测试
    + *  Created by huansinho on 2018/4/13.
    + * 
    + * + * @author huansinho + */ +@Guice(modules = ApiTestModule.class) +public class WxCpAgentServiceImplTest { + @Inject + private WxCpService wxCpService; + + @Test + public void testGet() throws Exception { + final Integer agentId = this.wxCpService.getWxCpConfigStorage().getAgentId(); + WxCpAgent wxCpAgent = this.wxCpService.getAgentService().get(agentId); + + assertThat(wxCpAgent.getAgentId()).isEqualTo(agentId); + + assertThat(wxCpAgent.getAllowUserInfos().getUsers().toArray()).isNotEmpty(); + assertThat(wxCpAgent.getAllowParties().getPartyIds().toArray()).isNotEmpty(); + assertThat(wxCpAgent.getAllowTags().getTagIds().toArray()).isNotEmpty(); + } + + @Test + public void testSet() throws WxErrorException { + final Integer agentId = this.wxCpService.getWxCpConfigStorage().getAgentId(); + + this.wxCpService.getAgentService().set(WxCpAgent.builder() + .description("abcddd") + .logoMediaId("aaaaaaaaaaaaaa") + .agentId(agentId) + .build()); + } + + @Test + public void testList() throws WxErrorException { + List list = this.wxCpService.getAgentService().list(); + + assertThat(list).isNotEmpty(); + + assertThat(list.get(0).getAgentId()).isNotNull(); + assertThat(list.get(0).getName()).isNotEmpty(); + assertThat(list.get(0).getSquareLogoUrl()).isNotEmpty(); + } + + public static class MockTest { + private WxCpService wxService = mock(WxCpService.class); + + @Test + public void testGet() throws Exception { + String returnJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\",\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\",\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}, {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]},\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]},\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0,\"isreportenter\": 0,\"home_url\": \"\"}"; + final WxCpConfigStorage configStorage = new WxCpDefaultConfigImpl(); + when(wxService.getWxCpConfigStorage()).thenReturn(configStorage); + when(wxService.get(String.format(configStorage.getApiUrl(WxCpApiPathConsts.Agent.AGENT_GET), 9), null)).thenReturn(returnJson); + when(wxService.getAgentService()).thenReturn(new WxCpAgentServiceImpl(wxService)); + + WxCpAgentService wxAgentService = this.wxService.getAgentService(); + WxCpAgent wxCpAgent = wxAgentService.get(9); + + assertEquals(9, wxCpAgent.getAgentId().intValue()); + + assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray()); + + assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagIds().toArray()); + + } + + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java new file mode 100644 index 0000000000..0056b88f77 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java @@ -0,0 +1,158 @@ +package me.chanjar.weixin.cp.api.impl; + +import java.util.Arrays; + +import org.testng.*; +import org.testng.annotations.*; + +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.constant.WxCpConsts.AppChatMsgType; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; +import me.chanjar.weixin.cp.bean.WxCpChat; +import me.chanjar.weixin.cp.bean.article.MpnewsArticle; +import me.chanjar.weixin.cp.bean.article.NewArticle; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试群聊服务 + * + * @author gaigeshen + */ +@Guice(modules = ApiTestModule.class) +public class WxCpChatServiceImplTest { + private String chatId; + private String userId; + + @Inject + private WxCpService cpService; + + @BeforeTest + public void init() { + this.chatId = "mychatid"; + this.userId = ((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.cpService.getWxCpConfigStorage()).getUserId(); + } + + @Test + public void testCreate() throws Exception { + final String result = cpService.getChatService().create("测试群聊", userId, + Arrays.asList(userId, userId), chatId); + assertThat(result).isNotEmpty(); + assertThat(result).isEqualTo(chatId); + } + + @Test + public void testGet() throws Exception { + WxCpChat chat = this.cpService.getChatService().get(chatId); + System.out.println(chat); + Assert.assertEquals(chat.getName(), "测试群聊"); + } + + @Test + public void testUpdate() throws Exception { + this.cpService.getChatService().update(chatId, "", "", Arrays.asList("ZhengWuYao"), null); + WxCpChat chat = this.cpService.getChatService().get(chatId); + System.out.println(chat); + Assert.assertEquals(chat.getUsers().size(), 3); + } + + @DataProvider + public Object[][] messages() { + return new Object[][]{ + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.TEXT) + .chatId(chatId) + .content("你的快递已到\n请携带工卡前往邮件中心领取") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.IMAGE) + .chatId(chatId) + .mediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.VOICE) + .chatId(chatId) + .mediaId("3X5t6HkdN1hUgB7OzrdRnc8v0yI0CqlAxFxnCkS3msTnTLanpYrV4esLv4foZVnlf") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.VIDEO) + .chatId(chatId) + .mediaId("3otWyy_acbID8fyltmCOW5hGVD8oa0_p0za5jhukxKTUDoGT71lqTvtQAWoycXpQf") + .title("aaaa") + .description("ddddd") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.FILE) + .chatId(chatId) + .mediaId("34AyVyDdndVhB4Z2tT-_FYKZ7Xqrr47LPC11GHH4oy7o") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.TEXTCARD) + .chatId(chatId) + .btnTxt("更多") + .title("领奖通知") + .url("https://zhidao.baidu.com/question/2073647112026042748.html") + .description("
    2016年9月26日
    恭喜你抽中iPhone 7一台,领奖码:520258
    请于2016年10月10日前联系行 政同事领取
    ") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.NEWS) + .chatId(chatId) + .articles(Lists.newArrayList(NewArticle.builder() + .title("领奖通知") + .url("https://zhidao.baidu.com/question/2073647112026042748.html") + .description("今年中秋节公司有豪礼相送") + .picUrl("http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png") + .build() + )) + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.MPNEWS) + .chatId(chatId) + .mpnewsArticles(Lists.newArrayList(MpnewsArticle.newBuilder() + .title("地球一小时") + .thumbMediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM") + .author("Author") + .contentSourceUrl("https://work.weixin.qq.com") + .content("3月24日20:30-21:30 \n办公区将关闭照明一小时,请各部门同事相互转告") + .digest("3月24日20:30-21:30 \n办公区将关闭照明一小时") + .build() + )) + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.MARKDOWN) + .chatId(chatId) + .content("您的会议室已经预定,稍后会同步到`邮箱` \n" + + " >**事项详情** \n" + + " >事 项:开会 \n" + + " >组织者:@miglioguan \n" + + " >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" + + " > \n" + + " >会议室:广州TIT 1楼 301 \n" + + " >日 期:2018年5月18日 \n" + + " >时 间:上午9:00-11:00 \n" + + " > \n" + + " >请准时参加会议。 \n" + + " > \n" + + " >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)") + .build() + }, + }; + } + + @Test(dataProvider = "messages") + public void testSendMsg(WxCpAppChatMessage message) throws WxErrorException { + this.cpService.getChatService().sendMsg(message); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java new file mode 100644 index 0000000000..57957d3fb6 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.cp.api.impl; + +import java.util.List; + +import org.testng.annotations.*; + +import com.google.inject.Inject; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpDepart; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +@Guice(modules = ApiTestModule.class) +public class WxCpDepartmentServiceImplTest { + @Inject + private WxCpService wxCpService; + + private WxCpDepart depart; + + @Test + public void testCreate() throws Exception { + WxCpDepart cpDepart = new WxCpDepart(); + cpDepart.setName("子部门" + System.currentTimeMillis()); + cpDepart.setParentId(1L); + cpDepart.setOrder(1L); + Long departId = this.wxCpService.getDepartmentService().create(cpDepart); + System.out.println(departId); + } + + @DataProvider + public Object[][] departIds(){ + return new Object[][]{ + {null}, + {1}, + {5} + }; + } + + @Test(dataProvider = "departIds") + public void testList(Long id) throws Exception { + System.out.println("=================获取部门"); + List departList = this.wxCpService.getDepartmentService().list(id); + assertThat(departList).isNotEmpty(); + for (WxCpDepart g : departList) { + this.depart = g; + System.out.println(this.depart.getId() + ":" + this.depart.getName()); + assertThat(g.getName()).isNotBlank(); + } + } + + @Test(dependsOnMethods = {"testList", "testCreate"}) + public void testUpdate() throws Exception { + System.out.println("=================更新部门"); + this.depart.setName("子部门改名" + System.currentTimeMillis()); + this.wxCpService.getDepartmentService().update(this.depart); + } + + @Test(dependsOnMethods = "testUpdate") + public void testDelete() throws Exception { + System.out.println("=================删除部门"); + System.out.println(this.depart.getId() + ":" + this.depart.getName()); + this.wxCpService.getDepartmentService().delete(this.depart.getId()); + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java new file mode 100644 index 0000000000..9a0fbdbd3d --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java @@ -0,0 +1,220 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.bean.external.*; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.testng.Assert.assertNotNull; + +@Guice(modules = ApiTestModule.class) +public class WxCpExternalContactServiceImplTest { + @Inject + private WxCpService wxCpService; + @Inject + protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; + private final String userId = "someone" + System.currentTimeMillis(); + + @Test + public void testGetExternalContact() throws WxErrorException { + String externalUserId = this.configStorage.getExternalUserId(); + WxCpUserExternalContactInfo result = this.wxCpService.getExternalContactService().getExternalContact(externalUserId); + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testAddContactWay() throws WxErrorException { + + final String concatUserId = "HuangXiaoMing"; + + WxCpContactWayInfo.ContactWay wayInfo = new WxCpContactWayInfo.ContactWay(); + wayInfo.setType(WxCpContactWayInfo.TYPE.SINGLE); + wayInfo.setScene(WxCpContactWayInfo.SCENE.QRCODE); + wayInfo.setUsers(Lists.newArrayList(concatUserId)); + wayInfo.setRemark("CreateDate:" + DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT.format(new Date())); + + WxCpContactWayInfo info = new WxCpContactWayInfo(); + info.setContactWay(wayInfo); + this.wxCpService.getExternalContactService().addContactWay(info); + } + + @Test + public void testGetContactWay() throws WxErrorException { + final String configId = "39fea3d93e30faaa8c7a9edd4cfe4d08"; + WxCpContactWayInfo contactWayInfo = this.wxCpService.getExternalContactService().getContactWay(configId); + System.out.println(contactWayInfo.toJson()); + assertNotNull(contactWayInfo); + } + + @Test + public void testUpdateContactWay() throws WxErrorException { + final String configId = "2d7a68c657663afbd1d90db19a4b5ee9"; + final String concatUserId = "符合要求的userId"; + WxCpContactWayInfo.ContactWay wayInfo = new WxCpContactWayInfo.ContactWay(); + wayInfo.setConfigId(configId); + wayInfo.setUsers(Lists.newArrayList(concatUserId)); + wayInfo.setRemark("CreateDate:" + DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT.format(new Date())); + WxCpContactWayInfo info = new WxCpContactWayInfo(); + info.setContactWay(wayInfo); + WxCpBaseResp resp = this.wxCpService.getExternalContactService().updateContactWay(info); + System.out.println(resp); + assertNotNull(resp); + } + + @Test + public void testDelContactWay() throws WxErrorException { + final String configId = "2d7a68c657663afbd1d90db19a4b5ee9"; + WxCpBaseResp resp = this.wxCpService.getExternalContactService().deleteContactWay(configId); + System.out.println(resp); + assertNotNull(resp); + } + + @Test + public void testCloseTempChat() throws WxErrorException { + final String externalUserId = "externalUserId"; + WxCpBaseResp resp = this.wxCpService.getExternalContactService().closeTempChat(userId, externalUserId); + System.out.println(resp); + } + + @Test + public void testListExternalContacts() throws WxErrorException { + String userId = this.configStorage.getUserId(); + List ret = this.wxCpService.getExternalContactService().listExternalContacts(userId); + System.out.println(ret); + assertNotNull(ret); + } + + @Test + public void testListExternalWithPermission() throws WxErrorException { + List ret = this.wxCpService.getExternalContactService().listFollowers(); + System.out.println(ret); + assertNotNull(ret); + } + + @Test + public void testGetContactDetail() throws WxErrorException { + String externalUserId = this.configStorage.getExternalUserId(); + WxCpUserExternalContactInfo result = this.wxCpService.getExternalContactService().getContactDetail(externalUserId); + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testGetCorpTagList() throws WxErrorException { + String[] tag = {}; + WxCpUserExternalTagGroupList result = this.wxCpService.getExternalContactService().getCorpTagList(null); + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testAddCorpTag() throws WxErrorException { + + List list = new ArrayList<>(); + WxCpUserExternalTagGroupInfo.Tag tag = new WxCpUserExternalTagGroupInfo.Tag(); + tag.setName("测试标签20"); + tag.setOrder(1); + list.add(tag); + + WxCpUserExternalTagGroupInfo tagGroupInfo = new WxCpUserExternalTagGroupInfo(); + WxCpUserExternalTagGroupInfo.TagGroup tagGroup = new WxCpUserExternalTagGroupInfo.TagGroup(); + tagGroup.setGroupName("其他"); + tagGroup.setOrder(1); + tagGroup.setTag(list); + tagGroupInfo.setTagGroup(tagGroup); + + WxCpUserExternalTagGroupInfo result = this.wxCpService.getExternalContactService().addCorpTag(tagGroupInfo); + + System.out.println(result.toJson()); + assertNotNull(result); + } + + @Test + public void testEditCorpTag() throws WxErrorException { + + WxCpBaseResp result = this.wxCpService.getExternalContactService().editCorpTag("et2omCCwAA6PtGsfeEOQMENl3Ub1FA6A", "未知6", 2); + + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testDelCorpTag() throws WxErrorException { + + String[] tagId = {}; + String[] groupId = {"et2omCCwAAM3WzL00QpK9xARab3HGkAg"}; + + WxCpBaseResp result = this.wxCpService.getExternalContactService().delCorpTag(tagId, groupId); + + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testMarkTag() throws WxErrorException { + + String userid = "HuangXiaoMing"; + String externalUserid = "wo2omCCwAAzR0Rt1omz-90o_XJkPGXIQ"; + String[] addTag = {"et2omCCwAAzdcSK-RV80YS9sbpCXlNlQ"}; + String[] removeTag = {}; + + WxCpBaseResp result = this.wxCpService.getExternalContactService().markTag(userid, externalUserid, addTag, removeTag); + + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testDeleteContactWay() { + } + + @Test + public void testListFollowers() { + } + + @Test + public void testListUnassignedList() { + } + + @Test + public void testTransferExternalContact() { + } + + @Test + public void testListGroupChat() { + } + + @Test + public void testGetGroupChat() { + } + + @Test + public void testGetUserBehaviorStatistic() { + } + + @Test + public void testGetGroupChatStatistic() { + } + + @Test + public void testAddMsgTemplate() { + } + + @Test + public void testSendWelcomeMsg() throws WxErrorException { + this.wxCpService.getExternalContactService().sendWelcomeMsg(WxCpWelcomeMsg.builder() + .welcomeCode("abc") + .build()); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImplTest.java new file mode 100644 index 0000000000..b697efd53b --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpGroupRobotServiceImplTest.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpGroupRobotService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.article.NewArticle; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.InputStream; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.testng.Assert.*; + +/** + * 微信群机器人消息发送api 单元测试 + * + * @author yr + * @date 2020-08-20 + */ +@Slf4j +@Guice(modules = ApiTestModule.class) +public class WxCpGroupRobotServiceImplTest { + @Inject + protected WxCpService wxService; + + private WxCpGroupRobotService robotService; + + @BeforeTest + public void setup() { + robotService = wxService.getGroupRobotService(); + } + + @Test + public void testSendText() throws WxErrorException { + robotService.sendText("Hello World", null, null); + } + + @Test + public void testSendMarkDown() throws WxErrorException { + String content = "实时新增用户反馈132例,请相关同事注意。\n" + + ">类型:用户反馈 \n" + + ">普通用户反馈:117例 \n" + + ">VIP用户反馈:15例"; + robotService.sendMarkDown(content); + } + + @Test + public void testSendImage() throws WxErrorException { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream("mm.jpeg"); + assert inputStream != null; + String base64 = FileUtils.imageToBase64ByStream(inputStream); + String md5 = "1cb2e787063d66e24f5f89e7fc267a4d"; + robotService.sendImage(base64, md5); + } + + @Test + public void testSendNews() throws WxErrorException { + NewArticle article = new NewArticle("图文消息测试","hello world","http://www.baidu.com","http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png"); + robotService.sendNews(Stream.of(article).collect(Collectors.toList())); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java new file mode 100644 index 0000000000..70873c49cf --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java @@ -0,0 +1,94 @@ +package me.chanjar.weixin.cp.api.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.testng.annotations.*; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.TestConstants; +import me.chanjar.weixin.cp.api.WxCpService; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.*; + +/** + * Created by Binary Wang on 2017-6-25. + * + * @author Binary Wang + */ +@Guice(modules = ApiTestModule.class) +public class WxCpMediaServiceImplTest { + @Inject + private WxCpService wxService; + + private List mediaIds = new ArrayList<>(); + + @DataProvider + public Object[][] mediaData() { + return new Object[][]{ + new Object[]{WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, + //new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"}, + // {"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"} + new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_AMR, "mm.amr"}, + new Object[]{WxConsts.MediaFileType.VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, + new Object[]{WxConsts.MediaFileType.FILE, TestConstants.FILE_JPG, "mm.jpeg"} + }; + } + + @Test(dataProvider = "mediaData") + public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)) { + WxMediaUploadResult res = this.wxService.getMediaService().upload(mediaType, fileType, inputStream); + assertThat(res).isNotNull(); + assertThat(res.getType()).isNotEmpty(); + assertThat(res.getCreatedAt()).isGreaterThan(0); + assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null); + + if (res.getMediaId() != null) { + this.mediaIds.add(res.getMediaId()); + } + if (res.getThumbMediaId() != null) { + this.mediaIds.add(res.getThumbMediaId()); + } + } + } + + @DataProvider + public Object[][] downloadMedia() { + Object[][] params = new Object[this.mediaIds.size()][]; + for (int i = 0; i < this.mediaIds.size(); i++) { + params[i] = new Object[]{this.mediaIds.get(i)}; + } + return params; + } + + @Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadMedia") + public void testDownload(String mediaId) throws WxErrorException { + File file = this.wxService.getMediaService().download(mediaId); + assertThat(file).isNotNull(); + System.out.println(file); + } + + @Test + public void testUploadImg() throws WxErrorException { + URL url = ClassLoader.getSystemResource("mm.jpeg"); + String res = this.wxService.getMediaService().uploadImg(new File(url.getFile())); + assertThat(res).isNotEmpty(); + } + + @Test + public void testGetJssdkFile() throws WxErrorException { + File file = this.wxService.getMediaService().getJssdkFile("...."); + assertThat(file).isNotNull(); + System.out.println(file); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImplTest.java similarity index 50% rename from weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java rename to weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImplTest.java index c837e51e1d..b9dbbd3aa1 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImplTest.java @@ -1,52 +1,37 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.api.impl; import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.bean.menu.WxMenuButton; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import org.testng.annotations.*; + +import static org.testng.Assert.*; /** - * 测试菜单 + *
      *
    - * @author Daniel Qian
    + * Created by Binary Wang on 2017-6-25.
    + * @author Binary Wang
    + * 
    */ -@Test(groups = "menuAPI", dependsOnGroups = "baseAPI") @Guice(modules = ApiTestModule.class) -public class WxMenuAPITest { - +public class WxCpMenuServiceImplTest { @Inject - protected WxCpServiceImpl wxService; - - @Test(dataProvider = "menu") - public void testCreateMenu(WxMenu wxMenu) throws WxErrorException { - this.wxService.menuCreate(wxMenu); - } + protected WxCpService wxService; - @Test(dependsOnMethods = {"testCreateMenu"}) - public void testGetMenu() throws WxErrorException { - Assert.assertNotNull(this.wxService.menuGet()); - } - - @Test(dependsOnMethods = {"testGetMenu"}) - public void testDeleteMenu() throws WxErrorException { - this.wxService.menuDelete(); - } - - @DataProvider(name = "menu") - public Object[][] getMenu() { + @DataProvider + public Object[][] menuData() { WxMenu menu = new WxMenu(); WxMenuButton button1 = new WxMenuButton(); - button1.setType(WxConsts.BUTTON_CLICK); + button1.setType(WxConsts.MenuButtonType.CLICK); button1.setName("今日歌曲"); button1.setKey("V1001_TODAY_MUSIC"); WxMenuButton button2 = new WxMenuButton(); - button2.setType(WxConsts.BUTTON_CLICK); + button2.setType(WxConsts.MenuButtonType.CLICK); button2.setName("歌手简介"); button2.setKey("V1001_TODAY_SINGER"); @@ -58,17 +43,17 @@ public Object[][] getMenu() { menu.getButtons().add(button3); WxMenuButton button31 = new WxMenuButton(); - button31.setType(WxConsts.BUTTON_VIEW); + button31.setType(WxConsts.MenuButtonType.VIEW); button31.setName("搜索"); button31.setUrl("http://www.soso.com/"); WxMenuButton button32 = new WxMenuButton(); - button32.setType(WxConsts.BUTTON_VIEW); + button32.setType(WxConsts.MenuButtonType.VIEW); button32.setName("视频"); button32.setUrl("http://v.qq.com/"); WxMenuButton button33 = new WxMenuButton(); - button33.setType(WxConsts.BUTTON_CLICK); + button33.setType(WxConsts.MenuButtonType.CLICK); button33.setName("赞一下我们"); button33.setKey("V1001_GOOD"); @@ -77,12 +62,28 @@ public Object[][] getMenu() { button3.getSubButtons().add(button33); return new Object[][]{ - new Object[]{ - menu - } + new Object[]{ + menu + } }; } + @Test(dataProvider = "menuData") + public void testCreate(WxMenu wxMenu) throws Exception { + this.wxService.getMenuService().create(wxMenu); + } + + @Test(dependsOnMethods = "testCreate") + public void testGet() throws Exception { + WxMenu menu = this.wxService.getMenuService().get(); + assertNotNull(menu); + System.out.println(menu.toJson()); + } + + @Test(dependsOnMethods = {"testGet", "testCreate"}) + public void testDelete() throws Exception { + this.wxService.getMenuService().delete(); + } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImplTest.java new file mode 100644 index 0000000000..4df8756334 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImplTest.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; +import me.chanjar.weixin.cp.bean.WxCpUserDetail; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *  Created by BinaryWang on 2018/4/22.
    + * 
    + * + * @author Binary Wang + */ +@Guice(modules = ApiTestModule.class) +public class WxCpOAuth2ServiceImplTest { + @Inject + private WxCpService wxService; + + @Test + public void testGetUserDetail() throws WxErrorException { + WxCpUserDetail userDetail = this.wxService.getOauth2Service().getUserDetail("b"); + System.out.println(userDetail); + } + + @Test + public void testGetUserInfo() throws WxErrorException { + final WxCpOauth2UserInfo result = this.wxService.getOauth2Service().getUserInfo("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBuildAuthorizationUrl() { + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java new file mode 100644 index 0000000000..6f3a76b0ab --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java @@ -0,0 +1,104 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.Gson; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.oa.*; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; +import org.testng.collections.Lists; + +import java.text.ParseException; +import java.util.Date; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 企业微信 OA数据接口 测试用例 + * + * @author Element + */ + +@Guice(modules = ApiTestModule.class) +public class WxCpOaServiceImplTest { + + @Inject + protected WxCpService wxService; + + @Inject + protected Gson gson; + + @Test + public void testGetCheckinData() throws ParseException, WxErrorException { + Date startTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-04-11"); + Date endTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-05-10"); + + List results = wxService.getOAService() + .getCheckinData(1, startTime, endTime, Lists.newArrayList("binary")); + + assertThat(results).isNotNull(); + + System.out.println("results "); + System.out.println(gson.toJson(results)); + + } + + @Test + public void testGetCheckinOption() throws WxErrorException { + + Date now = new Date(); + List results = wxService.getOAService().getCheckinOption(now, Lists.newArrayList("binary")); + assertThat(results).isNotNull(); + System.out.println("results "); + System.out.println(gson.toJson(results)); + } + + @Test + public void testGetApprovalInfo() throws WxErrorException, ParseException { + Date startTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-12-01"); + Date endTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-12-31"); + WxCpApprovalInfo result = wxService.getOAService().getApprovalInfo(startTime, endTime); + + assertThat(result).isNotNull(); + + System.out.println("result "); + System.out.println(gson.toJson(result)); + } + + @Test + public void testGetApprovalDetail() throws WxErrorException { + String spNo = "201912020001"; + WxCpApprovalDetailResult result = wxService.getOAService().getApprovalDetail(spNo); + + assertThat(result).isNotNull(); + + System.out.println("result "); + System.out.println(gson.toJson(result)); + } + + @Test + public void testGetTemplateDetail() throws WxErrorException { + String templateId = "3TkZjxugodbqpEMk9j7X6h6zKqYkc7MxQrrFmT7H"; + WxCpTemplateResult result = wxService.getOAService().getTemplateDetail(templateId); + assertThat(result).isNotNull(); + System.out.println("result "); + System.out.println(gson.toJson(result)); + } + + @Test + public void testApply() throws WxErrorException { + this.wxService.getOAService().apply(new WxCpOaApplyEventRequest().setCreatorUserId("123")); + } + + @Test + public void testGetApprovalData() { + } + + @Test + public void testGetDialRecord() { + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java new file mode 100644 index 0000000000..7db4564f62 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java @@ -0,0 +1,102 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.common.base.Splitter; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.WxCpTagService; +import me.chanjar.weixin.cp.bean.WxCpTag; +import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult; +import me.chanjar.weixin.cp.bean.WxCpTagGetResult; +import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; + +/** + *
    + * Created by Binary Wang on 2017-6-25.
    + * 
    + * + * @author Binary Wang + */ +@Guice(modules = ApiTestModule.class) +public class WxCpTagServiceImplTest { + @Inject + protected WxCpService wxService; + + @Inject + protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; + + private String tagId; + + @Test + public void testCreate() throws Exception { + this.tagId = this.wxService.getTagService().create("测试标签" + System.currentTimeMillis(), null); + System.out.println(this.tagId); + } + + @Test(dependsOnMethods = "testCreate") + public void testUpdate() throws Exception { + this.wxService.getTagService().update(this.tagId, "测试标签-改名" + System.currentTimeMillis()); + } + + @Test(dependsOnMethods = {"testUpdate", "testCreate"}) + public void testListAll() throws Exception { + List tags = this.wxService.getTagService().listAll(); + assertNotEquals(tags.size(), 0); + } + + @Test(dependsOnMethods = {"testListAll", "testUpdate", "testCreate"}) + public void testAddUsers2Tag() throws Exception { + List userIds = Splitter.on("|").splitToList(this.configStorage.getUserId()); + WxCpTagAddOrRemoveUsersResult result = this.wxService.getTagService().addUsers2Tag(this.tagId, userIds, null); + assertEquals(result.getErrCode(), Integer.valueOf(0)); + } + + @Test(dependsOnMethods = {"testAddUsers2Tag", "testListAll", "testUpdate", "testCreate"}) + public void testListUsersByTagId() throws Exception { + List users = this.wxService.getTagService().listUsersByTagId(this.tagId); + assertNotEquals(users.size(), 0); + } + + @Test(dependsOnMethods = {"testListUsersByTagId", "testAddUsers2Tag", "testListAll", "testUpdate", "testCreate"}) + public void testRemoveUsersFromTag() throws Exception { + List userIds = Splitter.on("|").splitToList(this.configStorage.getUserId()); + WxCpTagAddOrRemoveUsersResult result = this.wxService.getTagService().removeUsersFromTag(this.tagId, userIds, null); + assertEquals(result.getErrCode(), Integer.valueOf(0)); + } + + @Test(dependsOnMethods = {"testRemoveUsersFromTag", "testListUsersByTagId", "testAddUsers2Tag", "testListAll", "testUpdate", "testCreate"}) + public void testDelete() throws Exception { + this.wxService.getTagService().delete(this.tagId); + } + + @Test + public void testGet() throws WxErrorException { + String apiResultJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"userlist\": [{\"userid\": \"0124035\",\"name\": \"王五\"},{\"userid\": \"0114035\",\"name\": \"梦雪\"}],\"partylist\": [9576,9567,9566],\"tagname\": \"测试标签-001\"}"; + WxCpService wxService = mock(WxCpService.class); + when(wxService.get(String.format(wxService.getWxCpConfigStorage().getApiUrl(WxCpApiPathConsts.Tag.TAG_GET), 150), null)).thenReturn(apiResultJson); + when(wxService.getTagService()).thenReturn(new WxCpTagServiceImpl(wxService)); + + WxCpTagService wxCpTagService = wxService.getTagService(); + + WxCpTagGetResult wxCpTagGetResult = wxCpTagService.get(String.valueOf(150)); + + assertEquals(0, wxCpTagGetResult.getErrcode().intValue()); + + assertEquals(2, wxCpTagGetResult.getUserlist().size()); + assertEquals(3, wxCpTagGetResult.getPartylist().size()); + assertEquals("测试标签-001", wxCpTagGetResult.getTagname()); + + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java new file mode 100644 index 0000000000..be387548b9 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpMessage; +import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.Arrays; + +import static org.testng.Assert.assertNotNull; + +/** + * 测试任务卡片服务 + * + * @author Jeff + * @date 2019-05-16 + */ +@Guice(modules = ApiTestModule.class) +public class WxCpTaskCardServiceImplTest { + + @Inject + private WxCpService wxCpService; + + @Test + public void testSendTaskCard() throws WxErrorException { + TaskCardButton btn1 = TaskCardButton.builder() + .key("key1") + .name("同意") + .replaceName("已同意") + .bold(true) + .build(); + TaskCardButton btn2 = TaskCardButton.builder() + .key("key2") + .name("拒绝") + .replaceName("已拒绝") + .color("red") + .build(); + WxCpMessage message = WxCpMessage.TASKCARD() + .toUser("jeff|mr.t") + .title("有一个待审批的请求") + .description("申请:购买图书\n金额:100 元") + .taskId("task_1") + .url("http://www.qq.com") + .buttons(Arrays.asList(btn1, btn2)) + .build(); + + WxCpMessageSendResult messageSendResult = this.wxCpService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } + + @Test + public void testUpdate() throws Exception { + wxCpService.getTaskCardService().update(Arrays.asList("jeff", "mr.t"), "task_1", "key1"); + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java new file mode 100644 index 0000000000..9c4448830e --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java @@ -0,0 +1,124 @@ +package me.chanjar.weixin.cp.api.impl; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.testng.annotations.*; + +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.Gender; +import me.chanjar.weixin.cp.bean.WxCpInviteResult; +import me.chanjar.weixin.cp.bean.WxCpUser; + +import static org.testng.Assert.*; + +/** + *
    + *  Created by BinaryWang on 2017/6/24.
    + * 
    + * + * @author Binary Wang + */ +@Guice(modules = ApiTestModule.class) +public class WxCpUserServiceImplTest { + @Inject + private WxCpService wxCpService; + private String userId = "someone" + System.currentTimeMillis(); + + @Test + public void testAuthenticate() throws Exception { + this.wxCpService.getUserService().authenticate("abc"); + } + + @Test + public void testCreate() throws Exception { + WxCpUser user = new WxCpUser(); + user.setUserId(userId); + user.setName("Some Woman"); + user.setDepartIds(new Long[]{2L}); + user.setEmail("none@none.com"); + user.setGender(Gender.FEMALE); + user.setMobile("13560084979"); + user.setPosition("woman"); + user.setTelephone("3300393"); + user.addExtAttr("爱好", "table"); + this.wxCpService.getUserService().create(user); + } + + @Test(dependsOnMethods = "testCreate") + public void testUpdate() throws Exception { + WxCpUser user = new WxCpUser(); + user.setUserId(userId); + user.setName("Some Woman"); + user.addExtAttr("爱好", "table2"); + this.wxCpService.getUserService().update(user); + } + + @Test(dependsOnMethods = {"testCreate", "testUpdate"}) + public void testDelete() throws Exception { + this.wxCpService.getUserService().delete(userId); + } + + @Test(dependsOnMethods = "testUpdate") + public void testGetById() throws Exception { + WxCpUser user = this.wxCpService.getUserService().getById(userId); + assertNotNull(user); + } + + @Test + public void testListByDepartment() throws Exception { + List users = this.wxCpService.getUserService().listByDepartment(2L, true, 0); + assertNotEquals(users.size(), 0); + for (WxCpUser user : users) { + System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE)); + } + } + + @Test + public void testListSimpleByDepartment() throws Exception { + List users = this.wxCpService.getUserService().listSimpleByDepartment(1L, true, 0); + assertNotEquals(users.size(), 0); + for (WxCpUser user : users) { + System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE)); + } + } + + @Test + public void testInvite() throws Exception { + WxCpInviteResult result = this.wxCpService.getUserService().invite( + Lists.newArrayList(userId), null, null); + System.out.println(result); + } + + @Test + public void testUserId2Openid() throws Exception { + Map result = this.wxCpService.getUserService().userId2Openid(userId, null); + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testOpenid2UserId() throws Exception { + String result = this.wxCpService.getUserService().openid2UserId(userId); + System.out.println(result); + assertNotNull(result); + } + + + @Test + public void testGetUserId() throws WxErrorException { + String result = this.wxCpService.getUserService().getUserId("xxx"); + System.out.println(result); + assertNotNull(result); + } + + @Test + public void testGetExternalContact() { + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpAgentTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpAgentTest.java new file mode 100644 index 0000000000..9f187e43aa --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpAgentTest.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.cp.bean; + +import org.testng.*; +import org.testng.annotations.*; + +/** + * Created by huansinho on 2018/4/13. + */ +@Test +public class WxCpAgentTest { + + public void testDeserialize() { + String json = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\"," + + "\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\"," + + "\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}," + + " {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]}," + + "\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]}," + + "\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0," + + "\"isreportenter\": 0,\"home_url\": \"\"}"; + + WxCpAgent wxCpAgent = WxCpAgent.fromJson(json); + + Assert.assertEquals(9, wxCpAgent.getAgentId().intValue()); + + Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray()); + + Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, + wxCpAgent.getAllowTags().getTagIds().toArray()); + + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java index ce75b06690..c54211758b 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java @@ -1,92 +1,56 @@ package me.chanjar.weixin.cp.bean; -import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.cp.bean.article.NewArticle; import me.chanjar.weixin.cp.bean.article.MpnewsArticle; -import org.testng.Assert; +import me.chanjar.weixin.cp.bean.article.NewArticle; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; @Test public class WxCpMessageTest { - public void testTextReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); - reply.setContent("sfsfdsdf"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); - } - public void testTextBuild() { WxCpMessage reply = WxCpMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"},\"safe\":\"0\"}"); } - public void testImageReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); - reply.setMediaId("MEDIA_ID"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + public void testTextCardBuild() { + WxCpMessage reply = WxCpMessage.TEXTCARD().toUser("OPENID") + .title("领奖通知") + .description("
    2016年9月26日
    恭喜你抽中iPhone 7一台," + + "领奖码:xxxx
    请于2016年10月10日前联系行政同事领取
    ") + .url("http://www.qq.com") + .btnTxt("更多") + .build(); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\"," + + "\"description\":\"
    2016年9月26日
    " + + "恭喜你抽中iPhone 7一台,领奖码:xxxx
    请于2016年10月10日前联系行政同事领取
    \"," + + "\"url\":\"http://www.qq.com\",\"btntxt\":\"更多\"},\"safe\":\"0\"}"); } public void testImageBuild() { WxCpMessage reply = WxCpMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); - } - - public void testVoiceReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_VOICE); - reply.setMediaId("MEDIA_ID"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"},\"safe\":\"0\"}"); } public void testVoiceBuild() { WxCpMessage reply = WxCpMessage.VOICE().toUser("OPENID").mediaId("MEDIA_ID").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); - } - - public void testVideoReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_VIDEO); - reply.setMediaId("MEDIA_ID"); - reply.setThumbMediaId("MEDIA_ID"); - reply.setTitle("TITLE"); - reply.setDescription("DESCRIPTION"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"},\"safe\":\"0\"}"); } public void testVideoBuild() { - WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); - } - - public void testNewsReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_NEWS); - - NewArticle article1 = new NewArticle(); - article1.setUrl("URL"); - article1.setPicUrl("PIC_URL"); - article1.setDescription("Is Really A Happy Day"); - article1.setTitle("Happy Day"); - reply.getArticles().add(article1); - - NewArticle article2 = new NewArticle(); - article2.setUrl("URL"); - article2.setPicUrl("PIC_URL"); - article2.setDescription("Is Really A Happy Day"); - article2.setTitle("Happy Day"); - reply.getArticles().add(article2); - - - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID") + .description("DESCRIPTION").build(); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\"," + + "\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"},\"safe\":\"0\"}"); } public void testNewsBuild() { @@ -104,7 +68,11 @@ public void testNewsBuild() { WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":" + + "[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}," + + "{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}," + + "\"safe\":\"0\"}"); } public void testMpnewsBuild_with_articles() { @@ -128,18 +96,48 @@ public void testMpnewsBuild_with_articles() { .thumbMediaId("thumb") .build(); - WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); + WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1, article2).build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\"," + - "\"mpnews\":{\"articles\":[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}," + - "{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}}"); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"articles\":" + + "[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," + + "\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}" + + ",{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," + + "\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}," + + "\"safe\":\"0\"}"); } public void testMpnewsBuild_with_media_id() { WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").mediaId("mmm").build(); - assertEquals(reply.toJson(), - "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"}}"); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"},\"safe\":\"0\"}"); + } + + public void testTaskCardBuilder() { + TaskCardButton button1 = TaskCardButton.builder() + .key("yes") + .name("批准") + .replaceName("已批准") + .color("blue") + .bold(true) + .build(); + TaskCardButton button2 = TaskCardButton.builder() + .key("yes") + .name("拒绝") + .replaceName("已拒绝") + .color("red") + .bold(false) + .build(); + WxCpMessage reply = WxCpMessage.TASKCARD().toUser("OPENID") + .title("任务卡片") + .description("有一条待处理任务") + .url("http://www.qq.com") + .taskId("task_123") + .buttons(Arrays.asList(button1, button2)) + .build(); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"taskcard\",\"taskcard\":{\"title\":\"任务卡片\",\"description\":\"有一条待处理任务\",\"url\":\"http://www.qq.com\",\"task_id\":\"task_123\",\"btn\":[{\"key\":\"yes\",\"name\":\"批准\",\"replace_name\":\"已批准\",\"color\":\"blue\",\"is_bold\":true},{\"key\":\"yes\",\"name\":\"拒绝\",\"replace_name\":\"已拒绝\",\"color\":\"red\",\"is_bold\":false}]}}"); } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackageTest.java new file mode 100644 index 0000000000..5a54bfd8af --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackageTest.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.cp.bean; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * . + * + * @author Binary Wang + * @date 2019-08-18 + */ +public class WxCpTpXmlPackageTest { + + @Test + public void testFromXml() { + WxCpTpXmlPackage result = WxCpTpXmlPackage.fromXml(" \n" + + " \n" + + " \n" + + " \n" + + "\n"); + assertThat(result).isNotNull(); + assertThat(result.getToUserName()).isEqualTo("toUser"); + assertThat(result.getAgentId()).isEqualTo("toAgentID"); + assertThat(result.getMsgEncrypt()).isEqualTo("msg_encrypt"); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfoTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfoTest.java new file mode 100644 index 0000000000..77b25f9198 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfoTest.java @@ -0,0 +1,130 @@ +package me.chanjar.weixin.cp.bean; + +import java.util.List; + +import me.chanjar.weixin.cp.bean.external.WxCpUserExternalContactInfo; +import org.testng.annotations.*; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/9/16.
    + * 
    + * + * @author Binary Wang + */ +public class WxCpUserExternalContactInfoTest { + + @Test + public void testFromJson() { + final String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"external_contact\": {\n" + + " \"external_userid\": \"woAJ2GCAAAXtWyujaWJHDDGi0mACH71w\",\n" + + " \"name\": \"李四\",\n" + + " \"position\": \"Mangaer\",\n" + + " \"avatar\": \"http://p.qlogo.cn/bizmail/IcsdgagqefergqerhewSdage/0\",\n" + + " \"corp_name\": \"腾讯\",\n" + + " \"corp_full_name\": \"腾讯科技有限公司\",\n" + + " \"type\": 2,\n" + + " \"gender\": 1,\n" + + " \"unionid\": \"ozynqsulJFCZ2z1aYeS8h-nuasdfR1\",\n" + + " \"external_profile\": {\n" + + " \"external_attr\": [\n" + + " {\n" + + " \"type\": 0,\n" + + " \"name\": \"文本名称\",\n" + + " \"text\": {\n" + + " \"value\": \"文本\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"type\": 1,\n" + + " \"name\": \"网页名称\",\n" + + " \"web\": {\n" + + " \"url\": \"http://www.test.com\",\n" + + " \"title\": \"标题\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"type\": 2,\n" + + " \"name\": \"测试app\",\n" + + " \"miniprogram\": {\n" + + " \"appid\": \"wx8bd80126147df384\",\n" + + " \"pagepath\": \"/index\",\n" + + " \"title\": \"my miniprogram\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " },\n" + + " \"follow_user\": [\n" + + " {\n" + + " \"userid\": \"rocky\",\n" + + " \"remark\": \"李部长\",\n" + + " \"description\": \"对接采购事物\",\n" + + " \"createtime\": 1525779812\n" + + " },\n" + + " {\n" + + " \"userid\": \"tommy\",\n" + + " \"remark\": \"李总\",\n" + + " \"description\": \"采购问题咨询\",\n" + + " \"createtime\": 1525881637\n" + + " }\n" + + " ]\n" + + "}"; + + final WxCpUserExternalContactInfo contactInfo = WxCpUserExternalContactInfo.fromJson(json); + assertThat(contactInfo).isNotNull(); + assertThat(contactInfo.getExternalContact()).isNotNull(); + + assertThat(contactInfo.getExternalContact().getExternalUserId()).isEqualTo("woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); + assertThat(contactInfo.getExternalContact().getPosition()).isEqualTo("Mangaer"); + assertThat(contactInfo.getExternalContact().getAvatar()).isEqualTo("http://p.qlogo.cn/bizmail/IcsdgagqefergqerhewSdage/0"); + assertThat(contactInfo.getExternalContact().getCorpName()).isEqualTo("腾讯"); + assertThat(contactInfo.getExternalContact().getCorpFullName()).isEqualTo("腾讯科技有限公司"); + assertThat(contactInfo.getExternalContact().getType()).isEqualTo(2); + assertThat(contactInfo.getExternalContact().getGender()).isEqualTo(1); + assertThat(contactInfo.getExternalContact().getUnionId()).isEqualTo("ozynqsulJFCZ2z1aYeS8h-nuasdfR1"); + assertThat(contactInfo.getExternalContact().getName()).isEqualTo("李四"); + + assertThat(contactInfo.getExternalContact().getExternalProfile()).isNotNull(); + + final List externalAttrs = contactInfo.getExternalContact().getExternalProfile().getExternalAttrs(); + assertThat(externalAttrs).isNotEmpty(); + + final WxCpUserExternalContactInfo.ExternalAttribute externalAttr1 = externalAttrs.get(0); + assertThat(externalAttr1.getType()).isEqualTo(0); + assertThat(externalAttr1.getName()).isEqualTo("文本名称"); + assertThat(externalAttr1.getText().getValue()).isEqualTo("文本"); + + final WxCpUserExternalContactInfo.ExternalAttribute externalAttr2 = externalAttrs.get(1); + assertThat(externalAttr2.getType()).isEqualTo(1); + assertThat(externalAttr2.getName()).isEqualTo("网页名称"); + assertThat(externalAttr2.getWeb().getUrl()).isEqualTo("http://www.test.com"); + assertThat(externalAttr2.getWeb().getTitle()).isEqualTo("标题"); + + final WxCpUserExternalContactInfo.ExternalAttribute externalAttr3 = externalAttrs.get(2); + assertThat(externalAttr3.getType()).isEqualTo(2); + assertThat(externalAttr3.getName()).isEqualTo("测试app"); + assertThat(externalAttr3.getMiniProgram().getAppid()).isEqualTo("wx8bd80126147df384"); + assertThat(externalAttr3.getMiniProgram().getPagePath()).isEqualTo("/index"); + assertThat(externalAttr3.getMiniProgram().getTitle()).isEqualTo("my miniprogram"); + + + List followedUsers = contactInfo.getFollowedUsers(); + assertThat(followedUsers).isNotEmpty(); + assertThat(followedUsers.get(0).getUserId()).isEqualTo("rocky"); + assertThat(followedUsers.get(0).getRemark()).isEqualTo("李部长"); + assertThat(followedUsers.get(0).getDescription()).isEqualTo("对接采购事物"); + assertThat(followedUsers.get(0).getCreateTime()).isEqualTo(1525779812); + + assertThat(followedUsers.get(1).getUserId()).isEqualTo("tommy"); + assertThat(followedUsers.get(1).getRemark()).isEqualTo("李总"); + assertThat(followedUsers.get(1).getDescription()).isEqualTo("采购问题咨询"); + assertThat(followedUsers.get(1).getCreateTime()).isEqualTo(1525881637); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java index 270fa1d349..0a9f17a626 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java @@ -1,91 +1,275 @@ package me.chanjar.weixin.cp.bean; import me.chanjar.weixin.common.api.WxConsts; -import org.testng.Assert; +import me.chanjar.weixin.cp.constant.WxCpConsts; +import me.chanjar.weixin.cp.util.xml.XStreamTransformer; import org.testng.annotations.Test; +import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.TASKCARD_CLICK; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + @Test public class WxCpXmlMessageTest { public void testFromXml() { String xml = "" - + "" - + " " - + "1348831860" - + "" - + "" - + "1234567890123456" - + "" - + "" - + "" - + "" - + "23.134521" - + "113.358803" - + "20" - + "" - + "" - + "" - + "<![CDATA[公众平台官网链接]]>" - + "" - + "" - + "" - + "23.137466" - + "113.352425" - + "119.385040" - + "" - + " " - + " " - + "" - + "" - + " 1\n" - + " " - + " " - + " " - + " " - + " " - + "" - + "" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "" - + ""; + + "" + + " " + + "1348831860" + + "" + + "" + + "1234567890123456" + + "" + + "" + + "" + + "" + + "23.134521" + + "113.358803" + + "20" + + "" + + "" + + "" + + "<![CDATA[公众平台官网链接]]>" + + "" + + "" + + "" + + "23.137466" + + "113.352425" + + "119.385040" + + "" + + " " + + " " + + "" + + "" + + " 1\n" + + " " + + " " + + " " + + " " + + " " + + "" + + "" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Long(1348831860)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getFormat(), "Format"); + assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); + assertEquals(wxMessage.getLocationX().doubleValue(), 23.134521d); + assertEquals(wxMessage.getLocationY().doubleValue(), 113.358803d); + assertEquals(wxMessage.getScale().doubleValue(), 20d); + assertEquals(wxMessage.getLabel(), "位置信息"); + assertEquals(wxMessage.getDescription(), "公众平台官网链接"); + assertEquals(wxMessage.getUrl(), "url"); + assertEquals(wxMessage.getTitle(), "公众平台官网链接"); + assertEquals(wxMessage.getEvent(), "subscribe"); + assertEquals(wxMessage.getEventKey(), "qrscene_123123"); + assertEquals(wxMessage.getTicket(), "TICKET"); + assertEquals(wxMessage.getLatitude().doubleValue(), 23.137466); + assertEquals(wxMessage.getLongitude().doubleValue(), 113.352425); + assertEquals(wxMessage.getPrecision().doubleValue(), 119.385040); + assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); + assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); + assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); + assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); + assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); + assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); + assertEquals(wxMessage.getSendLocationInfo().getPoiName(), "wo de poi"); + } + + public void testSendPicsInfo() { + String xml = "" + + "" + + "" + + "1502012364" + + "" + + "1000004" + + "" + + "" + + "" + + "" + + "" + + "2" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("", "")); + assertEquals(wxMessage.getToUserName(), "wx45a0972125658be9"); + assertEquals(wxMessage.getFromUserName(), "xiaohe"); + assertEquals(wxMessage.getCreateTime(), new Long(1502012364L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getAgentId(), Integer.valueOf(1000004)); + assertEquals(wxMessage.getEvent(), "pic_weixin"); + assertEquals(wxMessage.getEventKey(), "faceSimilarity"); + assertNotNull(wxMessage.getSendPicsInfo()); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(2L)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "aef52ae501537e552725c5d7f99c1741"); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(1).getPicMd5Sum(), "c4564632a4fab91378c39bea6aad6f9e"); + } + + public void testExtAttr() { + + String xml = "" + + " " + + " " + + " 1557241961" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + "
    " + + "
    "; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUserName(), "w56c9fe3d50ad1ea2"); + assertEquals(wxMessage.getFromUserName(), "sys"); + assertEquals(wxMessage.getCreateTime(), new Long(1557241961)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), "change_contact"); + assertEquals(wxMessage.getChangeType(), "update_user"); + assertEquals(wxMessage.getUserId(), "zhangsan"); + assertNotNull(wxMessage.getExtAttrs()); + assertNotNull(wxMessage.getExtAttrs().getItems()); + assertEquals(wxMessage.getExtAttrs().getItems().size(), 3); + assertEquals(wxMessage.getExtAttrs().getItems().get(0).getName(), "爱好"); + + } + + public void testTaskCardEvent() { + String xml = "" + + "" + + "" + + "123456789" + + "" + + "" + + "" + + "" + + "1" + + ""; WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); - Assert.assertEquals(wxMessage.getToUserName(), "toUser"); - Assert.assertEquals(wxMessage.getFromUserName(), "fromUser"); - Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860l)); - Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); - Assert.assertEquals(wxMessage.getContent(), "this is a test"); - Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l)); - Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); - Assert.assertEquals(wxMessage.getMediaId(), "media_id"); - Assert.assertEquals(wxMessage.getFormat(), "Format"); - Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); - Assert.assertEquals(wxMessage.getLocationX(), new Double(23.134521d)); - Assert.assertEquals(wxMessage.getLocationY(), new Double(113.358803d)); - Assert.assertEquals(wxMessage.getScale(), new Double(20)); - Assert.assertEquals(wxMessage.getLabel(), "位置信息"); - Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getUrl(), "url"); - Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getEvent(), "subscribe"); - Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); - Assert.assertEquals(wxMessage.getTicket(), "TICKET"); - Assert.assertEquals(wxMessage.getLatitude(), new Double(23.137466)); - Assert.assertEquals(wxMessage.getLongitude(), new Double(113.352425)); - Assert.assertEquals(wxMessage.getPrecision(), new Double(119.385040)); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); - Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l)); - Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "FromUser"); + assertEquals(wxMessage.getCreateTime(), Long.valueOf(123456789L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getAgentId(), Integer.valueOf(1)); + assertEquals(wxMessage.getEvent(), TASKCARD_CLICK); + assertEquals(wxMessage.getEventKey(), "key111"); + assertEquals(wxMessage.getTaskId(), "taskid111"); } + public void testAddExternalUserEvent() { + String xml = "" + + "" + + "" + + "1403610513" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "sys"); + assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT); + assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.ADD_EXTERNAL_CONTACT); + assertEquals(wxMessage.getExternalUserId(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); + assertEquals(wxMessage.getState(), "teststate"); + assertEquals(wxMessage.getWelcomeCode(), "WELCOMECODE"); + + } + + public void testDelExternalUserEvent() { + String xml = "" + + "" + + "" + + "1403610513" + + "" + + "" + + "" + + "" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "sys"); + assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT); + assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.DEL_EXTERNAL_CONTACT); + assertEquals(wxMessage.getUserId(), "zhangsan"); + assertEquals(wxMessage.getExternalUserId(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); + } + + public void testChangeContact() { + String xml = "\n" + + " \n" + + " \n" + + " 1403610513\n" + + " \n" + + " \n" + + " update_user\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 15913215421\n" + + " 1\n" + + " \n" + + " 1\n" + + " \n" + + " \n" + + " \n" + + "
    \n" + + " \n" + + " \n" + + " \n" + + " 0\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " \n" + + " <![CDATA[企业微信]]>\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
    "; + + WxCpXmlMessage wxCpXmlMessage = WxCpXmlMessage.fromXml(xml); + assertThat(wxCpXmlMessage).isNotNull(); + assertThat(wxCpXmlMessage.getDepartments()).isNotEmpty(); + + System.out.println(XStreamTransformer.toXml(WxCpXmlMessage.class, wxCpXmlMessage)); + } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java index 8ace5d0aae..87c9454c91 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java @@ -9,17 +9,17 @@ public class WxCpXmlOutImageMessageTest { public void test() { WxCpXmlOutImageMessage m = new WxCpXmlOutImageMessage(); m.setMediaId("ddfefesfsdfef"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("from"); m.setToUserName("to"); String expected = "" - + "" - + "" - + "1122" - + "" - + "" - + ""; + + "" + + "" + + "1122" + + "" + + "" + + ""; System.out.println(m.toXml()); Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); } @@ -27,21 +27,21 @@ public void test() { public void testBuild() { WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); String expected = "" - + "" - + "" - + "1122" - + "" - + "" - + ""; + + "" + + "" + + "1122" + + "" + + "" + + ""; System.out.println(m.toXml()); Assert.assertEquals( - m - .toXml() - .replaceAll("\\s", "") - .replaceAll(".*?", ""), - expected - .replaceAll("\\s", "") - .replaceAll(".*?", "") + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") ); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java index 872c0ac327..128bc9a4c6 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java @@ -8,7 +8,7 @@ public class WxCpXmlOutNewsMessageTest { public void test() { WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("fromUser"); m.setToUserName("toUser"); @@ -21,26 +21,26 @@ public void test() { m.addArticle(item); String expected = "" - + "" - + "" - + "1122" - + "" - + " " - + " " - + " <![CDATA[title]]>" - + " " - + " " - + " " - + " " - + " " - + " <![CDATA[title]]>" - + " " - + " " - + " " - + " " - + " " - + " 2" - + ""; + + "" + + "" + + "1122" + + "" + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + " 2" + + ""; System.out.println(m.toXml()); Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); } @@ -53,41 +53,41 @@ public void testBuild() { item.setUrl("url"); WxCpXmlOutNewsMessage m = WxCpXmlOutMessage.NEWS() - .fromUser("fromUser") - .toUser("toUser") - .addArticle(item) - .addArticle(item) - .build(); + .fromUser("fromUser") + .toUser("toUser") + .addArticle(item) + .addArticle(item) + .build(); String expected = "" - + "" - + "" - + "1122" - + "" - + " " - + " " - + " <![CDATA[title]]>" - + " " - + " " - + " " - + " " - + " " - + " <![CDATA[title]]>" - + " " - + " " - + " " - + " " - + " " - + " 2" - + ""; + + "" + + "" + + "1122" + + "" + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + " 2" + + ""; System.out.println(m.toXml()); Assert.assertEquals( - m - .toXml() - .replaceAll("\\s", "") - .replaceAll(".*?", ""), - expected - .replaceAll("\\s", "") - .replaceAll(".*?", "") + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") ); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java index 57fcf8eb98..fd09ed6b92 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java @@ -9,17 +9,17 @@ public class WxCpXmlOutTextMessageTest { public void test() { WxCpXmlOutTextMessage m = new WxCpXmlOutTextMessage(); m.setContent("content"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("from"); m.setToUserName("to"); String expected = "" - + "" - + "" - + "1122" - + "" - + "" - + ""; + + "" + + "" + + "1122" + + "" + + "" + + ""; System.out.println(m.toXml()); Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); } @@ -27,21 +27,21 @@ public void test() { public void testBuild() { WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); String expected = "" - + "" - + "" - + "1122" - + "" - + "" - + ""; + + "" + + "" + + "1122" + + "" + + "" + + ""; System.out.println(m.toXml()); Assert.assertEquals( - m - .toXml() - .replaceAll("\\s", "") - .replaceAll(".*?", ""), - expected - .replaceAll("\\s", "") - .replaceAll(".*?", "") + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") ); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java index 512dfef3b7..c5551dec01 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java @@ -11,53 +11,53 @@ public void test() { m.setMediaId("media_id"); m.setTitle("title"); m.setDescription("ddfff"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("fromUser"); m.setToUserName("toUser"); String expected = "" - + "" - + "" - + "1122" - + "" - + " " - + ""; + + "" + + "" + + "1122" + + "" + + " " + + ""; System.out.println(m.toXml()); Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); } public void testBuild() { WxCpXmlOutVideoMessage m = WxCpXmlOutMessage.VIDEO() - .mediaId("media_id") - .fromUser("fromUser") - .toUser("toUser") - .title("title") - .description("ddfff") - .build(); + .mediaId("media_id") + .fromUser("fromUser") + .toUser("toUser") + .title("title") + .description("ddfff") + .build(); String expected = "" - + "" - + "" - + "1122" - + "" - + " " - + ""; + + "" + + "" + + "1122" + + "" + + " " + + ""; System.out.println(m.toXml()); Assert.assertEquals( - m - .toXml() - .replaceAll("\\s", "") - .replaceAll(".*?", ""), - expected - .replaceAll("\\s", "") - .replaceAll(".*?", "") + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") ); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java index 26db29c3c3..a3c9688c44 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java @@ -9,17 +9,17 @@ public class WxCpXmlOutVoiceMessageTest { public void test() { WxCpXmlOutVoiceMessage m = new WxCpXmlOutVoiceMessage(); m.setMediaId("ddfefesfsdfef"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("from"); m.setToUserName("to"); String expected = "" - + "" - + "" - + "1122" - + "" - + "" - + ""; + + "" + + "" + + "1122" + + "" + + "" + + ""; System.out.println(m.toXml()); Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); } @@ -27,21 +27,21 @@ public void test() { public void testBuild() { WxCpXmlOutVoiceMessage m = WxCpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); String expected = "" - + "" - + "" - + "1122" - + "" - + "" - + ""; + + "" + + "" + + "1122" + + "" + + "" + + ""; System.out.println(m.toXml()); Assert.assertEquals( - m - .toXml() - .replaceAll("\\s", "") - .replaceAll(".*?", ""), - expected - .replaceAll("\\s", "") - .replaceAll(".*?", "") + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") ); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequestTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequestTest.java new file mode 100644 index 0000000000..b5022115b8 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequestTest.java @@ -0,0 +1,92 @@ +package me.chanjar.weixin.cp.bean.oa; + +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.bean.oa.applydata.ApplyDataContent; +import me.chanjar.weixin.cp.bean.oa.applydata.ContentValue; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试. + * + * @author Binary Wang + * @date 2020-07-18 + */ +public class WxCpOaApplyEventRequestTest { + @Test + public void testToJson() { + String json = "{\n" + + " \"creator_userid\": \"WangXiaoMing\",\n" + + " \"template_id\": \"3Tka1eD6v6JfzhDMqPd3aMkFdxqtJMc2ZRioeFXkaaa\",\n" + + " \"use_template_approver\":0,\n" + + " \"approver\": [\n" + + " {\n" + + " \"attr\": 2,\n" + + " \"userid\": [\"WuJunJie\",\"WangXiaoMing\"]\n" + + " },\n" + + " {\n" + + " \"attr\": 1,\n" + + " \"userid\": [\"LiuXiaoGang\"]\n" + + " }\n" + + " ],\n" + + " \"notifyer\":[ \"WuJunJie\",\"WangXiaoMing\" ],\n" + + " \"notify_type\" : 1,\n" + + " \"apply_data\": {\n" + + " \"contents\": [\n" + + " {\n" + + " \"control\": \"Text\",\n" + + " \"id\": \"Text-15111111111\",\n" + + " \"value\": {\n" + + " \"text\": \"文本填写的内容\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"summary_list\": [\n" + + " {\n" + + " \"summary_info\": [{\n" + + " \"text\": \"摘要第1行\",\n" + + " \"lang\": \"zh_CN\"\n" + + " }]\n" + + " },\n" + + " {\n" + + " \"summary_info\": [{\n" + + " \"text\": \"摘要第2行\",\n" + + " \"lang\": \"zh_CN\"\n" + + " }]\n" + + " },\n" + + " {\n" + + " \"summary_info\": [{\n" + + " \"text\": \"摘要第3行\",\n" + + " \"lang\": \"zh_CN\"\n" + + " }]\n" + + " }\n" + + " ]\n" + + "}"; + + WxCpOaApplyEventRequest request = new WxCpOaApplyEventRequest(); + request.setCreatorUserId("WangXiaoMing") + .setTemplateId("3Tka1eD6v6JfzhDMqPd3aMkFdxqtJMc2ZRioeFXkaaa") + .setUseTemplateApprover(0) + .setApprovers(Arrays.asList(new WxCpOaApplyEventRequest.Approver().setAttr(2).setUserIds(new String[]{"WuJunJie", "WangXiaoMing"}), + new WxCpOaApplyEventRequest.Approver().setAttr(1).setUserIds(new String[]{"LiuXiaoGang"}))) + .setNotifiers(new String[]{"WuJunJie", "WangXiaoMing"}) + .setNotifyType(1) + .setApplyData(new WxCpOaApplyEventRequest.ApplyData() + .setContents(Collections.singletonList(new ApplyDataContent() + .setControl("Text").setId("Text-15111111111").setValue(new ContentValue().setText("文本填写的内容"))))) + .setSummaryList(Arrays.asList(new SummaryInfo() + .setSummaryInfoData(Collections.singletonList(new SummaryInfo.SummaryInfoData().setLang("zh_CN").setText("摘要第1行"))), + new SummaryInfo() + .setSummaryInfoData(Collections.singletonList(new SummaryInfo.SummaryInfoData().setLang("zh_CN").setText("摘要第2行"))), + new SummaryInfo() + .setSummaryInfoData(Collections.singletonList(new SummaryInfo.SummaryInfoData().setLang("zh_CN").setText("摘要第3行"))))) + ; + + assertThat(request.toJson()).isEqualTo(GsonParser.parse(json).toString()); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java index 9a97b6cd11..35a2fc0e43 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java @@ -1,28 +1,23 @@ package me.chanjar.weixin.cp.demo; +import java.io.InputStream; + import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.ToString; import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.cp.api.WxCpInMemoryConfigStorage; - -import java.io.InputStream; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; /** * @author Daniel Qian */ @XStreamAlias("xml") -class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { - +@ToString +public class WxCpDemoInMemoryConfigStorage extends WxCpDefaultConfigImpl { public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class); return (WxCpDemoInMemoryConfigStorage) xstream.fromXML(is); } - @Override - public String toString() { - return "SimpleWxConfigProvider [appidOrCorpid=" + this.corpId + ", corpSecret=" + this.corpSecret + ", accessToken=" + this.accessToken - + ", expiresTime=" + this.expiresTime + ", token=" + this.token + ", aesKey=" + this.aesKey + "]"; - } - } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java index dc667d88ba..df656a68a8 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java @@ -8,15 +8,17 @@ import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.WxCpMessageHandler; -import me.chanjar.weixin.cp.api.WxCpMessageRouter; +import me.chanjar.weixin.cp.constant.WxCpConsts; import me.chanjar.weixin.cp.api.WxCpService; -import me.chanjar.weixin.cp.api.WxCpServiceImpl; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.message.WxCpMessageHandler; +import me.chanjar.weixin.cp.message.WxCpMessageRouter; public class WxCpDemoServer { @@ -44,9 +46,9 @@ public static void main(String[] args) throws Exception { private static void initWeixin() throws IOException { try (InputStream is1 = ClassLoader - .getSystemResourceAsStream("test-config.xml")) { + .getSystemResourceAsStream("test-config.xml")) { WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage - .fromXml(is1); + .fromXml(is1); wxCpConfigStorage = config; wxCpService = new WxCpServiceImpl(); @@ -55,11 +57,11 @@ private static void initWeixin() throws IOException { WxCpMessageHandler handler = new WxCpMessageHandler() { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, - Map context, WxCpService wxService, - WxSessionManager sessionManager) { + Map context, WxCpService wxService, + WxSessionManager sessionManager) { WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content("测试加密消息") - .fromUser(wxMessage.getToUserName()) - .toUser(wxMessage.getFromUserName()).build(); + .fromUser(wxMessage.getToUserName()) + .toUser(wxMessage.getFromUserName()).build(); return m; } }; @@ -67,22 +69,38 @@ public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, WxCpMessageHandler oauth2handler = new WxCpMessageHandler() { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, - Map context, WxCpService wxService, - WxSessionManager sessionManager) { + Map context, WxCpService wxService, + WxSessionManager sessionManager) { String href = "测试oauth2"; + + wxService.getOauth2Service().buildAuthorizationUrl(wxCpConfigStorage.getOauth2redirectUri(), null) + + "\">测试oauth2"; return WxCpXmlOutMessage.TEXT().content(href) - .fromUser(wxMessage.getToUserName()) - .toUser(wxMessage.getFromUserName()).build(); + .fromUser(wxMessage.getToUserName()) + .toUser(wxMessage.getFromUserName()).build(); } }; wxCpMessageRouter = new WxCpMessageRouter(wxCpService); - wxCpMessageRouter.rule().async(false).content("哈哈") // 拦截内容为“哈哈”的消息 - .handler(handler).end().rule().async(false).content("oauth") - .handler(oauth2handler).end(); + wxCpMessageRouter.rule() + .async(false) + .content("哈哈") // 拦截内容为“哈哈”的消息 + .handler(handler) + .end() + .rule() + .async(false) + .content("oauth") + .handler(oauth2handler) + .end() + .rule() + .event(WxCpConsts.EventType.CHANGE_CONTACT) + .handler(new WxCpMessageHandler() { + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService, WxSessionManager sessionManager) throws WxErrorException { + System.out.println("通讯录发生变更"); + return null; + } + }) + .end(); } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java index 43bf179943..291cef403d 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.demo; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.WxCpMessageRouter; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.message.WxCpMessageRouter; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; @@ -31,7 +31,7 @@ public WxCpEndpointServlet(WxCpConfigStorage wxCpConfigStorage, WxCpService wxCp @Override protected void service(HttpServletRequest request, HttpServletResponse response) - throws IOException { + throws IOException { response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); @@ -55,7 +55,7 @@ protected void service(HttpServletRequest request, HttpServletResponse response) } WxCpXmlMessage inMessage = WxCpXmlMessage - .fromEncryptedXml(request.getInputStream(), this.wxCpConfigStorage, timestamp, nonce, msgSignature); + .fromEncryptedXml(request.getInputStream(), this.wxCpConfigStorage, timestamp, nonce, msgSignature); WxCpXmlOutMessage outMessage = this.wxCpMessageRouter.route(inMessage); if (outMessage != null) { response.getWriter().write(outMessage.toEncryptedXml(this.wxCpConfigStorage)); diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java index edab896644..b6dda81301 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java @@ -1,14 +1,13 @@ package me.chanjar.weixin.cp.demo; -import java.io.IOException; -import java.util.Arrays; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.api.WxCpService; +import java.io.IOException; public class WxCpOAuth2Servlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -21,7 +20,7 @@ public WxCpOAuth2Servlet(WxCpService wxCpService) { @Override protected void service(HttpServletRequest request, HttpServletResponse response) - throws IOException { + throws IOException { response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); @@ -31,9 +30,9 @@ protected void service(HttpServletRequest request, HttpServletResponse response) response.getWriter().println("

    code

    "); response.getWriter().println(code); - String[] res = this.wxCpService.oauth2getUserInfo(code); + WxCpOauth2UserInfo res = this.wxCpService.getOauth2Service().getUserInfo(code); response.getWriter().println("

    result

    "); - response.getWriter().println(Arrays.toString(res)); + response.getWriter().println(res); } catch (WxErrorException e) { e.printStackTrace(); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtilTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtilTest.java new file mode 100644 index 0000000000..62fbce13b8 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtilTest.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.cp.util.crypto; + +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; +import org.apache.commons.codec.binary.Base64; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * @author Binary Wang + * @date 2020-06-11 + */ +public class WxCpCryptUtilTest { + @Test + public void test() { + String encodingAesKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C"; + final byte[] commonsCodec = Base64.decodeBase64(encodingAesKey + "="); + final byte[] guava = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey)); + final byte[] guava1 = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey + "=")); + assertEquals(commonsCodec, guava); + assertEquals(guava1, guava); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterForPrivatizedVersionTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterForPrivatizedVersionTest.java new file mode 100644 index 0000000000..167de73c46 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterForPrivatizedVersionTest.java @@ -0,0 +1,89 @@ +package me.chanjar.weixin.cp.util.json; + +import me.chanjar.weixin.cp.bean.WxCpUser; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + * 企业微信(私有化版本getUser兼容测试)
    + * 
    + * + * @author 庄壮壮 + * @since 2020-06-16 09:36 + */ +public class WxCpUserGsonAdapterForPrivatizedVersionTest { + + @Test + public void testDeserialize() { + final String userJson = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"userid\": \"zhangsan\",\n" + + " \"name\": \"李四\",\n" + + " \"department\": [1, 2],\n" + + " \"order\": [2, 10],\n" + + " \"position\": \"后台工程师1\",\n" + + " \"positions\": [\"后台工程师1\",\"后台工程师2\"],\n" + + " \"mobile\": \"15913215421\",\n" + + " \"hide_mobile\": 0,\n" + + " \"gender\": \"1\",\n" + + " \"email\": \"zhangsan@gzdev.com\",\n" + + " \"is_leader_in_dept\": [1, 0],\n" + + " \"avatar\": \"http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0\",\n" + + " \"telephone\": \"020-123456\",\n" + + " \"english_name\": \"jackzhang\",\n" + + " \"extattr\": {\"attrs\":[{\"name\":\"爱好\",\"value\":\"旅游\"},{\"name\":\"卡号\",\"value\":\"1234567234\"}]},\n" + + " \"status\": 1,\n" + + " \"enable\": 0,\n" + + " \"qr_code\": \"https://wwlocal.qq.com/wework_admin/userQRCode?vcode=vc2140a8b3c6207c74&lvc=vcf6f1acfdc4b45088\"\n" + + "}"; + + final WxCpUser user = WxCpUser.fromJson(userJson); + assertThat(user).isNotNull(); + + // test order + assertThat(user.getOrders()).isNotEmpty(); + assertThat(user.getOrders().length).isEqualTo(2); + assertThat(user.getOrders()[0]).isEqualTo(2); + assertThat(user.getOrders()[1]).isEqualTo(10); + + // test english name + assertThat(user.getEnglishName()).isEqualTo("jackzhang"); + + // test extattrs + assertThat(user.getExtAttrs()).isNotEmpty(); + final WxCpUser.Attr extraAttr1 = user.getExtAttrs().get(0); + assertThat(extraAttr1.getName()).isEqualTo("爱好"); + assertThat(extraAttr1.getTextValue()).isEqualTo("旅游"); + final WxCpUser.Attr extraAttr2 = user.getExtAttrs().get(1); + assertThat(extraAttr2.getName()).isEqualTo("卡号"); + assertThat(extraAttr2.getTextValue()).isEqualTo("1234567234"); + + // test position + assertThat(user.getPosition()).isEqualTo("后台工程师1"); + // test positions + assertThat(user.getPositions()).isNotEmpty(); + assertThat(user.getPositions().length).isEqualTo(2); + assertThat(user.getPositions()[0]).isEqualTo("后台工程师1"); + assertThat(user.getPositions()[1]).isEqualTo("后台工程师2"); + } + + @Test + public void testSerialize() { + WxCpUser user = new WxCpUser(); + user.setOrders(new Integer[]{1, 2}); + user.setPositions(new String[]{"后台工程师1", "后台工程师2"}); + user.setEnglishName("jackson"); + WxCpUser.Attr attr1 = new WxCpUser.Attr(); + attr1.setName("爱好").setTextValue("旅游"); + WxCpUser.Attr attr2 = new WxCpUser.Attr(); + attr2.setName("卡号").setTextValue("1234567234"); + + user.addExtAttr(attr1); + user.addExtAttr(attr2); + + assertThat(user.toJson()).isEqualTo("{\"order\":[1,2],\"positions\":[\"后台工程师1\",\"后台工程师2\"],\"english_name\":\"jackson\",\"extattr\":{\"attrs\":[{\"name\":\"爱好\",\"value\":\"旅游\"},{\"name\":\"卡号\",\"value\":\"1234567234\"}]},\"external_profile\":{}}"); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java new file mode 100644 index 0000000000..d78175c1b8 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java @@ -0,0 +1,168 @@ +package me.chanjar.weixin.cp.util.json; + +import me.chanjar.weixin.cp.bean.WxCpUser; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/9/16.
    + * 
    + * + * @author BinaryWang + */ +public class WxCpUserGsonAdapterTest { + + @Test + public void testDeserialize() { + final String userJson = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"userid\": \"zhangsan\",\n" + + " \"name\": \"李四\",\n" + + " \"department\": [1, 2],\n" + + " \"order\": [1, 2],\n" + + " \"position\": \"后台工程师\",\n" + + " \"mobile\": \"15913215421\",\n" + + " \"gender\": \"1\",\n" + + " \"email\": \"zhangsan@gzdev.com\",\n" + + " \"isleader\": 1,\n" + + " \"avatar\": \"http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0\",\n" + + " \"telephone\": \"020-123456\",\n" + + " \"address\": \"广州市海珠区新港中路\"," + + " \"enable\": 1,\n" + + " \"alias\": \"jackzhang\",\n" + + " \"extattr\": {\n" + + " \"attrs\": [\n" + + " {\n" + + " \"type\": 0,\n" + + " \"name\": \"文本名称\",\n" + + " \"text\": {\n" + + " \"value\": \"文本\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"type\": 1,\n" + + " \"name\": \"网页名称\",\n" + + " \"web\": {\n" + + " \"url\": \"http://www.test.com\",\n" + + " \"title\": \"标题\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }," + + " \"status\": 1,\n" + + " \"qr_code\": \"https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxx\",\n" + + " \"external_position\": \"高级产品经理\",\n" + + " \"external_profile\": {\n" + + " \"external_corp_name\": \"企业简称\",\n" + + " \"external_attr\": [\n" + + " {\n" + + " \"type\": 0,\n" + + " \"name\": \"文本名称\",\n" + + " \"text\": {\n" + + " \"value\": \"文本\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"type\": 1,\n" + + " \"name\": \"网页名称\",\n" + + " \"web\": {\n" + + " \"url\": \"http://www.test.com\",\n" + + " \"title\": \"标题\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"type\": 2,\n" + + " \"name\": \"测试app\",\n" + + " \"miniprogram\": {\n" + + " \"appid\": \"wx8bd8012614784fake\",\n" + + " \"pagepath\": \"/index\",\n" + + " \"title\": \"my miniprogram\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }" + + "}"; + + final WxCpUser user = WxCpUser.fromJson(userJson); + assertThat(user).isNotNull(); + + assertThat(user.getOrders()).isNotEmpty(); + assertThat(user.getOrders().length).isEqualTo(2); + assertThat(user.getOrders()[0]).isEqualTo(1); + assertThat(user.getOrders()[1]).isEqualTo(2); + + assertThat(user.getAddress()).isEqualTo("广州市海珠区新港中路"); + assertThat(user.getAlias()).isEqualTo("jackzhang"); + + assertThat(user.getExtAttrs()).isNotEmpty(); + + final WxCpUser.Attr extraAttr1 = user.getExtAttrs().get(0); + assertThat(extraAttr1.getType()).isEqualTo(0); + assertThat(extraAttr1.getName()).isEqualTo("文本名称"); + assertThat(extraAttr1.getTextValue()).isEqualTo("文本"); + + final WxCpUser.Attr extraAttr2 = user.getExtAttrs().get(1); + assertThat(extraAttr2.getType()).isEqualTo(1); + assertThat(extraAttr2.getName()).isEqualTo("网页名称"); + assertThat(extraAttr2.getWebTitle()).isEqualTo("标题"); + assertThat(extraAttr2.getWebUrl()).isEqualTo("http://www.test.com"); + + assertThat(user.getExternalPosition()).isEqualTo("高级产品经理"); + assertThat(user.getExternalCorpName()).isEqualTo("企业简称"); + + assertThat(user.getExternalAttrs()).isNotEmpty(); + + final WxCpUser.ExternalAttribute externalAttr1 = user.getExternalAttrs().get(0); + assertThat(externalAttr1.getType()).isEqualTo(0); + assertThat(externalAttr1.getName()).isEqualTo("文本名称"); + assertThat(externalAttr1.getValue()).isEqualTo("文本"); + + final WxCpUser.ExternalAttribute externalAttr2 = user.getExternalAttrs().get(1); + assertThat(externalAttr2.getType()).isEqualTo(1); + assertThat(externalAttr2.getName()).isEqualTo("网页名称"); + assertThat(externalAttr2.getUrl()).isEqualTo("http://www.test.com"); + assertThat(externalAttr2.getTitle()).isEqualTo("标题"); + + final WxCpUser.ExternalAttribute externalAttr3 = user.getExternalAttrs().get(2); + assertThat(externalAttr3.getType()).isEqualTo(2); + assertThat(externalAttr3.getName()).isEqualTo("测试app"); + assertThat(externalAttr3.getAppid()).isEqualTo("wx8bd8012614784fake"); + assertThat(externalAttr3.getPagePath()).isEqualTo("/index"); + assertThat(externalAttr3.getTitle()).isEqualTo("my miniprogram"); + + } + + @Test + public void testSerialize() { + WxCpUser user = new WxCpUser(); + user.setOrders(new Integer[]{1, 2}); + user.addExternalAttr(WxCpUser.ExternalAttribute.builder() + .type(0) + .name("文本名称") + .value("文本") + .build()); + user.addExternalAttr(WxCpUser.ExternalAttribute.builder() + .type(1) + .name("网页名称") + .url("http://www.test.com") + .title("标题") + .build()); + user.addExternalAttr(WxCpUser.ExternalAttribute.builder() + .type(2) + .name("测试app") + .appid("wx8bd80126147df384") + .pagePath("/index") + .title("my miniprogram") + .build()); + + assertThat(user.toJson()).isEqualTo("{\"order\":[1,2],\"external_profile\":{\"external_attr\":" + + "[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}}," + + "{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}}," + + "{\"type\":2,\"name\":\"测试app\"," + + "\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}"); + } +} diff --git a/weixin-java-cp/src/test/resources/logback-test.xml b/weixin-java-cp/src/test/resources/logback-test.xml index 9c2ec6ae38..e4a33acd88 100644 --- a/weixin-java-cp/src/test/resources/logback-test.xml +++ b/weixin-java-cp/src/test/resources/logback-test.xml @@ -1,16 +1,13 @@ - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n - - + + - diff --git a/weixin-java-cp/src/test/resources/mm.amr b/weixin-java-cp/src/test/resources/mm.amr new file mode 100644 index 0000000000..a4df01a0d9 Binary files /dev/null and b/weixin-java-cp/src/test/resources/mm.amr differ diff --git a/weixin-java-cp/src/test/resources/mm.jpeg b/weixin-java-cp/src/test/resources/mm.jpeg index 183699e96d..e1a0cea70d 100644 Binary files a/weixin-java-cp/src/test/resources/mm.jpeg and b/weixin-java-cp/src/test/resources/mm.jpeg differ diff --git a/weixin-java-cp/src/test/resources/test-config.sample.xml b/weixin-java-cp/src/test/resources/test-config.sample.xml index aa99a962bd..76a74a25a3 100644 --- a/weixin-java-cp/src/test/resources/test-config.sample.xml +++ b/weixin-java-cp/src/test/resources/test-config.sample.xml @@ -1,13 +1,14 @@ - 企业号corpid - 企业号corpsecret - 企业号应用id - 企业号应用Token - 企业号应用EncodingAESKey - 可以不填写 - 可以不填写 - 企业号通讯录里的某个userid - 企业号通讯录的某个部门id - 企业号通讯录里的某个tagid - 网页授权获取用户信息回调地址 + 企业号corpid + 企业号corpsecret + 企业号应用id + 企业号应用Token + 企业号应用EncodingAESKey + 可以不填写 + 可以不填写 + 企业号通讯录里的某个userid + 企业号通讯录的某个部门id + 企业号通讯录里的某个tagid + 网页授权获取用户信息回调地址 + webhook链接地址的key值 diff --git a/weixin-java-cp/src/test/resources/testng.xml b/weixin-java-cp/src/test/resources/testng.xml index ba92bc0c0c..563928bdf0 100644 --- a/weixin-java-cp/src/test/resources/testng.xml +++ b/weixin-java-cp/src/test/resources/testng.xml @@ -1,17 +1,12 @@ - - + + - - - - - diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml new file mode 100644 index 0000000000..16d56b9307 --- /dev/null +++ b/weixin-java-miniapp/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + com.github.binarywang + wx-java + 3.9.0 + + + weixin-java-miniapp + WxJava - MiniApp Java SDK + 微信小程序 Java SDK + + + + com.github.binarywang + weixin-java-common + ${project.version} + + + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + + + + org.testng + testng + test + + + ch.qos.logback + logback-classic + test + + + com.google.inject + guice + test + + + org.eclipse.jetty + jetty-server + test + + + org.eclipse.jetty + jetty-servlet + test + + + joda-time + joda-time + test + + + org.assertj + assertj-guava + test + + + redis.clients + jedis + + + org.bouncycastle + bcpkix-jdk15on + 1.65 + + + org.projectlombok + lombok + + + org.redisson + redisson + + + com.github.jedis-lock + jedis-lock + true + + + org.mockito + mockito-core + 3.3.3 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + src/test/resources/testng.xml + + + + + + + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + + diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaAnalysisService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaAnalysisService.java new file mode 100644 index 0000000000..e8273bc402 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaAnalysisService.java @@ -0,0 +1,145 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaSummaryTrend; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitDistribution; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitPage; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitTrend; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.Date; +import java.util.List; + +/** + * 小程序数据分析相关接口 + * 文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/analysis.html + * + * @author Charming + * @since 2018-04-28 + */ +public interface WxMaAnalysisService { + String GET_DAILY_SUMMARY_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailysummarytrend"; + String GET_DAILY_VISIT_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailyvisittrend"; + String GET_WEEKLY_VISIT_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappidweeklyvisittrend"; + String GET_MONTHLY_VISIT_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappidmonthlyvisittrend"; + String GET_VISIT_DISTRIBUTION_URL = "https://api.weixin.qq.com/datacube/getweanalysisappidvisitdistribution"; + String GET_DAILY_RETAIN_INFO_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailyretaininfo"; + String GET_WEEKLY_RETAIN_INFO_URL = "https://api.weixin.qq.com/datacube/getweanalysisappidweeklyretaininfo"; + String GET_MONTHLY_RETAIN_INFO_URL = "https://api.weixin.qq.com/datacube/getweanalysisappidmonthlyretaininfo"; + String GET_VISIT_PAGE_URL = "https://api.weixin.qq.com/datacube/getweanalysisappidvisitpage"; + String GET_USER_PORTRAIT_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiduserportrait"; + + /** + * 查询概况趋势 + * 温馨提示:小程序接口目前只能查询一天的数据,即 beginDate 和 endDate 一样 + * + * @param beginDate 开始日期 + * @param endDate 结束日期,限定查询1天数据,end_date允许设置的最大值为昨日 + * @return 概况趋势 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + List getDailySummaryTrend(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 获取日访问趋势 + * 温馨提示:小程序接口目前只能查询一天的数据,即 beginDate 和 endDate 一样 + * + * @param beginDate 开始日期 + * @param endDate 结束日期,限定查询1天数据,end_date允许设置的最大值为昨日 + * @return 日访问趋势 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + List getDailyVisitTrend(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 获取周访问趋势 + * 限定查询一个自然周的数据,时间必须按照自然周的方式输入: 如:20170306(周一), 20170312(周日) + * + * @param beginDate 开始日期,为周一日期 + * @param endDate 结束日期,为周日日期,限定查询一周数据 + * @return 周访问趋势(每项数据都是一个自然周汇总) + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + List getWeeklyVisitTrend(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 获取月访问趋势 + * 限定查询一个自然月的数据,时间必须按照自然月的方式输入: 如:20170201(月初), 20170228(月末) + * + * @param beginDate 开始日期,为自然月第一天 + * @param endDate 结束日期,为自然月最后一天,限定查询一个月数据 + * @return 月访问趋势(每项数据都是一个自然月汇总) + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + List getMonthlyVisitTrend(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 获取访问分布 + * (此接口目前只能查询一天的数据,即 beginDate 和 endDate 一样) + * + * @param beginDate 开始日期,为周一日期 + * @param endDate 结束日期,限定查询1天数据,end_date允许设置的最大值为昨日 + * @return 访问分布 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + WxMaVisitDistribution getVisitDistribution(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 日留存 + * (此接口目前只能查询一天的数据,即 beginDate 和 endDate 一样) + * + * @param beginDate 开始日期,为周一日期 + * @param endDate 结束日期,限定查询 1 天数据,endDate 允许设置的最大值为昨日 + * @return 日留存 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + WxMaRetainInfo getDailyRetainInfo(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 周留存 + * 限定查询一个自然周的数据,时间必须按照自然周的方式输入: 如:20170306(周一), 20170312(周日) + * + * @param beginDate 开始日期,为周一日期 + * @param endDate 结束日期,为周日日期,限定查询一周数据 + * @return 周留存 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + WxMaRetainInfo getWeeklyRetainInfo(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 月留存 + * 限定查询一个自然月的数据,时间必须按照自然月的方式输入: 如:20170201(月初), 20170228(月末) + * + * @param beginDate 开始日期,为自然月第一天 + * @param endDate 结束日期,为自然月最后一天,限定查询一个月数据 + * @return 月留存 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + WxMaRetainInfo getMonthlyRetainInfo(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 获取访问页面数据 + * 温馨提示:此接口目前只能查询一天的数据,即 beginDate 和 endDate 一样 + * + * @param beginDate 开始日期 + * @param endDate 结束日期,限定查询1天数据,end_date允许设置的最大值为昨日 + * @return 访问页面数据 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + List getVisitPage(Date beginDate, Date endDate) throws WxErrorException; + + /** + * 获取小程序新增或活跃用户的画像分布数据 + * 时间范围支持昨天、最近7天、最近30天。 + * 其中,新增用户数为时间范围内首次访问小程序的去重用户数, + * 活跃用户数为时间范围内访问过小程序的去重用户数。 + * 画像属性包括用户年龄、性别、省份、城市、终端类型、机型。 + * + * @param beginDate 开始日期 + * @param endDate 结束日期,开始日期与结束日期相差的天数限定为0/6/29,分别表示查询最近1/7/30天数据,end_date允许设置的最大值为昨日 + * @return 小程序新增或活跃用户的画像分布数据 + * @throws WxErrorException 获取失败时抛出,具体错误码请看文档 + */ + WxMaUserPortrait getUserPortrait(Date beginDate, Date endDate) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCloudService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCloudService.java new file mode 100644 index 0000000000..8f48bef834 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCloudService.java @@ -0,0 +1,392 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.cloud.*; +import com.google.gson.JsonArray; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; +import java.util.Map; + +/** + * 云开发相关接口. + * + * @author Binary Wang + * @date 2020-01-22 + */ +public interface WxMaCloudService { + String INVOKE_CLOUD_FUNCTION_URL = "https://api.weixin.qq.com/tcb/invokecloudfunction?env=%s&name=%s"; + String DATABASE_COLLECTION_GET_URL = "https://api.weixin.qq.com/tcb/databasecollectionget"; + String DATABASE_COLLECTION_DELETE_URL = "https://api.weixin.qq.com/tcb/databasecollectiondelete"; + String DATABASE_COLLECTION_ADD_URL = "https://api.weixin.qq.com/tcb/databasecollectionadd"; + String GET_QCLOUD_TOKEN_URL = "https://api.weixin.qq.com/tcb/getqcloudtoken"; + String BATCH_DELETE_FILE_URL = "https://api.weixin.qq.com/tcb/batchdeletefile"; + String BATCH_DOWNLOAD_FILE_URL = "https://api.weixin.qq.com/tcb/batchdownloadfile"; + String UPLOAD_FILE_URL = "https://api.weixin.qq.com/tcb/uploadfile"; + String DATABASE_MIGRATE_QUERY_INFO_URL = "https://api.weixin.qq.com/tcb/databasemigratequeryinfo"; + String DATABASE_MIGRATE_EXPORT_URL = "https://api.weixin.qq.com/tcb/databasemigrateexport"; + String DATABASE_MIGRATE_IMPORT_URL = "https://api.weixin.qq.com/tcb/databasemigrateimport"; + String UPDATE_INDEX_URL = "https://api.weixin.qq.com/tcb/updateindex"; + String DATABASE_COUNT_URL = "https://api.weixin.qq.com/tcb/databasecount"; + String DATABASE_AGGREGATE_URL = "https://api.weixin.qq.com/tcb/databaseaggregate"; + String DATABASE_QUERY_URL = "https://api.weixin.qq.com/tcb/databasequery"; + String DATABASE_UPDATE_URL = "https://api.weixin.qq.com/tcb/databaseupdate"; + String DATABASE_DELETE_URL = "https://api.weixin.qq.com/tcb/databasedelete"; + String DATABASE_ADD_URL = "https://api.weixin.qq.com/tcb/databaseadd"; + + String invokeCloudFunction(String name, String body) throws WxErrorException; + + /** + *
    +   * 触发云函数。注意:HTTP API 途径触发云函数不包含用户信息。
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/functions/invokeCloudFunction.html
    +   *
    +   * 请求地址
    +   * POST https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=ACCESS_TOKEN&env=ENV&name=FUNCTION_NAME
    +   *
    +   * 
    + * + * @param env string 是 云开发环境ID + * @param name string 是 云函数名称 + * @param body string 是 云函数的传入参数,具体结构由开发者定义。 + * @return resp_data string 云函数返回的buffer + * @throws WxErrorException . + */ + String invokeCloudFunction(String env, String name, String body) throws WxErrorException; + + List add(String collection, List list) throws WxErrorException; + + String add(String collection, Object obj) throws WxErrorException; + + JsonArray databaseAdd(String query) throws WxErrorException; + + /** + *
    +   * 数据库插入记录
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseAdd.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databaseadd?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param query 数据库操作语句 + * @return 插入成功的数据集合主键_id + * @throws WxErrorException . + */ + JsonArray databaseAdd(String env, String query) throws WxErrorException; + + Integer delete(String collection, String whereJson) throws WxErrorException; + + int databaseDelete(String query) throws WxErrorException; + + /** + *
    +   * 数据库删除记录
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseDelete.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databasedelete?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param query 数据库操作语句 + * @return 删除记录数量 + * @throws WxErrorException . + */ + int databaseDelete(String env, String query) throws WxErrorException; + + WxCloudDatabaseUpdateResult update(String collection, String whereJson, String updateJson) throws WxErrorException; + + WxCloudDatabaseUpdateResult databaseUpdate(String query) throws WxErrorException; + + /** + *
    +   * 数据库更新记录
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseUpdate.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databaseupdate?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param query 数据库操作语句 + * @return . + * @throws WxErrorException . + */ + WxCloudDatabaseUpdateResult databaseUpdate(String env, String query) throws WxErrorException; + + /** + * db.collection('geo') + * .where({ + * price: _.gt(10) + * }) + * .orderBy('_id', 'asc') + * .orderBy('price', 'desc') + * .skip(1) + * .limit(10) + * .get() + * @param collection + * @param whereJson + * @param orderBy + * @param skip + * @param limit + * @return + * @throws WxErrorException + */ + WxCloudDatabaseQueryResult query(String collection, String whereJson, Map orderBy, + Integer skip, Integer limit) throws WxErrorException; + + WxCloudDatabaseQueryResult databaseQuery(String query) throws WxErrorException; + + /** + *
    +   * 数据库查询记录
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseQuery.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databasequery?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param query 数据库操作语句 + * @return . + * @throws WxErrorException . + */ + WxCloudDatabaseQueryResult databaseQuery(String env, String query) throws WxErrorException; + + JsonArray databaseAggregate(String query) throws WxErrorException; + + /** + *
    +   * 数据库聚合记录
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseAggregate.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databaseaggregate?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param query 数据库操作语句 + * @return . + * @throws WxErrorException . + */ + JsonArray databaseAggregate(String env, String query) throws WxErrorException; + + Long count(String collection, String whereJson) throws WxErrorException; + + Long databaseCount(String query) throws WxErrorException; + + /** + *
    +   * 统计集合记录数或统计查询语句对应的结果记录数
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCount.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databasecount?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param query 数据库操作语句 + * @return 记录数量 + * @throws WxErrorException . + */ + Long databaseCount(String env, String query) throws WxErrorException; + + void updateIndex(String collectionName, List createIndexes, + List dropIndexNames) throws WxErrorException; + + /** + *
    +   * 变更数据库索引
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/updateIndex.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/updateindex?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param collectionName 集合名称 + * @param createIndexes 新增索引对象 + * @param dropIndexNames 要删除的索引的名字 + * @throws WxErrorException . + */ + void updateIndex(String env, String collectionName, List createIndexes, + List dropIndexNames) throws WxErrorException; + + Long databaseMigrateImport(String collectionName, String filePath, int fileType, + boolean stopOnError, int conflictMode) throws WxErrorException; + + /** + *
    +   * 数据库导入
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateImport
    +   * .html
    +   * 请求地址: POST https://api.weixin.qq.com/tcb/databasemigrateimport?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param collectionName 导入collection名 + * @param filePath 导入文件路径(导入文件需先上传到同环境的存储中,可使用开发者工具或 HTTP API的上传文件 API上传) + * @param fileType 导入文件类型, 1 JSON, 2 CSV + * @param stopOnError 是否在遇到错误时停止导入 + * @param conflictMode 冲突处理模式 : 1 INSERT , 2 UPSERT + * @return jobId + * @throws WxErrorException . + */ + Long databaseMigrateImport(String env, String collectionName, String filePath, int fileType, boolean stopOnError, + int conflictMode) throws WxErrorException; + + Long databaseMigrateExport(String filePath, int fileType, String query) throws WxErrorException; + + /** + *
    +   * 数据库导出
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateExport
    +   * .html
    +   * 请求地址: POST https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param filePath 导出文件路径(文件会导出到同环境的云存储中,可使用获取下载链接 API 获取下载链接) + * @param fileType 导出文件类型, 1 JSON, 2 CSV + * @param query 导出条件 + * @return jobId + * @throws WxErrorException . + */ + Long databaseMigrateExport(String env, String filePath, int fileType, String query) throws WxErrorException; + + WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(Long jobId) throws WxErrorException; + + /** + *
    +   *   数据库迁移状态查询
    +   *
    +   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database
    +   *  /databaseMigrateQueryInfo.html
    +   *  请求地址:POST https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param jobId 迁移任务ID + * @return . + * @throws WxErrorException . + */ + WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(String env, Long jobId) throws WxErrorException; + + WxCloudUploadFileResult uploadFile(String path) throws WxErrorException; + + /** + *
    +   * 获取文件上传链接
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/uploadFile.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/uploadfile?access_token=ACCESS_TOKEN
    +   *
    +   * 
    + * + * @param env 云环境ID + * @param path 上传路径 + * @return 上传结果 + * @throws WxErrorException . + */ + WxCloudUploadFileResult uploadFile(String env, String path) throws WxErrorException; + + WxCloudBatchDownloadFileResult batchDownloadFile(String[] fileIds, long[] maxAges) throws WxErrorException; + + /** + *
    +   * 获取文件下载链接
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDownloadFile.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/batchdownloadfile?access_token=ACCESS_TOKEN
    +   *
    +   * 
    + * + * @param env 云环境ID + * @param fileIds 文件ID列表 + * @param maxAges 下载链接有效期列表,对应文件id列表 + * @return 下载链接信息 + * @throws WxErrorException . + */ + WxCloudBatchDownloadFileResult batchDownloadFile(String env, String[] fileIds, long[] maxAges) throws WxErrorException; + + WxCloudBatchDeleteFileResult batchDeleteFile(String[] fileIds) throws WxErrorException; + + /** + *
    +   * 删除文件
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDeleteFile.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/batchdeletefile?access_token=ACCESS_TOKEN
    +   *
    +   * 
    + * + * @param env 云环境ID + * @param fileIds 文件ID列表 + * @return 下载链接信息 + * @throws WxErrorException . + */ + WxCloudBatchDeleteFileResult batchDeleteFile(String env, String[] fileIds) throws WxErrorException; + + /** + *
    +   *  获取腾讯云API调用凭证
    +   *
    +   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/utils/getQcloudToken.html
    +   *  请求地址:POST https://api.weixin.qq.com/tcb/getqcloudtoken?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param lifeSpan 有效期(单位为秒,最大7200) + * @return . + * @throws WxErrorException . + */ + WxCloudGetQcloudTokenResult getQcloudToken(long lifeSpan) throws WxErrorException; + + void databaseCollectionAdd(String collectionName) throws WxErrorException; + + /** + *
    +   * 新增集合
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionAdd
    +   * .html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databasecollectionadd?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param collectionName 集合名称 + * @throws WxErrorException . + */ + void databaseCollectionAdd(String env, String collectionName) throws WxErrorException; + + void databaseCollectionDelete(String collectionName) throws WxErrorException; + + /** + *
    +   * 删除集合
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database
    +   * /databaseCollectionDelete.html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databasecollectionadd?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param collectionName 集合名称 + * @throws WxErrorException . + */ + void databaseCollectionDelete(String env, String collectionName) throws WxErrorException; + + WxCloudDatabaseCollectionGetResult databaseCollectionGet(Long limit, Long offset) throws WxErrorException; + + /** + *
    +   * 获取特定云环境下集合信息
    +   *
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionGet
    +   * .html
    +   * 请求地址:POST https://api.weixin.qq.com/tcb/databasecollectionget?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param env 云环境ID + * @param limit 获取数量限制,默认值:10 + * @param offset 偏移量,默认值:0 + * @return . + * @throws WxErrorException . + */ + WxCloudDatabaseCollectionGetResult databaseCollectionGet(String env, Long limit, Long offset) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java new file mode 100644 index 0000000000..ad102805da --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java @@ -0,0 +1,140 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.code.*; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + * 小程序代码管理相关 API(大部分只能是第三方平台调用) + * 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN + * + * @author Charming + * @since 2018-04-26 19:43 + */ +public interface WxMaCodeService { + /** + * 为授权的小程序帐号上传小程序代码. + */ + String COMMIT_URL = "https://api.weixin.qq.com/wxa/commit"; + String GET_QRCODE_URL = "https://api.weixin.qq.com/wxa/get_qrcode"; + String GET_CATEGORY_URL = "https://api.weixin.qq.com/wxa/get_category"; + String GET_PAGE_URL = "https://api.weixin.qq.com/wxa/get_page"; + String SUBMIT_AUDIT_URL = "https://api.weixin.qq.com/wxa/submit_audit"; + String GET_AUDIT_STATUS_URL = "https://api.weixin.qq.com/wxa/get_auditstatus"; + String GET_LATEST_AUDIT_STATUS_URL = "https://api.weixin.qq.com/wxa/get_latest_auditstatus"; + String RELEASE_URL = "https://api.weixin.qq.com/wxa/release"; + String CHANGE_VISIT_STATUS_URL = "https://api.weixin.qq.com/wxa/change_visitstatus"; + String REVERT_CODE_RELEASE_URL = "https://api.weixin.qq.com/wxa/revertcoderelease"; + String GET_SUPPORT_VERSION_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/getweappsupportversion"; + String SET_SUPPORT_VERSION_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/setweappsupportversion"; + String UNDO_CODE_AUDIT_URL = "https://api.weixin.qq.com/wxa/undocodeaudit"; + + /** + * 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台). + * + * @param commitRequest 参数 + * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 + */ + void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException; + + /** + * 获取体验小程序的体验二维码. + * 文档地址: + * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN + * + * @param path 指定体验版二维码跳转到某个具体页面(如果不需要的话,则不需要填path参数,可在路径后以“?参数”方式传入参数) + * 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1 + * @return 二维码 bytes + * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 + */ + byte[] getQrCode(String path) throws WxErrorException; + + /** + * 获取授权小程序帐号的可选类目. + * + * @return List + * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 + */ + List getCategory() throws WxErrorException; + + /** + * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用). + * + * @return page_list 页面配置列表 + * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 + */ + List getPage() throws WxErrorException; + + /** + * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用). + * + * @param auditRequest 提交审核参数 + * @return 审核编号 + * @throws WxErrorException 提交失败时返回,具体错误码请看此接口的注释文档 + */ + long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException; + + /** + * 查询某个指定版本的审核状态(仅供第三方代小程序调用). + * + * @param auditId 提交审核时获得的审核id + * @return 审核状态 + * @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档 + */ + WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException; + + /** + * 查询最新一次提交的审核状态(仅供第三方代小程序调用). + * + * @return 审核状态 + * @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档 + */ + WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException; + + /** + * 发布已通过审核的小程序(仅供第三方代小程序调用). + * + * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 + */ + void release() throws WxErrorException; + + /** + * 修改小程序线上代码的可见状态(仅供第三方代小程序调用). + * + * @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见 + * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 + */ + void changeVisitStatus(String action) throws WxErrorException; + + /** + * 小程序版本回退(仅供第三方代小程序调用). + * + * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 + */ + void revertCodeRelease() throws WxErrorException; + + /** + * 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用). + * + * @return 小程序版本分布信息 + * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 + */ + WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException; + + /** + * 设置最低基础库版本(仅供第三方代小程序调用). + * + * @param version 版本 + * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 + */ + void setSupportVersion(String version) throws WxErrorException; + + /** + * 小程序审核撤回. + * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次 + * + * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 + */ + void undoCodeAudit() throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaExpressService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaExpressService.java new file mode 100644 index 0000000000..b6f5e2d683 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaExpressService.java @@ -0,0 +1,203 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressAccount; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressDelivery; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressPath; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressPrinter; +import cn.binarywang.wx.miniapp.bean.express.request.*; +import cn.binarywang.wx.miniapp.bean.express.result.WxMaExpressOrderInfoResult; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + * 小程序物流助手 + * @author xiaoyu + * @since 2019-11-26 + */ +public interface WxMaExpressService { + /** + * 获取支持的快递公司列表 + */ + String ALL_DELIVERY_URL = "https://api.weixin.qq.com/cgi-bin/express/business/delivery/getall"; + + /** + * 获取所有绑定的物流账号 + */ + String ALL_ACCOUNT_URL = "https://api.weixin.qq.com/cgi-bin/express/business/account/getall"; + + /** + * 绑定、解绑物流账号 + */ + String BIND_ACCOUNT_URL = "https://api.weixin.qq.com/cgi-bin/express/business/account/bind"; + + /** + * 获取电子面单余额 + */ + String GET_QUOTA_URL = "https://api.weixin.qq.com/cgi-bin/express/business/quota/get"; + + /** + * 配置面单打印员 + */ + String UPDATE_PRINTER_URL = "https://api.weixin.qq.com/cgi-bin/express/business/printer/update"; + + /** + * 获取打印员 + */ + String GET_PRINTER_URL = "https://api.weixin.qq.com/cgi-bin/express/business/printer/getall"; + + /** + * 生成运单 + */ + String ADD_ORDER_URL = "https://api.weixin.qq.com/cgi-bin/express/business/order/add"; + + /** + * 批量获取运单数据 + */ + String BATCH_GET_ORDER_URL = "https://api.weixin.qq.com/cgi-bin/express/business/order/batchget"; + + /** + * 取消运单 + */ + String CANCEL_ORDER_URL = "https://api.weixin.qq.com/cgi-bin/express/business/order/cancel"; + + /** + * 获取运单数据 + */ + String GET_ORDER_URL = "https://api.weixin.qq.com/cgi-bin/express/business/order/get"; + + /** + * 查询运单轨迹 + */ + String GET_PATH_URL = "https://api.weixin.qq.com/cgi-bin/express/business/path/get"; + + /** + * 模拟快递公司更新订单状态 + */ + String TEST_UPDATE_ORDER_URL = "https://api.weixin.qq.com/cgi-bin/express/business/test_update_order"; + + /** + * 获取支持的快递公司列表 + * @return 快递公司列表 + * @throws WxErrorException 获取失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + List getAllDelivery() throws WxErrorException; + + /** + * 获取所有绑定的物流账号 + * @return 物流账号list + * @throws WxErrorException 获取失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + List getAllAccount() throws WxErrorException; + + /** + * 绑定、解绑物流账号 + * @param wxMaExpressBindAccountRequest 物流账号对象 + * @throws WxErrorException 请求失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + void bindAccount(WxMaExpressBindAccountRequest wxMaExpressBindAccountRequest) throws WxErrorException; + + /** + * 获取电子面单余额。仅在使用加盟类快递公司时,才可以调用。 + * @param wxMaExpressBindAccountRequest 物流账号对象 + * @return 电子面单余额 + * @throws WxErrorException 获取失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + Integer getQuota(WxMaExpressBindAccountRequest wxMaExpressBindAccountRequest) throws WxErrorException; + + /** + * 配置面单打印员,可以设置多个,若需要使用微信打单 PC 软件,才需要调用。 + * @param wxMaExpressPrinterUpdateRequest 面单打印员对象 + * @throws WxErrorException 请求失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + void updatePrinter(WxMaExpressPrinterUpdateRequest wxMaExpressPrinterUpdateRequest) throws WxErrorException; + + /** + * 获取打印员。若需要使用微信打单 PC 软件,才需要调用 + * @return 打印员 + * @throws WxErrorException 获取失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + WxMaExpressPrinter getPrinter() throws WxErrorException; + + /** + * 生成运单 + * @param wxMaExpressAddOrderRequest 生成运单请求对象 + * @return 生成运单结果 + * @throws WxErrorException 请求失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + WxMaExpressOrderInfoResult addOrder(WxMaExpressAddOrderRequest wxMaExpressAddOrderRequest) throws WxErrorException; + + /** + * 批量获取运单数据 + * @param requests 获取运单请求对象集合,最多不能超过1000个 + * @return 运单信息集合 + * @throws WxErrorException 获取失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + List batchGetOrder(List requests) throws WxErrorException; + + /** + * 取消运单 + * @param wxMaExpressGetOrderRequest 运单信息请求对象 + * @throws WxErrorException 取消失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + void cancelOrder(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException; + + /** + * 获取运单数据 + * @param wxMaExpressGetOrderRequest 运单信息请求对象 + * @return 运单信息 + * @throws WxErrorException 获取失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + WxMaExpressOrderInfoResult getOrder(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException; + + /** + * 查询运单轨迹 + * @param wxMaExpressGetOrderRequest 运单信息请求对象 + * @return 运单轨迹对象 + * @throws WxErrorException 查询失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + WxMaExpressPath getPath(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException; + + /** + * 模拟快递公司更新订单状态, 该接口只能用户测试 + * @param wxMaExpressTestUpdateOrderRequest 模拟快递公司更新订单状态请求对象 + * @throws WxErrorException 模拟更新订单状态失败时返回 + *
    +   *   查看文档
    +   * 
    + */ + void testUpdateOrder(WxMaExpressTestUpdateOrderRequest wxMaExpressTestUpdateOrderRequest) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaJsapiService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaJsapiService.java new file mode 100644 index 0000000000..f81b7c6ce7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaJsapiService.java @@ -0,0 +1,67 @@ +package cn.binarywang.wx.miniapp.api; + +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + *
    + *  jsapi相关接口
    + *  Created by BinaryWang on 2018/8/5.
    + * 
    + * + * @author Binary Wang + */ +public interface WxMaJsapiService { + /** + * 获得jsapi_ticket的url + */ + String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"; + + /** + * 获得卡券api_ticket,不强制刷新api_ticket + * + * @see #getJsapiTicket(boolean) + */ + String getCardApiTicket() throws WxErrorException; + + /** + *
    +   * 获得卡券api_ticket
    +   * 获得时会检查apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    +   *
    +   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
    +   * 
    + * + * @param forceRefresh 强制刷新 + */ + String getCardApiTicket(boolean forceRefresh) throws WxErrorException; + + /** + * 获得jsapi_ticket,不强制刷新jsapi_ticket + * + * @see #getJsapiTicket(boolean) + */ + String getJsapiTicket() throws WxErrorException; + + /** + *
    +   * 获得jsapi_ticket
    +   * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    +   *
    +   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
    +   * 
    + * + * @param forceRefresh 强制刷新 + */ + String getJsapiTicket(boolean forceRefresh) throws WxErrorException; + + /** + *
    +   * 创建调用jsapi时所需要的签名
    +   *
    +   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
    +   * 
    + */ + WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveGoodsService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveGoodsService.java new file mode 100644 index 0000000000..f52f9efde2 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveGoodsService.java @@ -0,0 +1,123 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; +import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + *
    + * 直播间商品相关操作接口
    + * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/liveplayer/commodity-api.html
    + * Created by lipengjun on 2020/6/29.
    + * 
    + * + * @author lipengjun (939961241@qq.com) + */ +public interface WxMaLiveGoodsService { + String ADD_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/add"; + String RESET_AUDIT_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/resetaudit"; + String AUDIT_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/audit"; + String DELETE_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/delete"; + String UPDATE_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/update"; + String GET_GOODS_WARE_HOUSE = "https://api.weixin.qq.com/wxa/business/getgoodswarehouse"; + String GET_APPROVED_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved"; + + /** + * 商品添加并提审 + *
    +   * 调用此接口上传并提审需要直播的商品信息,审核通过后商品录入【小程序直播】商品库
    +   * 注意:开发者必须保存【商品ID】与【审核单ID】,如果丢失,则无法调用其他相关接口
    +   * 调用额度:500次/一天
    +   * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/add?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param goods 商品 + * @return 返回auditId、goodsId + * @throws WxErrorException . + */ + WxMaLiveResult addGoods(WxMaLiveInfo.Goods goods) throws WxErrorException; + + /** + * 撤回审核 + *
    +   * 调用此接口,可撤回直播商品的提审申请,消耗的提审次数不返还
    +   * 调用额度:500次/一天
    +   * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/resetaudit?access_token=ACCESS_TOKEN
    +   * 
    +   * @param auditId 审核单ID
    +   * @param goodsId 商品ID
    +   * @return 撤回审核是否成功
    +   * @throws WxErrorException .
    +   */
    +  boolean resetAudit(Integer auditId, Integer goodsId) throws WxErrorException;
    +
    +  /**
    +   * 重新提交审核
    +   * 
    +   * 调用此接口,可撤回直播商品的提审申请,消耗的提审次数不返还
    +   * 调用额度:500次/一天(与接口'商品添加并提审'共用500次限制)
    +   * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/audit?access_token=ACCESS_TOKEN
    +   * 
    +   * @param goodsId 商品ID
    +   * @return 审核单ID
    +   * @throws WxErrorException .
    +   */
    +  String auditGoods(Integer goodsId) throws WxErrorException;
    +
    +  /**
    +   * 删除商品
    +   * 
    +   * 调用此接口,可删除【小程序直播】商品库中的商品,删除后直播间上架的该商品也将被同步删除,不可恢复;
    +   * 调用额度:1000次/一天
    +   * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/delete?access_token=ACCESS_TOKEN
    +   * 
    +   * @param goodsId 商品ID
    +   * @return 删除商品是否成功
    +   * @throws WxErrorException .
    +   */
    +  boolean deleteGoods(Integer goodsId) throws WxErrorException;
    +
    +  /**
    +   * 更新商品
    +   * 
    +   * 调用此接口可以更新商品信息,审核通过的商品仅允许更新价格类型与价格,审核中的商品不允许更新,未审核的商品允许更新所有字段, 只传入需要更新的字段。
    +   * 调用额度:1000次/一天
    +   * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/update?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param goods 商品 + * @return 更新商品是否成功 + * @throws WxErrorException . + */ + boolean updateGoods(WxMaLiveInfo.Goods goods) throws WxErrorException; + + /** + * 获取商品状态 + *
    +   * 调用此接口可获取商品的信息与审核状态
    +   * 调用额度:1000次/一天
    +   * http请求方式:POST https://api.weixin.qq.com/wxa/business/getgoodswarehouse?access_token=ACCESS_TOKEN
    +   * 
    +   * @param goodsIds 商品ID集
    +   * @return 商品状态信息
    +   * @throws WxErrorException .
    +   */
    +  WxMaLiveResult getGoodsWareHouse(List goodsIds) throws WxErrorException;
    +
    +  /**
    +   * 获取商品列表
    +   * 
    +   * 调用此接口可获取商品列表
    +   * 调用额度:10000次/一天
    +   * http请求方式:GET https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved?access_token=ACCESS_TOKEN
    +   * 
    +   * @param offset 分页条数起点
    +   * @param limit  分页大小,默认30,不超过100
    +   * @param status 商品状态,0:未审核。1:审核中,2:审核通过,3:审核驳回
    +   * @return 商品列表
    +   * @throws WxErrorException .
    +   */
    +  WxMaLiveResult getApprovedGoods(Integer offset, Integer limit, Integer status) throws WxErrorException;
    +}
    diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveService.java
    new file mode 100644
    index 0000000000..185085478e
    --- /dev/null
    +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveService.java
    @@ -0,0 +1,94 @@
    +package cn.binarywang.wx.miniapp.api;
    +
    +import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo;
    +import cn.binarywang.wx.miniapp.bean.WxMaLiveResult;
    +import me.chanjar.weixin.common.error.WxErrorException;
    +
    +import java.util.List;
    +
    +/**
    + * 
    + *  直播相关操作接口.
    + *  Created by yjwang on 2020/4/5.
    + * 
    + * + * @author yjwang + */ +public interface WxMaLiveService { + String GET_LIVE_INFO = "https://api.weixin.qq.com/wxa/business/getliveinfo"; + String CREATE_ROOM = "https://api.weixin.qq.com/wxaapi/broadcast/room/create"; + String ADD_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/room/addgoods"; + + /** + * 创建直播间 + *
    +   * 调用此接口创建直播间,创建成功后将在直播间列表展示,调用额度:10000次/一天
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/liveplayer/studio-api.html#1
    +   * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/create?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param roomInfo 直播间信息 + * @return . + * @throws WxErrorException . + */ + Integer createRoom(WxMaLiveInfo.RoomInfo roomInfo) throws WxErrorException; + + /** + * 获取直播房间列表.(分页) + * + * @param start 起始拉取房间,start = 0 表示从第 1 个房间开始拉取 + * @param limit 每次拉取的个数上限,不要设置过大,建议 100 以内 + * @return . + * @throws WxErrorException . + */ + WxMaLiveResult getLiveInfo(Integer start, Integer limit) throws WxErrorException; + + /** + * 获取所有直播间信息(没有分页直接获取全部) + * + * @return . + * @throws WxErrorException . + */ + List getLiveInfos() throws WxErrorException; + + /** + * 获取直播房间回放数据信息. + * + * @param action 获取回放 + * @param roomId 直播间 id + * @param start 起始拉取视频,start = 0 表示从第 1 个视频片段开始拉取 + * @param limit 每次拉取的个数上限,不要设置过大,建议 100 以内 + * @return . + * @throws WxErrorException . + */ + WxMaLiveResult getLiveReplay(String action, Integer roomId, Integer start, Integer limit) throws WxErrorException; + + /** + * 获取直播房间回放数据信息. + *

    + * 获取回放 (默认:get_replay) + * + * @param roomId 直播间 id + * @param start 起始拉取视频,start = 0 表示从第 1 个视频片段开始拉取 + * @param limit 每次拉取的个数上限,不要设置过大,建议 100 以内 + * @return . + * @throws WxErrorException . + */ + WxMaLiveResult getLiveReplay(Integer roomId, Integer start, Integer limit) throws WxErrorException; + + /** + * 直播间导入商品 + *

    + * 调用接口往指定直播间导入已入库的商品 + * 调用频率 + * 调用额度:10000次/一天 + *

    + * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/addgoods?access_token=ACCESS_TOKEN + *

    +   * @param roomId 房间ID
    +   * @param goodsIds 数组列表,可传入多个,里面填写 商品 ID
    +   * @return 导入商品是否成功
    +   * @throws WxErrorException .
    +   */
    +  boolean addGoodsToRoom(Integer roomId, List goodsIds) throws WxErrorException;
    +}
    diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java
    new file mode 100644
    index 0000000000..48ebb8a101
    --- /dev/null
    +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java
    @@ -0,0 +1,70 @@
    +package cn.binarywang.wx.miniapp.api;
    +
    +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
    +import me.chanjar.weixin.common.error.WxErrorException;
    +
    +import java.io.File;
    +import java.io.InputStream;
    +
    +/**
    + * 
    + * 临时素材接口
    + * Created by Binary Wang on 2016/7/21.
    + * 
    + * + * @author Binary Wang + */ +public interface WxMaMediaService { + String MEDIA_UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/upload?type=%s"; + String MEDIA_GET_URL = "https://api.weixin.qq.com/cgi-bin/media/get"; + + /** + *
    +   * 新增临时素材
    +   * 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
    +   * 详情请见: 新增临时素材
    +   * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
    +   * 
    + * + * @param mediaType 媒体类型, + * @param file 文件对象 + * @return the wx media upload result + * @throws WxErrorException the wx error exception + * @see #uploadMedia(String, String, InputStream) #uploadMedia(String, String, InputStream) + */ + WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException; + + /** + *
    +   * 新增临时素材
    +   * 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
    +   *
    +   * 详情请见: 新增临时素材
    +   * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
    +   * 
    + * + * @param mediaType 媒体类型 + * @param fileType 文件类型 + * @param inputStream 输入流 + * @return the wx media upload result + * @throws WxErrorException the wx error exception + * @see #uploadMedia(java.lang.String, java.io.File) #uploadMedia(java.lang.String, java.io.File) + */ + WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException; + + /** + *
    +   * 获取临时素材
    +   * 小程序可以使用本接口获取客服消息内的临时素材(即下载临时的多媒体文件)。目前小程序仅支持下载图片文件。
    +   *
    +   * 详情请见: 获取临时素材
    +   * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
    +   * 
    + * + * @param mediaId 媒体Id + * @return 保存到本地的临时文件 media + * @throws WxErrorException the wx error exception + */ + File getMedia(String mediaId) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java new file mode 100644 index 0000000000..f166a2b248 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java @@ -0,0 +1,85 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.*; +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + *
    + * 消息发送接口
    + * 
    + * + * @author Binary Wang + */ +public interface WxMaMsgService { + String KEFU_MESSAGE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; + String TEMPLATE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send"; + String SUBSCRIBE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"; + String UNIFORM_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send"; + String ACTIVITY_ID_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/activityid/create"; + String UPDATABLE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/updatablemsg/send"; + + /** + *
    +   * 发送客服消息
    +   * 详情请见: 发送客服消息
    +   * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param message 客服消息 + * @return . + * @throws WxErrorException . + */ + boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException; + + /** + *
    +   * 发送订阅消息
    +   * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
    +   * 
    + * + * @param subscribeMessage 订阅消息 + * @throws WxErrorException . + */ + void sendSubscribeMsg(WxMaSubscribeMessage subscribeMessage) throws WxErrorException; + + /** + *
    +   * 下发小程序和公众号统一的服务消息
    +   * 详情请见: 下发小程序和公众号统一的服务消息
    +   * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param uniformMessage 消息 + * @throws WxErrorException . + */ + void sendUniformMsg(WxMaUniformMessage uniformMessage) throws WxErrorException; + + /** + *
    +   *  创建被分享动态消息的 activity_id.
    +   *  动态消息: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share/updatable-message.html
    +   *
    +   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.createActivityId.html
    +   *  接口地址:GET https://api.weixin.qq.com/cgi-bin/message/wxopen/activityid/create?access_token=ACCESS_TOKEN
    +   * 
    + * + * @return . + * @throws WxErrorException . + */ + JsonObject createUpdatableMessageActivityId() throws WxErrorException; + + /** + *
    +   *  修改被分享的动态消息.
    +   *  动态消息: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share/updatable-message.html
    +   *
    +   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.setUpdatableMsg.html
    +   *  接口地址:POST https://api.weixin.qq.com/cgi-bin/message/wxopen/activityid/create?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param msg 动态消息 + * @throws WxErrorException . + */ + void setUpdatableMsg(WxMaUpdatableMsg msg) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaPluginService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaPluginService.java new file mode 100644 index 0000000000..83e2b1c5aa --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaPluginService.java @@ -0,0 +1,50 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaPluginListResult; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * 小程序插件管理 API + *

    + * 详情请见:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/plugin-management/pluginManager.applyPlugin.html + * 或者:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Plug-ins_Management.html + */ +public interface WxMaPluginService { + + String PLUGIN_URL = "https://api.weixin.qq.com/wxa/plugin"; + + /** + * 向插件开发者发起使用插件的申请 + * + * @param pluginAppId 插件 appId + * @param reason 申请使用理由 + * @throws WxErrorException 异常 + */ + void applyPlugin(String pluginAppId, String reason) throws WxErrorException; + + /** + * 查询已添加的插件 + * + * @return + * @throws WxErrorException + */ + WxMaPluginListResult getPluginList() throws WxErrorException; + + /** + * 删除已添加的插件 + * + * @param pluginAppId 插件 appId + * @throws WxErrorException + */ + void unbindPlugin(String pluginAppId) throws WxErrorException; + + /** + * 快速更新插件版本号(第三方平台代小程序管理插件) + * + * @param pluginAppId 插件 appid + * @param userVersion 升级至版本号,要求此插件版本支持快速更新 + * @throws WxErrorException + */ + void updatePlugin(String pluginAppId, String userVersion) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java new file mode 100644 index 0000000000..f2c9e8f1dc --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java @@ -0,0 +1,179 @@ +package cn.binarywang.wx.miniapp.api; + +import java.io.File; + +import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + *

    + * 二维码相关操作接口.
    + *
    + * 接口A(createWxaCode)加上接口C(createQrcode),总共生成的码数量限制为100,000,请谨慎调用。
    + *
    + * 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/qrcode.html
    + * 
    + * + * @author Binary Wang + */ +public interface WxMaQrcodeService { + String CREATE_QRCODE_URL = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode"; + String GET_WXACODE_URL = "https://api.weixin.qq.com/wxa/getwxacode"; + String GET_WXACODE_UNLIMIT_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit"; + + /** + * 接口C: 获取小程序页面二维码. + *
    +   * 适用于需要的码数量较少的业务场景
    +   * 通过该接口,仅能生成已发布的小程序的二维码。
    +   * 可以在开发者工具预览时生成开发版的带参二维码。
    +   * 带参二维码只有 100000 个,请谨慎调用。
    +   * 
    + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @return 文件内容字节数组 + * @throws WxErrorException 异常 + */ + byte[] createQrcodeBytes(String path, int width) throws WxErrorException; + + /** + * 接口C: 获取小程序页面二维码. + *
    +   * 适用于需要的码数量较少的业务场景
    +   * 通过该接口,仅能生成已发布的小程序的二维码。
    +   * 可以在开发者工具预览时生成开发版的带参二维码。
    +   * 带参二维码只有 100000 个,请谨慎调用。
    +   * 
    + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @return 文件对象 + * @throws WxErrorException 异常 + */ + File createQrcode(String path, int width) throws WxErrorException; + + /** + * 接口C: 获取小程序页面二维码. + *
    +   * 适用于需要的码数量较少的业务场景
    +   * 通过该接口,仅能生成已发布的小程序的二维码。
    +   * 可以在开发者工具预览时生成开发版的带参二维码。
    +   * 带参二维码只有 100000 个,请谨慎调用。
    +   * 
    + * + * @param path 不能为空,最大长度 128 字节 + * @return 文件对象 + * @throws WxErrorException 异常 + */ + File createQrcode(String path) throws WxErrorException; + + /** + * 接口A: 获取小程序码. + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 + * @return 文件内容字节数组 + * @throws WxErrorException 异常 + */ + byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException; + + /** + * 接口A: 获取小程序码. + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 + * @return 文件对象 + * @throws WxErrorException 异常 + */ + File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException; + + /** + * 接口A: 获取小程序码. + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @return 文件对象 + * @throws WxErrorException 异常 + */ + File createWxaCode(String path, int width) throws WxErrorException; + + /** + * 接口A: 获取小程序码. + * + * @param path 不能为空,最大长度 128 字节 + * @return 文件对象 + * @throws WxErrorException 异常 + */ + File createWxaCode(String path) throws WxErrorException; + + /** + * 接口B: 获取小程序码(永久有效、数量暂无限制). + *
    +   * 通过该接口生成的小程序码,永久有效,数量暂无限制。
    +   * 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。
    +   * 使用如下代码可以获取到二维码中的 scene 字段的值。
    +   * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode
    +   * 
    + * + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @param width 默认false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @return 文件内容字节数组 + * @throws WxErrorException 异常 + */ + byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; + + /** + * 接口B: 获取小程序码(永久有效、数量暂无限制). + *
    +   * 通过该接口生成的小程序码,永久有效,数量暂无限制。
    +   * 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。
    +   * 使用如下代码可以获取到二维码中的 scene 字段的值。
    +   * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode
    +   * 
    + * + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @param width 默认false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @return 文件对象 + * @throws WxErrorException 异常 + */ + File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; + + /** + * 接口B: 获取小程序码(永久有效、数量暂无限制). + *
    +   * 通过该接口生成的小程序码,永久有效,数量暂无限制。
    +   * 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。
    +   * 使用如下代码可以获取到二维码中的 scene 字段的值。
    +   * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode
    +   * 
    + * + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @return 文件对象 + * @throws WxErrorException 异常 + */ + File createWxaCodeUnlimit(String scene, String page) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaRunService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaRunService.java new file mode 100644 index 0000000000..fe764d69a5 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaRunService.java @@ -0,0 +1,24 @@ +package cn.binarywang.wx.miniapp.api; + +import java.util.List; + +import cn.binarywang.wx.miniapp.bean.WxMaRunStepInfo; + +/** + * 微信运动相关操作接口. + * + * @author Binary Wang + */ +public interface WxMaRunService { + + /** + * 解密分享敏感数据. + * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/werun/wx.getWeRunData.html + * + * @param sessionKey 会话密钥 + * @param encryptedData 消息密文 + * @param ivStr 加密算法的初始向量 + */ + List getRunStepInfo(String sessionKey, String encryptedData, String ivStr); + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java new file mode 100644 index 0000000000..7733b77ed8 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java @@ -0,0 +1,74 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaMediaAsyncCheckResult; +import java.io.File; + +import me.chanjar.weixin.common.error.WxErrorException; + +/** + *
    + * 内容安全相关接口.
    + * Created by Binary Wang on 2018/11/24.
    + * 
    + * + * @author Binary Wang + */ +public interface WxMaSecCheckService { + + String IMG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/img_sec_check"; + + String MSG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/msg_sec_check"; + + String MEDIA_CHECK_ASYNC_URL = "https://api.weixin.qq.com/wxa/media_check_async"; + + /** + *
    +   * 校验一张图片是否含有违法违规内容.
    +   * 应用场景举例:
    +   * 1)图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等;
    +   * 2)敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。频率限制:单个 appId 调用上限为 1000 次/分钟,100,000 次/天
    +   * 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/imgSecCheck.html
    +   * 
    + */ + boolean checkImage(File file) throws WxErrorException; + + /** + * 校验一张图片是否含有违法违规内容 + * @param fileUrl 文件网络地址 + * @return 是否违规 + * @throws WxErrorException . + */ + boolean checkImage(String fileUrl) throws WxErrorException; + + /** + *
    +   * 检查一段文本是否含有违法违规内容。
    +   * 应用场景举例:
    +   * 用户个人资料违规文字检测;
    +   * 媒体新闻类用户发表文章,评论内容检测;
    +   * 游戏类用户编辑上传的素材(如答题类小游戏用户上传的问题及答案)检测等。 频率限制:单个 appId 调用上限为 4000 次/分钟,2,000,000 次/天*
    +   * 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/msgSecCheck.html
    +   * 
    + */ + boolean checkMessage(String msgString) throws WxErrorException; + + + /** + *
    +   * 异步校验图片/音频是否含有违法违规内容。
    +   * 应用场景举例:
    +   * 语音风险识别:社交类用户发表的语音内容检测;
    +   * 图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等;
    +   * 敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。
    +   * 频率限制:
    +   * 单个 appId 调用上限为 2000 次/分钟,200,000 次/天;文件大小限制:单个文件大小不超过10M
    +   * 详情请见:
    +   * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.mediaCheckAsync.html
    +   * 
    + * @param mediaUrl 要检测的多媒体url + * @param mediaType 媒体类型,{@link cn.binarywang.wx.miniapp.constant.WxMaConstants.SecCheckMediaType} + * @return + */ + WxMaMediaAsyncCheckResult mediaCheckAsync(String mediaUrl,int mediaType) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java new file mode 100644 index 0000000000..13862b12fd --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java @@ -0,0 +1,349 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import me.chanjar.weixin.common.api.WxImgProcService; +import me.chanjar.weixin.common.api.WxOcrService; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.service.WxService; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; + +import java.util.Map; + +/** + * @author Binary Wang + */ +public interface WxMaService extends WxService { + /** + * 获取access_token. + */ + String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; + + String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; + /** + * getPaidUnionId + */ + String GET_PAID_UNION_ID_URL = "https://api.weixin.qq.com/wxa/getpaidunionid"; + + /** + * 导入抽样数据 + */ + String SET_DYNAMIC_DATA_URL = "https://api.weixin.qq.com/wxa/setdynamicdata"; + + /** + * 获取登录后的session信息. + * + * @param jsCode 登录时获取的 code + */ + WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException; + + /** + * 导入抽样数据 + *
    +   * 第三方通过调用微信API,将数据写入到setdynamicdata这个API。每个Post数据包不超过5K,若数据过多可开多进(线)程并发导入数据(例如:数据量为十万量级可以开50个线程并行导数据)。
    +   * 文档地址:https://wsad.weixin.qq.com/wsad/zh_CN/htmledition/widget-docs-v3/html/custom/quickstart/implement/import/index.html
    +   * http请求方式:POST http(s)://api.weixin.qq.com/wxa/setdynamicdata?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param data 推送到微信后台的数据列表,该数据被微信用于流量分配,注意该字段为string类型而不是object + * @param lifespan 数据有效时间,秒为单位,一般为86400,一天一次导入的频率 + * @param scene 1代表用于搜索的数据 + * @param type 用于标识数据所属的服务类目 + * @throws WxErrorException . + */ + void setDynamicData(int lifespan, String type, int scene, String data) throws WxErrorException; + + /** + *
    +   * 验证消息的确来自微信服务器.
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
    +   * 
    + */ + boolean checkSignature(String timestamp, String nonce, String signature); + + /** + * 获取access_token, 不强制刷新access_token. + * + * @see #getAccessToken(boolean) + */ + String getAccessToken() throws WxErrorException; + + /** + *
    +   * 获取access_token,本方法线程安全.
    +   * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
    +   *
    +   * 另:本service的所有方法都会在access_token过期是调用此方法
    +   *
    +   * 程序员在非必要情况下尽量不要主动调用此方法
    +   *
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN
    +   * 
    + * + * @param forceRefresh 强制刷新 + */ + String getAccessToken(boolean forceRefresh) throws WxErrorException; + + /** + *
    +   * 用户支付完成后,获取该用户的 UnionId,无需用户授权。本接口支持第三方平台代理查询。
    +   *
    +   * 注意:调用前需要用户完成支付,且在支付后的五分钟内有效。
    +   * 请求地址: GET https://api.weixin.qq.com/wxa/getpaidunionid?access_token=ACCESS_TOKEN&openid=OPENID
    +   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/getPaidUnionId.html
    +   * 
    + * + * @param openid 必填 支付用户唯一标识 + * @param transactionId 非必填 微信支付订单号 + * @param mchId 非必填 微信支付分配的商户号,和商户订单号配合使用 + * @param outTradeNo 非必填 微信支付商户订单号,和商户号配合使用 + * @return UnionId. + * @throws WxErrorException . + */ + String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) throws WxErrorException; + + /** + *
    +   * Service没有实现某个API的时候,可以用这个,
    +   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
    +   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
    +   * 
    + * + * @param . + * @param . + * @param data 参数或请求数据 + * @param executor 执行器 + * @param uri 接口请求地址 + * @return . + */ + T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; + + /** + *
    +   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试.
    +   * 默认:1000ms
    +   * 
    + * + * @param retrySleepMillis 重试等待毫秒数 + */ + void setRetrySleepMillis(int retrySleepMillis); + + /** + *
    +   * 设置当微信系统响应系统繁忙时,最大重试次数.
    +   * 默认:5次
    +   * 
    + * + * @param maxRetryTimes 最大重试次数 + */ + void setMaxRetryTimes(int maxRetryTimes); + + /** + * 获取WxMaConfig 对象. + * + * @return WxMaConfig + */ + WxMaConfig getWxMaConfig(); + + /** + * 注入 {@link WxMaConfig} 的实现. + * + * @param maConfig config + */ + void setWxMaConfig(WxMaConfig maConfig); + + /** + * Map里 加入新的 {@link WxMaConfig},适用于动态添加新的微信公众号配置. + * + * @param miniappId 小程序标识 + * @param configStorage 新的微信配置 + */ + void addConfig(String miniappId, WxMaConfig configStorage); + + /** + * 从 Map中 移除 {@link String miniappId} 所对应的 {@link WxMaConfig},适用于动态移除小程序配置. + * + * @param miniappId 对应小程序的标识 + */ + void removeConfig(String miniappId); + + /** + * 注入多个 {@link WxMaConfig} 的实现. 并为每个 {@link WxMaConfig} 赋予不同的 {@link String mpId} 值 + * 随机采用一个{@link String mpId}进行Http初始化操作 + * + * @param configs WxMaConfig map + */ + void setMultiConfigs(Map configs); + + /** + * 注入多个 {@link WxMaConfig} 的实现. 并为每个 {@link WxMaConfig} 赋予不同的 {@link String label} 值 + * + * @param configs WxMaConfig map + * @param defaultMiniappId 设置一个{@link WxMaConfig} 所对应的{@link String defaultMiniappId}进行Http初始化 + */ + void setMultiConfigs(Map configs, String defaultMiniappId); + + /** + * 进行相应的公众号切换. + * + * @param mpId 公众号标识 + * @return 切换是否成功 + */ + boolean switchover(String mpId); + + /** + * 进行相应的公众号切换. + * + * @param miniappId 小程序标识 + * @return 切换成功,则返回当前对象,方便链式调用,否则抛出异常 + */ + WxMaService switchoverTo(String miniappId); + + /** + * 返回消息(客服消息和模版消息)发送接口方法实现类,以方便调用其各个接口. + * + * @return WxMaMsgService + */ + WxMaMsgService getMsgService(); + + /** + * 返回素材相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMaMediaService + */ + WxMaMediaService getMediaService(); + + /** + * 返回用户相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMaUserService + */ + WxMaUserService getUserService(); + + /** + * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMaQrcodeService + */ + WxMaQrcodeService getQrcodeService(); + + /** + * 返回订阅消息配置相关接口方法的实现类对象, 以方便调用其各个接口. + * + * @return WxMaSubscribeService + */ + WxMaSubscribeService getSubscribeService(); + + /** + * 数据分析相关查询服务. + * + * @return WxMaAnalysisService + */ + WxMaAnalysisService getAnalysisService(); + + /** + * 返回代码操作相关的 API. + * + * @return WxMaCodeService + */ + WxMaCodeService getCodeService(); + + /** + * 返回jsapi操作相关的 API服务类对象. + * + * @return WxMaJsapiService + */ + WxMaJsapiService getJsapiService(); + + /** + * 小程序修改服务器地址、成员管理 API. + * + * @return WxMaSettingService + */ + WxMaSettingService getSettingService(); + + /** + * 返回分享相关查询服务. + * + * @return WxMaShareService + */ + WxMaShareService getShareService(); + + /** + * 返回微信运动相关接口服务对象. + * + * @return WxMaShareService + */ + WxMaRunService getRunService(); + + /** + * 返回内容安全相关接口服务对象. + * + * @return WxMaShareService + */ + WxMaSecCheckService getSecCheckService(); + + /** + * 返回插件相关接口服务对象. + * + * @return WxMaPluginService + */ + WxMaPluginService getPluginService(); + + /** + * 初始化http请求对象. + */ + void initHttp(); + + /** + * 请求http请求相关信息. + * + * @return . + */ + RequestHttp getRequestHttp(); + + /** + * 获取物流助手接口服务对象 + * + * @return . + */ + WxMaExpressService getExpressService(); + + /** + * 获取云开发接口服务对象 + * + * @return . + */ + WxMaCloudService getCloudService(); + + /** + * 获取直播接口服务对象 + * + * @return . + */ + WxMaLiveService getLiveService(); + + /** + * 获取直播间商品服务对象 + * + * @return . + */ + WxMaLiveGoodsService getLiveGoodsService(); + + /** + * 获取ocr实现接口服务对象 + * + * @return 。 + */ + WxOcrService getOcrService(); + + /** + * 返回图像处理接口的实现类对象,以方便调用其各个接口. + * + * @return WxImgProcService + */ + WxImgProcService getImgProcService(); + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSettingService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSettingService.java new file mode 100644 index 0000000000..8cc7934988 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSettingService.java @@ -0,0 +1,65 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaDomainAction; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * 小程序修改服务器地址、成员管理 API(大部分只能是第三方平台调用) + * + * @author Charming + * @since 2018-04-27 15:46 + */ +public interface WxMaSettingService { + /** + * 修改服务器地址:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489138143_WPbOO&token=&lang=zh_CN + * access_token 为 authorizer_access_token + */ + String MODIFY_DOMAIN_URL = "https://api.weixin.qq.com/wxa/modify_domain"; + String SET_WEB_VIEW_DOMAIN_URL = "https://api.weixin.qq.com/wxa/setwebviewdomain"; + /** + * 小程序成员管理:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140588_nVUgx&token=&lang=zh_CN + * access_token 为 authorizer_access_token + */ + String BIND_TESTER_URL = "https://api.weixin.qq.com/wxa/bind_tester"; + String UNBIND_TESTER_URL = "https://api.weixin.qq.com/wxa/unbind_tester"; + + /** + * 操作服务器域名 + * + * @param domainAction 域名操作参数 + * 除了 webViewDomain,都是有效的 + * @return 以下字段仅在 get 时返回完整字段 + * @throws WxErrorException 操作失败时抛出,具体错误码请看文档 + */ + WxMaDomainAction modifyDomain(WxMaDomainAction domainAction) throws WxErrorException; + + /** + * 设置小程序业务域名(仅供第三方代小程序调用) + * 授权给第三方的小程序,其业务域名只可以为第三方的服务器, + * 当小程序通过第三方发布代码上线后,小程序原先自己配置的业务域名将被删除, + * 只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加业务域名。 + * 提示:需要先将域名登记到第三方平台的小程序业务域名中,才可以调用接口进行配置。 + * + * @param domainAction 域名操作参数 + * 只有 action 和 webViewDomain 是有效的 + * @return 以下字段仅在 get 时返回完整字段 + * @throws WxErrorException 操作失败时抛出,具体错误码请看文档 + */ + WxMaDomainAction setWebViewDomain(WxMaDomainAction domainAction) throws WxErrorException; + + /** + * 绑定微信用户为小程序体验者 + * + * @param wechatId 微信号 + * @throws WxErrorException 失败时抛出,具体错误码请看文档 + */ + void bindTester(String wechatId) throws WxErrorException; + + /** + * 解除绑定小程序的体验者 + * + * @param wechatId 微信号 + * @throws WxErrorException 失败时抛出,具体错误码请看文档 + */ + void unbindTester(String wechatId) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaShareService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaShareService.java new file mode 100644 index 0000000000..8c6030e53c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaShareService.java @@ -0,0 +1,21 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaShareInfo; + +/** + * 分享信息相关操作接口. + * + * @author zhfish + */ +public interface WxMaShareService { + + /** + * 解密分享敏感数据. + * + * @param sessionKey 会话密钥 + * @param encryptedData 消息密文 + * @param ivStr 加密算法的初始向量 + */ + WxMaShareInfo getShareInfo(String sessionKey, String encryptedData, String ivStr); + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java new file mode 100644 index 0000000000..19e22a5eb6 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java @@ -0,0 +1,153 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.template.WxMaPubTemplateTitleListResult; +import lombok.Data; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + * 订阅消息类 + * + * @author Binary Wang + * @date 2019-12-15 + */ +public interface WxMaSubscribeService { + /** + * 获取模板标题下的关键词列表. + */ + String GET_PUB_TEMPLATE_TITLE_LIST_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles"; + + /** + * 获取模板标题下的关键词列表. + */ + String GET_PUB_TEMPLATE_KEY_WORDS_BY_ID_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords"; + + /** + * 组合模板并添加至帐号下的个人模板库. + */ + String TEMPLATE_ADD_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate"; + + /** + * 获取当前帐号下的个人模板列表. + */ + String TEMPLATE_LIST_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate"; + + /** + * 删除帐号下的某个模板. + */ + String TEMPLATE_DEL_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate"; + + /** + * 获取小程序账号的类目 + */ + String GET_CATEGORY_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory"; + + /** + *
    +   * 获取帐号所属类目下的公共模板标题
    +   *
    +   * 详情请见: 获取帐号所属类目下的公共模板标题
    +   * 接口url格式: https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param ids 类目 id,多个用逗号隔开 + * @param limit 用于分页,表示拉取 limit 条记录。最大为 30。 + * @param start 用于分页,表示从 start 开始。从 0 开始计数。 + * @return . + * @throws WxErrorException . + */ + WxMaPubTemplateTitleListResult getPubTemplateTitleList(String[] ids, int start, int limit) throws WxErrorException; + + /** + *
    +   * 获取模板库某个模板标题下关键词库
    +   *
    +   * 详情请见: 获取模板标题下的关键词列表
    +   * 接口url格式: GET https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param id 模板标题 id,可通过接口获取 + * @return . + * @throws WxErrorException . + */ + List getPubTemplateKeyWordsById(String id) throws WxErrorException; + + /** + *
    +   * 组合模板并添加至帐号下的个人模板库
    +   *
    +   * 详情请见: 获取小程序模板库标题列表
    +   * 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param id 模板标题 id,可通过接口获取,也可登录小程序后台查看获取 + * @param keywordIdList 模板关键词列表 + * @param sceneDesc 服务场景描述,15个字以内 + * @return 添加至帐号下的模板id,发送小程序订阅消息时所需 + * @throws WxErrorException . + */ + String addTemplate(String id, List keywordIdList, String sceneDesc) throws WxErrorException; + + /** + *
    +   * 获取当前帐号下的个人模板列表
    +   *
    +   * 详情请见: 获取当前帐号下的个人模板列表
    +   * 接口url格式: GET https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token=ACCESS_TOKEN
    +   * 
    + * + * @return . + * @throws WxErrorException . + */ + List getTemplateList() throws WxErrorException; + + /** + *
    +   * 删除帐号下的某个模板
    +   *
    +   * 详情请见: 删除帐号下的个人模板
    +   * 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param templateId 要删除的模板id + * @return 删除是否成功 + * @throws WxErrorException . + */ + boolean delTemplate(String templateId) throws WxErrorException; + + /** + *
    +   * 获取小程序账号的类目
    +   * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getCategory.html
    +   * GET https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=ACCESS_TOKEN
    +   * 
    + * + * @return . + * @throws WxErrorException . + */ + List getCategory() throws WxErrorException; + + @Data + class CategoryData { + int id; + String name; + } + + @Data + class TemplateInfo { + private String priTmplId; + private String title; + private String content; + private String example; + private int type; + } + + @Data + class PubTemplateKeyword { + private int kid; + private String name; + private String example; + private String rule; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java new file mode 100644 index 0000000000..3b7abeeb42 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java @@ -0,0 +1,67 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.Map; + +/** + * 用户信息相关操作接口. + * + * @author Binary Wang + */ +public interface WxMaUserService { + String SET_USER_STORAGE = "https://api.weixin.qq.com/wxa/set_user_storage?appid=%s&signature=%s&openid=%s&sig_method=%s"; + + /** + * 获取登录后的session信息. + * + * @param jsCode 登录时获取的 code + * @return . + * @throws WxErrorException . + */ + WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException; + + /** + * 解密用户敏感数据. + * + * @param sessionKey 会话密钥 + * @param encryptedData 消息密文 + * @param ivStr 加密算法的初始向量 + */ + WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); + + /** + * 上报用户数据后台接口. + *

    小游戏可以通过本接口上报key-value数据到用户的CloudStorage。

    + * 文档参考https://developers.weixin.qq.com/minigame/dev/document/open-api/data/setUserStorage.html + * + * @param kvMap 要上报的数据 + * @param sessionKey 通过wx.login 获得的登录态 + * @param openid . + * @throws WxErrorException . + */ + void setUserStorage(Map kvMap, String sessionKey, String openid) throws WxErrorException; + + /** + * 解密用户手机号信息. + * + * @param sessionKey 会话密钥 + * @param encryptedData 消息密文 + * @param ivStr 加密算法的初始向量 + * @return . + */ + WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr); + + /** + * 验证用户信息完整性. + * + * @param sessionKey 会话密钥 + * @param rawData 微信用户基本信息 + * @param signature 数据签名 + * @return . + */ + boolean checkUserInfo(String sessionKey, String rawData, String signature); +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java new file mode 100644 index 0000000000..7bd1eec748 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java @@ -0,0 +1,476 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.*; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.WxMaConfigHolder; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.api.WxImgProcService; +import me.chanjar.weixin.common.api.WxOcrService; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.DataUtils; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * @author Binary Wang + * @see #doGetAccessTokenRequest + */ +@Slf4j +public abstract class BaseWxMaServiceImpl implements WxMaService, RequestHttp { + private Map configMap; + + private final WxMaMsgService kefuService = new WxMaMsgServiceImpl(this); + private final WxMaMediaService materialService = new WxMaMediaServiceImpl(this); + private final WxMaUserService userService = new WxMaUserServiceImpl(this); + private final WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this); + private final WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); + private final WxMaCodeService codeService = new WxMaCodeServiceImpl(this); + private final WxMaSettingService settingService = new WxMaSettingServiceImpl(this); + private final WxMaJsapiService jsapiService = new WxMaJsapiServiceImpl(this); + private final WxMaShareService shareService = new WxMaShareServiceImpl(this); + private final WxMaRunService runService = new WxMaRunServiceImpl(this); + private final WxMaSecCheckService secCheckService = new WxMaSecCheckServiceImpl(this); + private final WxMaPluginService pluginService = new WxMaPluginServiceImpl(this); + private final WxMaExpressService expressService = new WxMaExpressServiceImpl(this); + private final WxMaSubscribeService subscribeService = new WxMaSubscribeServiceImpl(this); + private final WxMaCloudService cloudService = new WxMaCloudServiceImpl(this); + private final WxMaLiveService liveService = new WxMaLiveServiceImpl(this); + private final WxMaLiveGoodsService liveGoodsService = new WxMaLiveGoodsServiceImpl(this); + private final WxOcrService ocrService = new WxMaOcrServiceImpl(this); + private final WxImgProcService imgProcService = new WxMaImgProcServiceImpl(this); + + private int retrySleepMillis = 1000; + private int maxRetryTimes = 5; + + protected static final Gson GSON = new Gson(); + + @Override + public RequestHttp getRequestHttp() { + return this; + } + + @Override + public String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) + throws WxErrorException { + Map params = new HashMap<>(8); + params.put("openid", openid); + + if (StringUtils.isNotEmpty(transactionId)) { + params.put("transaction_id", transactionId); + } + + if (StringUtils.isNotEmpty(mchId)) { + params.put("mch_id", mchId); + } + + if (StringUtils.isNotEmpty(outTradeNo)) { + params.put("out_trade_no", outTradeNo); + } + + String responseContent = this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); + WxError error = WxError.fromJson(responseContent, WxType.MiniApp); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return GsonParser.parse(responseContent).get("unionid").getAsString(); + } + + @Override + public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { + final WxMaConfig config = getWxMaConfig(); + Map params = new HashMap<>(8); + params.put("appid", config.getAppid()); + params.put("secret", config.getSecret()); + params.put("js_code", jsCode); + params.put("grant_type", "authorization_code"); + + String result = get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); + return WxMaJscode2SessionResult.fromJson(result); + } + + @Override + public void setDynamicData(int lifespan, String type, int scene, String data) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("lifespan", lifespan); + jsonObject.addProperty("query", WxGsonBuilder.create().toJson(ImmutableMap.of("type", type))); + jsonObject.addProperty("data", data); + jsonObject.addProperty("scene", scene); + + this.post(SET_DYNAMIC_DATA_URL, jsonObject.toString()); + } + + @Override + public boolean checkSignature(String timestamp, String nonce, String signature) { + try { + return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); + } catch (Exception e) { + log.error("Checking signature failed, and the reason is :" + e.getMessage()); + return false; + } + } + + @Override + public String getAccessToken() throws WxErrorException { + return getAccessToken(false); + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!forceRefresh && !this.getWxMaConfig().isAccessTokenExpired()) { + return this.getWxMaConfig().getAccessToken(); + } + + Lock lock = this.getWxMaConfig().getAccessTokenLock(); + boolean locked = false; + try { + do { + locked = lock.tryLock(100, TimeUnit.MILLISECONDS); + if (!forceRefresh && !this.getWxMaConfig().isAccessTokenExpired()) { + return this.getWxMaConfig().getAccessToken(); + } + } while (!locked); + String response = doGetAccessTokenRequest(); + return extractAccessToken(response); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (locked) { + lock.unlock(); + } + } + } + + /** + * 通过网络请求获取AccessToken + * + * @return . + * @throws IOException . + */ + protected abstract String doGetAccessTokenRequest() throws IOException; + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(SimpleGetRequestExecutor.create(this), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(SimplePostRequestExecutor.create(this), url, postData); + } + + @Override + public String post(String url, Object obj) throws WxErrorException { + return this.execute(SimplePostRequestExecutor.create(this), url, WxGsonBuilder.create().toJson(obj)); + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 + */ + @Override + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + int retryTimes = 0; + do { + try { + return this.executeInternal(executor, uri, data); + } catch (WxErrorException e) { + if (retryTimes + 1 > this.maxRetryTimes) { + log.warn("重试达到最大次数【{}】", maxRetryTimes); + //最后一次重试失败后,直接抛出异常,不再等待 + throw new WxErrorException(WxError.builder() + .errorCode(e.getError().getErrorCode()) + .errorMsg("微信服务端异常,超出重试次数!") + .build()); + } + + WxError error = e.getError(); + // -1 系统繁忙, 1000ms后重试 + if (error.getErrorCode() == -1) { + int sleepMillis = this.retrySleepMillis * (1 << retryTimes); + try { + log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + Thread.sleep(sleepMillis); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } + } else { + throw e; + } + } + } while (retryTimes++ < this.maxRetryTimes); + + log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + private T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + E dataForLog = DataUtils.handleDataWithSecret(data); + + if (uri.contains("access_token=")) { + throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); + } + String accessToken = getAccessToken(false); + + String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken; + + try { + T result = executor.execute(uriWithAccessToken, data, WxType.MiniApp); + log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); + return result; + } catch (WxErrorException e) { + WxError error = e.getError(); + if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) { + // 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token + Lock lock = this.getWxMaConfig().getAccessTokenLock(); + lock.lock(); + try { + if (StringUtils.equals(this.getWxMaConfig().getAccessToken(), accessToken)) { + this.getWxMaConfig().expireAccessToken(); + } + } catch (Exception ex) { + this.getWxMaConfig().expireAccessToken(); + } finally { + lock.unlock(); + } + if (this.getWxMaConfig().autoRefreshToken()) { + log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg()); + return this.execute(executor, uri, data); + } + } + + if (error.getErrorCode() != 0) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); + throw new WxErrorException(error, e); + } + return null; + } catch (IOException e) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * 设置当前的AccessToken + * + * @param resultContent 响应内容 + * @return access token + * @throws WxErrorException 异常 + */ + protected String extractAccessToken(String resultContent) throws WxErrorException { + WxMaConfig config = this.getWxMaConfig(); + WxError error = WxError.fromJson(resultContent, WxType.MiniApp); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + config.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + return accessToken.getAccessToken(); + } + + @Override + public WxMaConfig getWxMaConfig() { + if (this.configMap.size() == 1) { + // 只有一个小程序,直接返回其配置即可 + return this.configMap.values().iterator().next(); + } + + return this.configMap.get(WxMaConfigHolder.get()); + } + + @Override + public void setWxMaConfig(WxMaConfig maConfig) { + final String appid = maConfig.getAppid(); + this.setMultiConfigs(ImmutableMap.of(appid, maConfig), appid); + } + + @Override + public void setMultiConfigs(Map configs) { + this.setMultiConfigs(configs, configs.keySet().iterator().next()); + } + + @Override + public void setMultiConfigs(Map configs, String defaultMiniappId) { + this.configMap = Maps.newHashMap(configs); + WxMaConfigHolder.set(defaultMiniappId); + this.initHttp(); + } + + @Override + public void addConfig(String mpId, WxMaConfig configStorages) { + synchronized (this) { + if (this.configMap == null) { + this.setWxMaConfig(configStorages); + } else { + this.configMap.put(mpId, configStorages); + } + } + } + + @Override + public void removeConfig(String miniappId) { + synchronized (this) { + if (this.configMap.size() == 1) { + this.configMap.remove(miniappId); + log.warn("已删除最后一个小程序配置:{},须立即使用setWxMaConfig或setMultiConfigs添加配置", miniappId); + return; + } + if (WxMaConfigHolder.get().equals(miniappId)) { + this.configMap.remove(miniappId); + final String defaultMpId = this.configMap.keySet().iterator().next(); + WxMaConfigHolder.set(defaultMpId); + log.warn("已删除默认小程序配置,小程序【{}】被设为默认配置", defaultMpId); + return; + } + this.configMap.remove(miniappId); + } + } + + @Override + public WxMaService switchoverTo(String miniappId) { + if (this.configMap.containsKey(miniappId)) { + WxMaConfigHolder.set(miniappId); + return this; + } + + throw new RuntimeException(String.format("无法找到对应【%s】的小程序配置信息,请核实!", miniappId)); + } + + @Override + public boolean switchover(String mpId) { + if (this.configMap.containsKey(mpId)) { + WxMaConfigHolder.set(mpId); + return true; + } + + log.error("无法找到对应【{}】的小程序配置信息,请核实!", mpId); + return false; + } + + @Override + public void setRetrySleepMillis(int retrySleepMillis) { + this.retrySleepMillis = retrySleepMillis; + } + + @Override + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + @Override + public WxMaMsgService getMsgService() { + return this.kefuService; + } + + @Override + public WxMaMediaService getMediaService() { + return this.materialService; + } + + @Override + public WxMaUserService getUserService() { + return this.userService; + } + + @Override + public WxMaQrcodeService getQrcodeService() { + return this.qrCodeService; + } + + @Override + public WxMaSubscribeService getSubscribeService() { + return this.subscribeService; + } + + @Override + public WxMaAnalysisService getAnalysisService() { + return this.analysisService; + } + + @Override + public WxMaCodeService getCodeService() { + return this.codeService; + } + + @Override + public WxMaJsapiService getJsapiService() { + return this.jsapiService; + } + + @Override + public WxMaSettingService getSettingService() { + return this.settingService; + } + + @Override + public WxMaShareService getShareService() { + return this.shareService; + } + + @Override + public WxMaRunService getRunService() { + return this.runService; + } + + @Override + public WxMaSecCheckService getSecCheckService() { + return this.secCheckService; + } + + @Override + public WxMaPluginService getPluginService() { + return this.pluginService; + } + + @Override + public WxMaExpressService getExpressService() { + return this.expressService; + } + + @Override + public WxMaCloudService getCloudService() { + return this.cloudService; + } + + @Override + public WxMaLiveService getLiveService() { + return this.liveService; + } + + @Override + public WxMaLiveGoodsService getLiveGoodsService() { + return this.liveGoodsService; + } + + @Override + public WxOcrService getOcrService() { + return this.ocrService; + } + + @Override + public WxImgProcService getImgProcService() { + return this.imgProcService; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaAnalysisServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaAnalysisServiceImpl.java new file mode 100644 index 0000000000..db69642714 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaAnalysisServiceImpl.java @@ -0,0 +1,124 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaSummaryTrend; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitDistribution; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitPage; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitTrend; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.reflect.Type; +import java.util.Date; +import java.util.List; + +/** + * @author Charming + * @since 2018-04-28 + */ +@AllArgsConstructor +public class WxMaAnalysisServiceImpl implements WxMaAnalysisService { + + private WxMaService wxMaService; + + @Override + public List getDailySummaryTrend(Date beginDate, Date endDate) throws WxErrorException { + return getAnalysisResultAsList(GET_DAILY_SUMMARY_TREND_URL, beginDate, endDate, + new TypeToken>() { + }.getType()); + } + + @Override + public List getDailyVisitTrend(Date beginDate, Date endDate) throws WxErrorException { + return getAnalysisResultAsList(GET_DAILY_VISIT_TREND_URL, beginDate, endDate, + new TypeToken>() { + }.getType()); + } + + @Override + public List getWeeklyVisitTrend(Date beginDate, Date endDate) throws WxErrorException { + return getAnalysisResultAsList(GET_WEEKLY_VISIT_TREND_URL, beginDate, endDate, + new TypeToken>() { + }.getType()); + } + + @Override + public List getMonthlyVisitTrend(Date beginDate, Date endDate) throws WxErrorException { + return getAnalysisResultAsList(GET_MONTHLY_VISIT_TREND_URL, beginDate, endDate, + new TypeToken>() { + }.getType()); + } + + @Override + public WxMaVisitDistribution getVisitDistribution(Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_VISIT_DISTRIBUTION_URL, toJson(beginDate, endDate)); + return WxMaVisitDistribution.fromJson(responseContent); + } + + @Override + public WxMaRetainInfo getDailyRetainInfo(Date beginDate, Date endDate) throws WxErrorException { + return getRetainInfo(beginDate, endDate, GET_DAILY_RETAIN_INFO_URL); + } + + @Override + public WxMaRetainInfo getWeeklyRetainInfo(Date beginDate, Date endDate) throws WxErrorException { + return getRetainInfo(beginDate, endDate, GET_WEEKLY_RETAIN_INFO_URL); + } + + @Override + public WxMaRetainInfo getMonthlyRetainInfo(Date beginDate, Date endDate) throws WxErrorException { + return getRetainInfo(beginDate, endDate, GET_MONTHLY_RETAIN_INFO_URL); + } + + @Override + public List getVisitPage(Date beginDate, Date endDate) throws WxErrorException { + return getAnalysisResultAsList(GET_VISIT_PAGE_URL, beginDate, endDate, + new TypeToken>() { + }.getType()); + } + + @Override + public WxMaUserPortrait getUserPortrait(Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_USER_PORTRAIT_URL, toJson(beginDate, endDate)); + return WxMaUserPortrait.fromJson(responseContent); + } + + private WxMaRetainInfo getRetainInfo(Date beginDate, Date endDate, String url) throws WxErrorException { + String responseContent = this.wxMaService.post(url, toJson(beginDate, endDate)); + return WxMaRetainInfo.fromJson(responseContent); + } + + /** + * 获取数据分析结果并返回 List,returnType 类型 + * + * @param url 链接 + * @param returnType 返回的类型 + * @param 返回的类型 + * @return List 类型的数据 + */ + private List getAnalysisResultAsList(String url, Date beginDate, Date endDate, Type returnType) throws WxErrorException { + String responseContent = this.wxMaService.post(url, toJson(beginDate, endDate)); + JsonObject response = GsonParser.parse(responseContent); + boolean hasList = response.has("list"); + if (hasList) { + return WxMaGsonBuilder.create().fromJson(response.getAsJsonArray("list"), returnType); + } else { + return null; + } + } + + private static String toJson(Date beginDate, Date endDate) { + JsonObject param = new JsonObject(); + param.addProperty("begin_date", DateFormatUtils.format(beginDate, "yyyyMMdd")); + param.addProperty("end_date", DateFormatUtils.format(endDate, "yyyyMMdd")); + return param.toString(); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImpl.java new file mode 100644 index 0000000000..300ded88fe --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImpl.java @@ -0,0 +1,409 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaCloudService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.cloud.*; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.util.JoinerUtils; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.*; + +/** + * 云开发相关接口实现类. + * + * @author Binary Wang + * @date 2020-01-22 + */ +@Slf4j +@RequiredArgsConstructor +public class WxMaCloudServiceImpl implements WxMaCloudService { + + private final WxMaService wxMaService; + + @Override + public String invokeCloudFunction(String name, String body) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return invokeCloudFunction(cloudEnv, name, body); + } + + @Override + public String invokeCloudFunction(String env, String name, String body) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + final String response = this.wxMaService.post(String.format(INVOKE_CLOUD_FUNCTION_URL, cloudEnv, name), body); + return GsonParser.parse(response).get("resp_data").getAsString(); + } + + @Override + public List add(String collection, List list) throws WxErrorException { + String jsonData = WxMaGsonBuilder.create().toJson(list); + String query = JoinerUtils.blankJoiner.join( + "db.collection('", collection, "')", + ".add({data: ", jsonData, "})"); + + JsonObject params = new JsonObject(); + params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv()); + params.addProperty("query", query); + + String responseContent = wxMaService.post(DATABASE_ADD_URL, params.toString()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent)); + } + JsonArray idArray = jsonObject.getAsJsonArray("id_list"); + List idList = new ArrayList<>(); + for (JsonElement id : idArray) { + idList.add(id.getAsString()); + } + return idList; + } + + @Override + public String add(String collection, Object obj) throws WxErrorException { + String jsonData = WxMaGsonBuilder.create().toJson(obj); + String query = JoinerUtils.blankJoiner.join( + "db.collection('", collection, "')", + ".add({data: ", jsonData, "})"); + + JsonObject params = new JsonObject(); + params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv()); + params.addProperty("query", query); + + String responseContent = wxMaService.post(DATABASE_ADD_URL, params.toString()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent)); + } + JsonArray idArray = jsonObject.getAsJsonArray("id_list"); + return idArray.getAsString(); + } + + @Override + public JsonArray databaseAdd(String query) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseAdd(cloudEnv, query); + } + + @Override + public JsonArray databaseAdd(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_ADD_URL, ImmutableMap.of("env", env, "query", query)); + return GsonParser.parse(response).get("id_list").getAsJsonArray(); + } + + @Override + public Integer delete(String collection, String whereJson) throws WxErrorException { + String query = JoinerUtils.blankJoiner.join( + "db.collection('", collection, "')", + ".where(", whereJson, ").remove()"); + + JsonObject params = new JsonObject(); + params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv()); + params.addProperty("query", query); + + String responseContent = wxMaService.post(DATABASE_DELETE_URL, params.toString()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent)); + } + return jsonObject.get("deleted").getAsInt(); + } + + @Override + public int databaseDelete(String query) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseDelete(cloudEnv, query); + } + + @Override + public int databaseDelete(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_DELETE_URL, ImmutableMap.of("env", env, "query", query)); + return GsonParser.parse(response).get("deleted").getAsInt(); + } + + @Override + public WxCloudDatabaseUpdateResult update(String collection, String whereJson, String updateJson) throws WxErrorException { + String query = JoinerUtils.blankJoiner.join( + "db.collection('", collection, "')", + ".where(", whereJson, ").update({data:", updateJson, " })"); + + JsonObject params = new JsonObject(); + params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv()); + params.addProperty("query", query); + + String responseContent = wxMaService.post(DATABASE_UPDATE_URL, params.toString()); + return WxGsonBuilder.create().fromJson(responseContent, WxCloudDatabaseUpdateResult.class); + } + + @Override + public WxCloudDatabaseUpdateResult databaseUpdate(String query) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseUpdate(cloudEnv, query); + } + + @Override + public WxCloudDatabaseUpdateResult databaseUpdate(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_UPDATE_URL, ImmutableMap.of("env", env, "query", query)); + return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseUpdateResult.class); + } + + @Override + public WxCloudDatabaseQueryResult query(String collection, String whereJson, Map orderBy, + Integer skip, Integer limit) throws WxErrorException { + if (StringUtils.isBlank(whereJson)) { + whereJson = "{}"; + } + StringBuilder orderBySb = new StringBuilder(); + if (null != orderBy && !orderBy.isEmpty()) { + for (Map.Entry entry : orderBy.entrySet()) { + orderBySb.append(".orderBy('").append(entry.getKey()).append("', '").append(entry.getValue()).append("')"); + } + } + + if (null == limit) { + limit = 100; + } + if (null == skip) { + skip = 0; + } + String query = JoinerUtils.blankJoiner.join( + "db.collection('", collection, "')", + ".where(", whereJson, ")", orderBySb.toString(), ".skip(", skip, ").limit(", limit, ").get()"); + + JsonObject params = new JsonObject(); + params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv()); + params.addProperty("query", query); + + String responseContent = wxMaService.post(DATABASE_QUERY_URL, params.toString()); + return WxGsonBuilder.create().fromJson(responseContent, WxCloudDatabaseQueryResult.class); + } + + @Override + public WxCloudDatabaseQueryResult databaseQuery(String query) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseQuery(cloudEnv, query); + } + + @Override + public WxCloudDatabaseQueryResult databaseQuery(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_QUERY_URL, ImmutableMap.of("env", env, "query", query)); + return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseQueryResult.class); + } + + @Override + public JsonArray databaseAggregate(String query) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseAggregate(cloudEnv, query); + } + + @Override + public JsonArray databaseAggregate(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_AGGREGATE_URL, ImmutableMap.of("env", env, "query", query)); + return GsonParser.parse(response).get("data").getAsJsonArray(); + } + + @Override + public Long count(String collection, String whereJson) throws WxErrorException { + String query = JoinerUtils.blankJoiner.join( + "db.collection('", collection, "')", + ".where(", whereJson, ").count()"); + + JsonObject params = new JsonObject(); + params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv()); + params.addProperty("query", query); + + String responseContent = wxMaService.post(DATABASE_COUNT_URL, params.toString()); + return GsonParser.parse(responseContent).get("count").getAsLong(); + } + + @Override + public Long databaseCount(String query) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseCount(cloudEnv, query); + } + + @Override + public Long databaseCount(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_COUNT_URL, ImmutableMap.of("env", env, "query", query)); + return GsonParser.parse(response).get("count").getAsLong(); + } + + @Override + public void updateIndex(String collectionName, List createIndexes, + List dropIndexNames) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + updateIndex(cloudEnv, collectionName, createIndexes, dropIndexNames); + } + + @Override + public void updateIndex(String env, String collectionName, List createIndexes, + List dropIndexNames) throws WxErrorException { + List> dropIndexes = Lists.newArrayList(); + if (dropIndexNames != null) { + for (String index : dropIndexNames) { + dropIndexes.add(ImmutableMap.of("name", index)); + } + } + + this.wxMaService.post(UPDATE_INDEX_URL, ImmutableMap.of("env", env, + "collection_name", collectionName, "create_indexes", createIndexes, "drop_indexes", dropIndexes)); + } + + @Override + public Long databaseMigrateImport(String collectionName, String filePath, int fileType, + boolean stopOnError, int conflictMode) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseMigrateImport(cloudEnv, collectionName, filePath, fileType, stopOnError, conflictMode); + } + + @Override + public Long databaseMigrateImport(String env, String collectionName, String filePath, int fileType, + boolean stopOnError, int conflictMode) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("env", env); + params.addProperty("collection_name", collectionName); + params.addProperty("file_path", filePath); + params.addProperty("file_type", fileType); + params.addProperty("stop_on_error", stopOnError); + params.addProperty("conflict_mode", conflictMode); + + String response = this.wxMaService.post(DATABASE_MIGRATE_IMPORT_URL, params.toString()); + return GsonParser.parse(response).get("job_id").getAsLong(); + } + + @Override + public Long databaseMigrateExport(String filePath, int fileType, String query) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseMigrateExport(cloudEnv, filePath, fileType, query); + } + + @Override + public Long databaseMigrateExport(String env, String filePath, int fileType, String query) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("env", env); + params.addProperty("file_path", filePath); + params.addProperty("file_type", fileType); + params.addProperty("query", query); + + String response = this.wxMaService.post(DATABASE_MIGRATE_EXPORT_URL, params.toString()); + return GsonParser.parse(response).get("job_id").getAsLong(); + } + + @Override + public WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(Long jobId) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseMigrateQueryInfo(cloudEnv, jobId); + } + + @Override + public WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(String env, Long jobId) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_MIGRATE_QUERY_INFO_URL, ImmutableMap.of("env", env, "job_id", + jobId)); + return WxGsonBuilder.create().fromJson(response, WxCloudCloudDatabaseMigrateQueryInfoResult.class); + } + + @Override + public WxCloudUploadFileResult uploadFile(String path) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return uploadFile(cloudEnv, path); + } + + @Override + public WxCloudUploadFileResult uploadFile(String env, String path) throws WxErrorException { + String response = this.wxMaService.post(UPLOAD_FILE_URL, ImmutableMap.of("env", env, "path", path)); + return WxGsonBuilder.create().fromJson(response, WxCloudUploadFileResult.class); + } + + @Override + public WxCloudBatchDownloadFileResult batchDownloadFile(String[] fileIds, long[] maxAges) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return batchDownloadFile(cloudEnv, fileIds, maxAges); + } + + @Override + public WxCloudBatchDownloadFileResult batchDownloadFile(String env, String[] fileIds, long[] maxAges) throws WxErrorException { + List> fileList = Lists.newArrayList(); + int i = 0; + for (String fileId : fileIds) { + fileList.add(ImmutableMap.of("fileid", fileId, "max_age", (Serializable) maxAges[i++])); + } + + String response = this.wxMaService.post(BATCH_DOWNLOAD_FILE_URL, ImmutableMap.of("env", env, "file_list", fileList)); + return WxGsonBuilder.create().fromJson(response, WxCloudBatchDownloadFileResult.class); + } + + @Override + public WxCloudBatchDeleteFileResult batchDeleteFile(String[] fileIds) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return batchDeleteFile(cloudEnv, fileIds); + } + + @Override + public WxCloudBatchDeleteFileResult batchDeleteFile(String env, String[] fileIds) throws WxErrorException { + String response = this.wxMaService.post(BATCH_DELETE_FILE_URL, ImmutableMap.of("env", env, "fileid_list", fileIds)); + return WxGsonBuilder.create().fromJson(response, WxCloudBatchDeleteFileResult.class); + } + + @Override + public WxCloudGetQcloudTokenResult getQcloudToken(long lifeSpan) throws WxErrorException { + String response = this.wxMaService.post(GET_QCLOUD_TOKEN_URL, ImmutableMap.of("lifespan", lifeSpan)); + return WxGsonBuilder.create().fromJson(response, WxCloudGetQcloudTokenResult.class); + } + + @Override + public void databaseCollectionAdd(String collectionName) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + databaseCollectionAdd(cloudEnv, collectionName); + } + + @Override + public void databaseCollectionAdd(String env, String collectionName) throws WxErrorException { + this.wxMaService.post(DATABASE_COLLECTION_ADD_URL, ImmutableMap.of("env", env, "collection_name", collectionName)); + } + + @Override + public void databaseCollectionDelete(String collectionName) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + databaseCollectionDelete(cloudEnv, collectionName); + } + + @Override + public void databaseCollectionDelete(String env, String collectionName) throws WxErrorException { + this.wxMaService.post(DATABASE_COLLECTION_DELETE_URL, ImmutableMap.of("env", env, "collection_name", + collectionName)); + } + + @Override + public WxCloudDatabaseCollectionGetResult databaseCollectionGet(Long limit, Long offset) throws WxErrorException { + String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv(); + return databaseCollectionGet(cloudEnv, limit, offset); + } + + @Override + public WxCloudDatabaseCollectionGetResult databaseCollectionGet(String env, Long limit, Long offset) throws WxErrorException { + Map params = new HashMap<>(2); + params.put("env", env); + if (limit != null) { + params.put("limit", limit); + } + + if (offset != null) { + params.put("offset", offset); + } + + String response = this.wxMaService.post(DATABASE_COLLECTION_GET_URL, params); + return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseCollectionGetResult.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImpl.java new file mode 100644 index 0000000000..2d965b4c45 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImpl.java @@ -0,0 +1,156 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import java.io.File; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.apache.commons.lang3.StringUtils; + +import cn.binarywang.wx.miniapp.api.WxMaCodeService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.json.GsonHelper; + +/** + * @author Charming + * @since 2018-04-26 20:00 + */ +@AllArgsConstructor +public class WxMaCodeServiceImpl implements WxMaCodeService { + + private WxMaService wxMaService; + + @Override + public void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException { + this.wxMaService.post(COMMIT_URL, commitRequest.toJson()); + } + + @Override + public byte[] getQrCode(String path) throws WxErrorException { + String appId = this.wxMaService.getWxMaConfig().getAppid(); + Path qrCodeFilePath = null; + try { + RequestExecutor executor = BaseMediaDownloadRequestExecutor + .create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("wxjava-ma-" + appId).toFile()); + + final StringBuilder url = new StringBuilder(GET_QRCODE_URL); + if (StringUtils.isNotBlank(path)) { + url.append("?path=").append(URLEncoder.encode(path, StandardCharsets.UTF_8.name())); + } + + qrCodeFilePath = this.wxMaService.execute(executor, url.toString(), null).toPath(); + return Files.readAllBytes(qrCodeFilePath); + } catch (IOException e) { + throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e); + } finally { + if (qrCodeFilePath != null) { + try { + // 及时删除二维码文件,避免积压过多缓存文件 + Files.delete(qrCodeFilePath); + } catch (Exception ignored) { + } + } + } + } + + @Override + public List getCategory() throws WxErrorException { + String responseContent = this.wxMaService.get(GET_CATEGORY_URL, null); + JsonObject jsonObject = GsonParser.parse(responseContent); + boolean hasCategoryList = jsonObject.has("category_list"); + if (hasCategoryList) { + return WxMaGsonBuilder.create().fromJson(jsonObject.getAsJsonArray("category_list"), + new TypeToken>() { + }.getType()); + } else { + return null; + } + } + + @Override + public List getPage() throws WxErrorException { + String responseContent = this.wxMaService.get(GET_PAGE_URL, null); + JsonObject jsonObject = GsonParser.parse(responseContent); + boolean hasPageList = jsonObject.has("page_list"); + if (hasPageList) { + return WxMaGsonBuilder.create().fromJson(jsonObject.getAsJsonArray("page_list"), + new TypeToken>() { + }.getType()); + } else { + return null; + } + } + + @Override + public long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(SUBMIT_AUDIT_URL, auditRequest.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + return GsonHelper.getLong(jsonObject, "auditid"); + } + + @Override + public WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("auditid", auditId); + String responseContent = this.wxMaService.post(GET_AUDIT_STATUS_URL, param.toString()); + return WxMaCodeAuditStatus.fromJson(responseContent); + } + + @Override + public WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException { + String responseContent = this.wxMaService.get(GET_LATEST_AUDIT_STATUS_URL, null); + return WxMaCodeAuditStatus.fromJson(responseContent); + } + + @Override + public void release() throws WxErrorException { + this.wxMaService.post(RELEASE_URL, "{}"); + } + + @Override + public void changeVisitStatus(String action) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("action", action); + this.wxMaService.post(CHANGE_VISIT_STATUS_URL, param.toString()); + } + + @Override + public void revertCodeRelease() throws WxErrorException { + this.wxMaService.get(REVERT_CODE_RELEASE_URL, null); + } + + @Override + public WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException { + String responseContent = this.wxMaService.post(GET_SUPPORT_VERSION_URL, "{}"); + return WxMaCodeVersionDistribution.fromJson(responseContent); + } + + @Override + public void setSupportVersion(String version) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("version", version); + this.wxMaService.post(SET_SUPPORT_VERSION_URL, param.toString()); + } + + @Override + public void undoCodeAudit() throws WxErrorException { + this.wxMaService.get(UNDO_CODE_AUDIT_URL, null); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImpl.java new file mode 100644 index 0000000000..21d0bfe0b0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImpl.java @@ -0,0 +1,98 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaExpressService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressAccount; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressDelivery; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressPath; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressPrinter; +import cn.binarywang.wx.miniapp.bean.express.request.*; +import cn.binarywang.wx.miniapp.bean.express.result.WxMaExpressOrderInfoResult; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author xiaoyu + * @since 2019-11-26 + */ +@AllArgsConstructor +public class WxMaExpressServiceImpl implements WxMaExpressService { + + private WxMaService wxMaService; + + @Override + public List getAllDelivery() throws WxErrorException { + String responseContent = this.wxMaService.get(ALL_DELIVERY_URL, null); + return WxMaExpressDelivery.fromJson(responseContent); + } + + @Override + public List getAllAccount() throws WxErrorException { + String responseContent = this.wxMaService.get(ALL_ACCOUNT_URL, null); + return WxMaExpressAccount.fromJsonList(responseContent); + } + + @Override + public void bindAccount(WxMaExpressBindAccountRequest wxMaExpressBindAccountRequest) throws WxErrorException { + this.wxMaService.post(BIND_ACCOUNT_URL,wxMaExpressBindAccountRequest.toJson()); + } + + @Override + public Integer getQuota(WxMaExpressBindAccountRequest wxMaExpressBindAccountRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_QUOTA_URL,wxMaExpressBindAccountRequest.toJson()); + WxMaExpressAccount account = WxMaExpressAccount.fromJson(responseContent); + return account.getQuotaNum(); + } + + @Override + public void updatePrinter(WxMaExpressPrinterUpdateRequest wxMaExpressPrinterUpdateRequest) throws WxErrorException { + this.wxMaService.post(UPDATE_PRINTER_URL,wxMaExpressPrinterUpdateRequest.toJson()); + } + + @Override + public WxMaExpressPrinter getPrinter() throws WxErrorException { + String responseContent = this.wxMaService.get(GET_PRINTER_URL, null); + return WxMaExpressPrinter.fromJson(responseContent); + } + + @Override + public WxMaExpressOrderInfoResult addOrder(WxMaExpressAddOrderRequest wxMaExpressAddOrderRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(ADD_ORDER_URL,wxMaExpressAddOrderRequest.toJson()); + return WxMaExpressOrderInfoResult.fromJson(responseContent); + } + + @Override + public List batchGetOrder(List requests) throws WxErrorException { + Map param = new HashMap<>(1); + param.put("order_list", requests); + String responseContent = this.wxMaService.post(BATCH_GET_ORDER_URL, WxMaGsonBuilder.create().toJson(param)); + return WxMaExpressOrderInfoResult.toList(responseContent); + } + + @Override + public void cancelOrder(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException { + this.wxMaService.post(CANCEL_ORDER_URL,wxMaExpressGetOrderRequest.toJson()); + } + + @Override + public WxMaExpressOrderInfoResult getOrder(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_ORDER_URL,wxMaExpressGetOrderRequest.toJson()); + return WxMaExpressOrderInfoResult.fromJson(responseContent); + } + + @Override + public WxMaExpressPath getPath(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_PATH_URL,wxMaExpressGetOrderRequest.toJson()); + return WxMaExpressPath.fromJson(responseContent); + } + + @Override + public void testUpdateOrder(WxMaExpressTestUpdateOrderRequest wxMaExpressTestUpdateOrderRequest) throws WxErrorException { + this.wxMaService.post(TEST_UPDATE_ORDER_URL,wxMaExpressTestUpdateOrderRequest.toJson()); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImgProcServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImgProcServiceImpl.java new file mode 100644 index 0000000000..0499b7c7e0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImgProcServiceImpl.java @@ -0,0 +1,131 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.api.WxImgProcService; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcAiCropResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcQrCodeResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcSuperResolutionResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.requestexecuter.ocr.OcrDiscernRequestExecutor; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + + +/** + * 图像处理接口实现. + * + * @author Theo Nie + */ +@RequiredArgsConstructor +public class WxMaImgProcServiceImpl implements WxImgProcService { + /** + * 二维码/条码识别 + */ + private static final String QRCODE = "/cv/img/qrcode?img_url=%s"; + + /** + * 二维码/条码识别(文件) + */ + private static final String FILE_QRCODE = "/cv/img/qrcode"; + + /** + * 图片高清化 + */ + private static final String SUPER_RESOLUTION = "/cv/img/superresolution?img_url=%s"; + + /** + * 图片高清化(文件) + */ + private static final String FILE_SUPER_RESOLUTION = "/cv/img/superresolution"; + + /** + * 图片智能裁剪 + */ + private static final String AI_CROP = "/cv/img/aicrop?img_url=%s&ratios=%s"; + + /** + * 图片智能裁剪(文件) + */ + private static final String FILE_AI_CROP = "/cv/img/aicrop?ratios=%s"; + private final WxMaService service; + + @Override + public WxImgProcQrCodeResult qrCode(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + //ignore + } + + final String result = this.service.get(String.format(QRCODE, imgUrl), null); + return WxImgProcQrCodeResult.fromJson(result); + } + + @Override + public WxImgProcQrCodeResult qrCode(File imgFile) throws WxErrorException { + String result = this.service.execute(OcrDiscernRequestExecutor.create(this.service.getRequestHttp()), + FILE_QRCODE, imgFile); + return WxImgProcQrCodeResult.fromJson(result); + } + + @Override + public WxImgProcSuperResolutionResult superResolution(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + //ignore + } + + final String result = this.service.get(String.format(SUPER_RESOLUTION, imgUrl), null); + return WxImgProcSuperResolutionResult.fromJson(result); + } + + @Override + public WxImgProcSuperResolutionResult superResolution(File imgFile) throws WxErrorException { + String result = this.service.execute(OcrDiscernRequestExecutor.create(this.service.getRequestHttp()), + FILE_SUPER_RESOLUTION, imgFile); + return WxImgProcSuperResolutionResult.fromJson(result); + } + + @Override + public WxImgProcAiCropResult aiCrop(String imgUrl) throws WxErrorException { + return this.aiCrop(imgUrl, ""); + } + + @Override + public WxImgProcAiCropResult aiCrop(String imgUrl, String ratios) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + //ignore + } + + if (StringUtils.isEmpty(ratios)) { + ratios = ""; + } + + final String result = this.service.get(String.format(AI_CROP, imgUrl, ratios), null); + return WxImgProcAiCropResult.fromJson(result); + } + + @Override + public WxImgProcAiCropResult aiCrop(File imgFile) throws WxErrorException { + return this.aiCrop(imgFile, ""); + } + + @Override + public WxImgProcAiCropResult aiCrop(File imgFile, String ratios) throws WxErrorException { + if (StringUtils.isEmpty(ratios)) { + ratios = ""; + } + + String result = this.service.execute(OcrDiscernRequestExecutor.create(this.service.getRequestHttp()), + String.format(FILE_AI_CROP, ratios), imgFile); + return WxImgProcAiCropResult.fromJson(result); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImpl.java new file mode 100644 index 0000000000..9177910e36 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImpl.java @@ -0,0 +1,98 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaJsapiService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.RandomUtils; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.json.GsonParser; + +import java.util.concurrent.locks.Lock; + +/** + *
    + *  Created by BinaryWang on 2018/8/5.
    + * 
    + * + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaJsapiServiceImpl implements WxMaJsapiService { + + + private WxMaService wxMaService; + + @Override + public String getCardApiTicket() throws WxErrorException { + return getCardApiTicket(false); + } + + @Override + public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { + Lock lock = this.wxMaService.getWxMaConfig().getCardApiTicketLock(); + lock.lock(); + try { + if (forceRefresh) { + this.wxMaService.getWxMaConfig().expireCardApiTicket(); + } + + if (this.wxMaService.getWxMaConfig().isCardApiTicketExpired()) { + String responseContent = this.wxMaService.get(GET_JSAPI_TICKET_URL + "?type=wx_card", null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); + int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); + this.wxMaService.getWxMaConfig().updateCardApiTicket(jsapiTicket, expiresInSeconds); + } + } finally { + lock.unlock(); + } + return this.wxMaService.getWxMaConfig().getCardApiTicket(); + } + + @Override + public String getJsapiTicket() throws WxErrorException { + return getJsapiTicket(false); + } + + @Override + public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { + Lock lock = this.wxMaService.getWxMaConfig().getJsapiTicketLock(); + lock.lock(); + try { + if (forceRefresh) { + this.wxMaService.getWxMaConfig().expireJsapiTicket(); + } + + if (this.wxMaService.getWxMaConfig().isJsapiTicketExpired()) { + String responseContent = this.wxMaService.get(GET_JSAPI_TICKET_URL + "?type=jsapi", null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); + int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); + this.wxMaService.getWxMaConfig().updateJsapiTicket(jsapiTicket, expiresInSeconds); + } + } finally { + lock.unlock(); + } + return this.wxMaService.getWxMaConfig().getJsapiTicket(); + } + + @Override + public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { + long timestamp = System.currentTimeMillis() / 1000; + String randomStr = RandomUtils.getRandomStr(); + String jsapiTicket = getJsapiTicket(false); + String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, + "noncestr=" + randomStr, "timestamp=" + timestamp, "url=" + url); + return WxJsapiSignature + .builder() + .appId(this.wxMaService.getWxMaConfig().getAppid()) + .timestamp(timestamp) + .nonceStr(randomStr) + .url(url) + .signature(signature) + .build(); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImpl.java new file mode 100644 index 0000000000..3a36a3c75a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImpl.java @@ -0,0 +1,130 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaLiveGoodsService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; +import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *
    + *  Created by lipengjun on 2020/6/29.
    + * 
    + * + * @author lipengjun (939961241@qq.com) + */ +@AllArgsConstructor +public class WxMaLiveGoodsServiceImpl implements WxMaLiveGoodsService { + private final WxMaService wxMaService; + + @Override + public WxMaLiveResult addGoods(WxMaLiveInfo.Goods goods) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("goodsInfo", goods); + String responseContent = this.wxMaService.post(ADD_GOODS, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return WxMaLiveResult.fromJson(jsonObject.toString()); + } + + @Override + public boolean resetAudit(Integer auditId, Integer goodsId) throws WxErrorException { + Map map = new HashMap<>(4); + map.put("auditId", auditId); + map.put("goodsId", goodsId); + String responseContent = this.wxMaService.post(RESET_AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return true; + } + + @Override + public String auditGoods(Integer goodsId) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("goodsId", goodsId); + String responseContent = this.wxMaService.post(AUDIT_GOODS, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return jsonObject.get("auditId").getAsString(); + } + + @Override + public boolean deleteGoods(Integer goodsId) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("goodsId", goodsId); + String responseContent = this.wxMaService.post(DELETE_GOODS, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return true; + } + + @Override + public boolean updateGoods(WxMaLiveInfo.Goods goods) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("goodsInfo", goods); + String responseContent = this.wxMaService.post(UPDATE_GOODS, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return true; + } + + @Override + public WxMaLiveResult getGoodsWareHouse(List goodsIds) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("goods_ids", goodsIds); + String responseContent = this.wxMaService.post(GET_GOODS_WARE_HOUSE, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return WxMaLiveResult.fromJson(jsonObject.toString()); + } + + @Override + public WxMaLiveResult getApprovedGoods(Integer offset, Integer limit, Integer status) throws WxErrorException { + ImmutableMap params = ImmutableMap.of("status", status, "offset", offset, "limit", limit); + String responseContent = wxMaService.get(GET_APPROVED_GOODS, Joiner.on("&").withKeyValueSeparator("=").join(params)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + JsonArray goodsArr = jsonObject.getAsJsonArray("goods"); + if (goodsArr.size() > 0) { + for (int i = 0; i < goodsArr.size(); i++) { + // 接口返回key是驼峰 + JsonObject goods = (JsonObject) goodsArr.get(i); + goods.addProperty("goods_id", goods.get("goodsId").getAsInt()); + goods.addProperty("cover_img_url", goods.get("coverImgUrl").getAsString()); + goods.addProperty("price_type", goods.get("priceType").getAsInt()); + goods.addProperty("third_party_tag", goods.get("thirdPartyTag").getAsInt()); + goods.addProperty("audit_status", status); + } + } + return WxMaLiveResult.fromJson(jsonObject.toString()); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImpl.java new file mode 100644 index 0000000000..3c5abc8781 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImpl.java @@ -0,0 +1,117 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaLiveService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; +import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *
    + *  Created by yjwang on 2020/4/5.
    + * 
    + * + * @author yjwang + */ +@Slf4j +@AllArgsConstructor +public class WxMaLiveServiceImpl implements WxMaLiveService { + private final WxMaService wxMaService; + + @Override + public Integer createRoom(WxMaLiveInfo.RoomInfo roomInfo) throws WxErrorException { + String responseContent = this.wxMaService.post(CREATE_ROOM, WxMaGsonBuilder.create().toJson(roomInfo)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return jsonObject.get("roomId").getAsInt(); + } + + @Override + public WxMaLiveResult getLiveInfo(Integer start, Integer limit) throws WxErrorException { + JsonObject jsonObject = getLiveInfo(start, limit, null); + return WxMaLiveResult.fromJson(jsonObject.toString()); + } + + @Override + public List getLiveInfos() throws WxErrorException { + List results = new ArrayList<>(); + int start = 0; + Integer limit = 80; + Integer total = 0; + WxMaLiveResult liveInfo; + do { + if (total != 0 && total <= start) { + break; + } + liveInfo = getLiveInfo(start, limit); + if (liveInfo == null) { + return null; + } + results.addAll(liveInfo.getRoomInfos()); + total = liveInfo.getTotal(); + start = results.size(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + log.error("InterruptedException", e); + } + } while (results.size() <= total); + + return results; + } + + @Override + public WxMaLiveResult getLiveReplay(String action, Integer roomId, Integer start, Integer limit) throws WxErrorException { + Map map = new HashMap<>(4); + map.put("action", action); + map.put("room_id", roomId); + JsonObject jsonObject = getLiveInfo(start, limit, map); + return WxMaLiveResult.fromJson(jsonObject.toString()); + } + + @Override + public WxMaLiveResult getLiveReplay(Integer roomId, Integer start, Integer limit) throws WxErrorException { + return getLiveReplay("get_replay", roomId, start, limit); + } + + @Override + public boolean addGoodsToRoom(Integer roomId, List goodsIds) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("roomId", roomId); + map.put("ids", goodsIds); + String responseContent = this.wxMaService.post(ADD_GOODS, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return true; + } + + private JsonObject getLiveInfo(Integer start, Integer limit, Map map) throws WxErrorException { + if (map == null) { + map = new HashMap(2); + } + map.put("start", start); + map.put("limit", limit); + String responseContent = wxMaService.post(GET_LIVE_INFO, WxMaGsonBuilder.create().toJson(map)); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return jsonObject; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java new file mode 100644 index 0000000000..b39f50cb41 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java @@ -0,0 +1,53 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaMediaService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestExecutor; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.UUID; + +/** + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaMediaServiceImpl implements WxMaMediaService { + private final WxMaService wxMaService; + + @Override + public WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException { + try { + return this.uploadMedia(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + } catch (IOException e) { + throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e); + } + } + + @Override + public WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException { + String url = String.format(MEDIA_UPLOAD_URL, mediaType); + return this.wxMaService.execute(MediaUploadRequestExecutor.create(this.wxMaService.getRequestHttp()), url, file); + } + + @Override + public File getMedia(String mediaId) throws WxErrorException { + try { + RequestExecutor executor = BaseMediaDownloadRequestExecutor + .create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("wxma").toFile()); + return this.wxMaService.execute(executor, MEDIA_GET_URL, "media_id=" + mediaId); + } catch (IOException e) { + throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e); + } + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java new file mode 100644 index 0000000000..f647a80fee --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java @@ -0,0 +1,57 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaMsgService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.*; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; + +/** + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaMsgServiceImpl implements WxMaMsgService { + private final WxMaService wxMaService; + + @Override + public boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException { + String responseContent = this.wxMaService.post(KEFU_MESSAGE_SEND_URL, message.toJson()); + return responseContent != null; + } + + @Override + public void sendSubscribeMsg(WxMaSubscribeMessage subscribeMessage) throws WxErrorException { + String responseContent = this.wxMaService.post(SUBSCRIBE_MSG_SEND_URL, subscribeMessage.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + } + + @Override + public void sendUniformMsg(WxMaUniformMessage uniformMessage) throws WxErrorException { + String responseContent = this.wxMaService.post(UNIFORM_MSG_SEND_URL, uniformMessage.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + } + + @Override + public JsonObject createUpdatableMessageActivityId() throws WxErrorException { + final String responseContent = this.wxMaService.get(ACTIVITY_ID_CREATE_URL, null); + return GsonParser.parse(responseContent); + } + + @Override + public void setUpdatableMsg(WxMaUpdatableMsg msg) throws WxErrorException { + this.wxMaService.post(UPDATABLE_MSG_SEND_URL, WxMaGsonBuilder.create().toJson(msg)); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOcrServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOcrServiceImpl.java new file mode 100644 index 0000000000..8106dcd6c7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOcrServiceImpl.java @@ -0,0 +1,151 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.api.WxOcrService; +import me.chanjar.weixin.common.bean.ocr.*; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.requestexecuter.ocr.OcrDiscernRequestExecutor; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * ocr 接口实现. + * + * @author Binary Wang + * @date 2019-06-22 + */ +@RequiredArgsConstructor +public class WxMaOcrServiceImpl implements WxOcrService { + private static final String IDCARD = "https://api.weixin.qq.com/cv/ocr/idcard?img_url=%s"; + private static final String FILEIDCARD = "https://api.weixin.qq.com/cv/ocr/idcard"; + private static final String BANK_CARD = "https://api.weixin.qq.com/cv/ocr/bankcard?img_url=%s"; + private static final String FILE_BANK_CARD = "https://api.weixin.qq.com/cv/ocr/bankcard"; + private static final String DRIVING = "https://api.weixin.qq.com/cv/ocr/driving?img_url=%s"; + private static final String FILE_DRIVING = "https://api.weixin.qq.com/cv/ocr/driving"; + private static final String DRIVING_LICENSE = "https://api.weixin.qq.com/cv/ocr/drivinglicense?img_url=%s"; + private static final String FILE_DRIVING_LICENSE = "https://api.weixin.qq.com/cv/ocr/drivinglicense"; + private static final String BIZ_LICENSE = "https://api.weixin.qq.com/cv/ocr/bizlicense?img_url=%s"; + private static final String FILE_BIZ_LICENSE = "https://api.weixin.qq.com/cv/ocr/bizlicense"; + private static final String COMM = "https://api.weixin.qq.com/cv/ocr/comm?img_url=%s"; + private static final String FILE_COMM = "https://api.weixin.qq.com/cv/ocr/comm"; + + private final WxMaService mainService; + + @Override + public WxOcrIdCardResult idCard(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(IDCARD, imgUrl), null); + return WxOcrIdCardResult.fromJson(result); + } + + @Override + public WxOcrIdCardResult idCard(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILEIDCARD, imgFile); + return WxOcrIdCardResult.fromJson(result); + } + + @Override + public WxOcrBankCardResult bankCard(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(BANK_CARD, imgUrl), null); + return WxOcrBankCardResult.fromJson(result); + } + + @Override + public WxOcrBankCardResult bankCard(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_BANK_CARD, imgFile); + return WxOcrBankCardResult.fromJson(result); + } + + @Override + public WxOcrDrivingResult driving(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(DRIVING, imgUrl), null); + return WxOcrDrivingResult.fromJson(result); + } + + @Override + public WxOcrDrivingResult driving(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_DRIVING, imgFile); + return WxOcrDrivingResult.fromJson(result); + } + + @Override + public WxOcrDrivingLicenseResult drivingLicense(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(DRIVING_LICENSE, imgUrl), null); + return WxOcrDrivingLicenseResult.fromJson(result); + } + + @Override + public WxOcrDrivingLicenseResult drivingLicense(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_DRIVING_LICENSE, imgFile); + return WxOcrDrivingLicenseResult.fromJson(result); + } + + @Override + public WxOcrBizLicenseResult bizLicense(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(BIZ_LICENSE, imgUrl), null); + return WxOcrBizLicenseResult.fromJson(result); + } + + @Override + public WxOcrBizLicenseResult bizLicense(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_BIZ_LICENSE, imgFile); + return WxOcrBizLicenseResult.fromJson(result); + } + + @Override + public WxOcrCommResult comm(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(COMM, imgUrl), null); + return WxOcrCommResult.fromJson(result); + } + + @Override + public WxOcrCommResult comm(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_COMM, imgFile); + return WxOcrCommResult.fromJson(result); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaPluginServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaPluginServiceImpl.java new file mode 100644 index 0000000000..134ed66d51 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaPluginServiceImpl.java @@ -0,0 +1,48 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaPluginService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaPluginListResult; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.common.collect.ImmutableMap; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.Map; + +@AllArgsConstructor +public class WxMaPluginServiceImpl implements WxMaPluginService { + private WxMaService wxMaService; + + @Override + public void applyPlugin(String pluginAppId, String reason) throws WxErrorException { + Map params = ImmutableMap.of("action", "apply", + "plugin_appid", pluginAppId, + "reason", reason); + + this.wxMaService.post(PLUGIN_URL, WxMaGsonBuilder.create().toJson(params)); + } + + @Override + public WxMaPluginListResult getPluginList() throws WxErrorException { + Map params = ImmutableMap.of("action", "list"); + + String responseContent = this.wxMaService.post(PLUGIN_URL, WxMaGsonBuilder.create().toJson(params)); + return WxMaPluginListResult.fromJson(responseContent); + } + + @Override + public void unbindPlugin(String pluginAppId) throws WxErrorException { + Map params = ImmutableMap.of("action", "unbind", "plugin_appid", pluginAppId); + this.wxMaService.post(PLUGIN_URL, WxMaGsonBuilder.create().toJson(params)); + } + + @Override + public void updatePlugin(String pluginAppId, String userVersion) throws WxErrorException { + Map params = ImmutableMap.of("action", "update", + "plugin_appid", pluginAppId, + "user_version", userVersion); + + this.wxMaService.post(PLUGIN_URL, WxMaGsonBuilder.create().toJson(params)); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java new file mode 100644 index 0000000000..905aff4a2a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java @@ -0,0 +1,110 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor; +import cn.binarywang.wx.miniapp.bean.WxMaQrcode; +import cn.binarywang.wx.miniapp.bean.WxaCode; +import cn.binarywang.wx.miniapp.bean.WxaCodeUnlimit; +import cn.binarywang.wx.miniapp.util.QrcodeBytesRequestExecutor; +import cn.binarywang.wx.miniapp.util.QrcodeRequestExecutor; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.io.File; + +/** + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaQrcodeServiceImpl implements WxMaQrcodeService { + private WxMaService wxMaService; + + @Override + public byte[] createQrcodeBytes(String path, int width) throws WxErrorException { + final QrcodeBytesRequestExecutor executor = new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, CREATE_QRCODE_URL, new WxMaQrcode(path, width)); + } + + @Override + public File createQrcode(String path, int width) throws WxErrorException { + final QrcodeRequestExecutor executor = new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, CREATE_QRCODE_URL, new WxMaQrcode(path, width)); + } + + @Override + public File createQrcode(String path) throws WxErrorException { + return this.createQrcode(path, 430); + } + + @Override + public byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException { + final QrcodeBytesRequestExecutor executor = new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, GET_WXACODE_URL, WxaCode.builder() + .path(path) + .width(width) + .autoColor(autoColor) + .lineColor(lineColor) + .isHyaline(isHyaline) + .build()); + } + + @Override + public File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException { + final QrcodeRequestExecutor executor = new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, GET_WXACODE_URL, WxaCode.builder() + .path(path) + .width(width) + .autoColor(autoColor) + .lineColor(lineColor) + .isHyaline(isHyaline) + .build()); + } + + @Override + public File createWxaCode(String path, int width) throws WxErrorException { + return this.createWxaCode(path, width, true, null, false); + } + + @Override + public File createWxaCode(String path) throws WxErrorException { + return this.createWxaCode(path, 430); + } + + @Override + public byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { + return this.wxMaService.execute(new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()), + GET_WXACODE_UNLIMIT_URL, + this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline)); + } + + @Override + public File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { + return this.wxMaService.execute(new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()), + GET_WXACODE_UNLIMIT_URL, + this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline)); + } + + private WxaCodeUnlimit buildWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) { + WxaCodeUnlimit wxaCodeUnlimit = new WxaCodeUnlimit(); + wxaCodeUnlimit.setScene(scene); + wxaCodeUnlimit.setPage(page); + wxaCodeUnlimit.setWidth(width); + wxaCodeUnlimit.setAutoColor(autoColor); + wxaCodeUnlimit.setLineColor(lineColor); + wxaCodeUnlimit.setHyaline(isHyaline); + + return wxaCodeUnlimit; + } + + @Override + public File createWxaCodeUnlimit(String scene, String page) throws WxErrorException { + return this.createWxaCodeUnlimit(scene, page, 430, true, null, false); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaRunServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaRunServiceImpl.java new file mode 100644 index 0000000000..5b9cd073f3 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaRunServiceImpl.java @@ -0,0 +1,27 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import java.util.List; + +import cn.binarywang.wx.miniapp.api.WxMaRunService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaRunStepInfo; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import lombok.AllArgsConstructor; + +/** + *
    + *
    + * Created by Binary Wang on 2018/11/4.
    + * 
    + * + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaRunServiceImpl implements WxMaRunService { + private WxMaService service; + + @Override + public List getRunStepInfo(String sessionKey, String encryptedData, String ivStr) { + return WxMaRunStepInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java new file mode 100644 index 0000000000..1dd58bb698 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java @@ -0,0 +1,71 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaSecCheckService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMediaAsyncCheckResult; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +/** + *
    + *
    + * Created by Binary Wang on 2018/11/24.
    + * 
    + * + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaSecCheckServiceImpl implements WxMaSecCheckService { + private WxMaService service; + + @Override + public boolean checkImage(File file) throws WxErrorException { + WxMediaUploadResult result = this.service.execute(MediaUploadRequestExecutor + .create(this.service.getRequestHttp()), IMG_SEC_CHECK_URL, file); + return result != null; + } + + @Override + public boolean checkImage(String fileUrl) throws WxErrorException { + File file = new File(FileUtils.getTempDirectory(), System.currentTimeMillis() + ".tmp"); + try { + URL url = new URL(fileUrl); + FileUtils.copyURLToFile(url, file); + } catch (IOException e) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("文件地址读取异常").build(), e); + } + + return this.checkImage(file); + } + + @Override + public boolean checkMessage(String msgString) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("content", msgString); + + this.service.post(MSG_SEC_CHECK_URL, jsonObject.toString()); + + return true; + } + + @Override + public WxMaMediaAsyncCheckResult mediaCheckAsync(String mediaUrl, int mediaType) + throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("media_url", mediaUrl); + jsonObject.addProperty("media_type", mediaType); + + return WxMaMediaAsyncCheckResult + .fromJson(this.service.post(MEDIA_CHECK_ASYNC_URL, jsonObject.toString())); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceHttpClientImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceHttpClientImpl.java new file mode 100644 index 0000000000..c69772a5d8 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceHttpClientImpl.java @@ -0,0 +1,88 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * @author Binary Wang + */ +@Slf4j +public class WxMaServiceHttpClientImpl extends BaseWxMaServiceImpl { + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + + @Override + public void initHttp() { + WxMaConfig configStorage = this.getWxMaConfig(); + ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); + if (null == apacheHttpClientBuilder) { + apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); + } + + apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) + .httpProxyPort(configStorage.getHttpProxyPort()) + .httpProxyUsername(configStorage.getHttpProxyUsername()) + .httpProxyPassword(configStorage.getHttpProxyPassword()); + + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + } + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.APACHE_HTTP; + } + + @Override + protected String doGetAccessTokenRequest() throws IOException { + String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), this.getWxMaConfig().getSecret()); + + HttpGet httpGet = null; + CloseableHttpResponse response = null; + try { + httpGet = new HttpGet(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + response = getRequestHttpClient().execute(httpGet); + return new BasicResponseHandler().handleResponse(response); + } finally { + if (httpGet != null) { + httpGet.releaseConnection(); + } + if (response != null) { + try { + response.close(); + } catch (IOException e) { + } + } + } + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java new file mode 100644 index 0000000000..16478f8411 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java @@ -0,0 +1,11 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author Binary Wang + */ +@Slf4j +public class WxMaServiceImpl extends WxMaServiceHttpClientImpl { + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceJoddHttpImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceJoddHttpImpl.java new file mode 100644 index 0000000000..984d0d3fe6 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceJoddHttpImpl.java @@ -0,0 +1,59 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.ProxyInfo; +import jodd.http.net.SocketHttpConnectionProvider; +import me.chanjar.weixin.common.util.http.HttpType; + +import java.io.IOException; + +/** + * jodd-http方式实现. + * + * @author someone + */ +public class WxMaServiceJoddHttpImpl extends BaseWxMaServiceImpl { + private HttpConnectionProvider httpClient; + private ProxyInfo httpProxy; + + @Override + public void initHttp() { + WxMaConfig configStorage = this.getWxMaConfig(); + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); + } + this.httpClient = new SocketHttpConnectionProvider(); + } + + @Override + public HttpConnectionProvider getRequestHttpClient() { + return httpClient; + } + + @Override + public ProxyInfo getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.JODD_HTTP; + } + + @Override + protected String doGetAccessTokenRequest() throws IOException { + String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), this.getWxMaConfig().getSecret()); + HttpRequest request = HttpRequest.get(url); + if (this.getRequestHttpProxy() != null) { + SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); + provider.useProxy(getRequestHttpProxy()); + + request.withConnectionProvider(provider); + } + return request.send().bodyText(); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceOkHttpImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceOkHttpImpl.java new file mode 100644 index 0000000000..a36444d97a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceOkHttpImpl.java @@ -0,0 +1,72 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import okhttp3.*; + +import java.io.IOException; +import java.util.Objects; + +/** + * okhttp实现. + */ +public class WxMaServiceOkHttpImpl extends BaseWxMaServiceImpl { + + private OkHttpClient httpClient; + private OkHttpProxyInfo httpProxy; + + @Override + public void initHttp() { + WxMaConfig wxMpConfigStorage = this.getWxMaConfig(); + //设置代理 + if (wxMpConfigStorage.getHttpProxyHost() != null && wxMpConfigStorage.getHttpProxyPort() > 0) { + httpProxy = OkHttpProxyInfo.httpProxy(wxMpConfigStorage.getHttpProxyHost(), + wxMpConfigStorage.getHttpProxyPort(), + wxMpConfigStorage.getHttpProxyUsername(), + wxMpConfigStorage.getHttpProxyPassword()); + } + + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + if (httpProxy != null) { + clientBuilder.proxy(getRequestHttpProxy().getProxy()); + + //设置授权 + clientBuilder.authenticator(new Authenticator() { + @Override + public Request authenticate(Route route, Response response) throws IOException { + String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); + return response.request().newBuilder() + .header("Authorization", credential) + .build(); + } + }); + } + httpClient = clientBuilder.build(); + } + + @Override + public OkHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public OkHttpProxyInfo getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.OK_HTTP; + } + + @Override + protected String doGetAccessTokenRequest() throws IOException { + String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), this.getWxMaConfig().getSecret()); + Request request = new Request.Builder().url(url).get().build(); + try (Response response = getRequestHttpClient().newCall(request).execute()) { + return Objects.requireNonNull(response.body()).string(); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSettingServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSettingServiceImpl.java new file mode 100644 index 0000000000..c69a58d1b1 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSettingServiceImpl.java @@ -0,0 +1,46 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaSettingService; +import cn.binarywang.wx.miniapp.bean.WxMaDomainAction; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Charming + * @since 2018-04-27 15:46 + */ +@AllArgsConstructor +public class WxMaSettingServiceImpl implements WxMaSettingService { + private WxMaService wxMaService; + + @Override + public WxMaDomainAction modifyDomain(WxMaDomainAction domainAction) throws WxErrorException { + String responseContent = this.wxMaService.post(MODIFY_DOMAIN_URL, domainAction.toJson()); + return WxMaDomainAction.fromJson(responseContent); + } + + @Override + public WxMaDomainAction setWebViewDomain(WxMaDomainAction domainAction) throws WxErrorException { + String responseContent = this.wxMaService.post(SET_WEB_VIEW_DOMAIN_URL, domainAction.toJson()); + return WxMaDomainAction.fromJson(responseContent); + } + + @Override + public void bindTester(String wechatId) throws WxErrorException { + Map param = new HashMap<>(1); + param.put("wechatid", wechatId); + this.wxMaService.post(BIND_TESTER_URL, WxMaGsonBuilder.create().toJson(param)); + } + + @Override + public void unbindTester(String wechatId) throws WxErrorException { + Map param = new HashMap<>(1); + param.put("wechatid", wechatId); + this.wxMaService.post(UNBIND_TESTER_URL, WxMaGsonBuilder.create().toJson(param)); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaShareServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaShareServiceImpl.java new file mode 100644 index 0000000000..a9d1f47457 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaShareServiceImpl.java @@ -0,0 +1,21 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaShareService; +import cn.binarywang.wx.miniapp.bean.WxMaShareInfo; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import lombok.AllArgsConstructor; + +/** + * @author zhfish + */ +@AllArgsConstructor +public class WxMaShareServiceImpl implements WxMaShareService { + private WxMaService service; + + @Override + public WxMaShareInfo getShareInfo(String sessionKey, String encryptedData, String ivStr) { + return WxMaShareInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImpl.java new file mode 100644 index 0000000000..8682612a9b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImpl.java @@ -0,0 +1,73 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; +import cn.binarywang.wx.miniapp.bean.template.WxMaPubTemplateTitleListResult; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Binary Wang + * @date 2019-12-15 + */ +@AllArgsConstructor +public class WxMaSubscribeServiceImpl implements WxMaSubscribeService { + private WxMaService wxMaService; + + @Override + public WxMaPubTemplateTitleListResult getPubTemplateTitleList(String[] ids, int start, int limit) throws WxErrorException { + ImmutableMap params = ImmutableMap.of("ids", StringUtils.join(ids, ","), + "start", start, "limit", limit); + String responseText = this.wxMaService.get(GET_PUB_TEMPLATE_TITLE_LIST_URL, + Joiner.on("&").withKeyValueSeparator("=").join(params)); + return WxMaPubTemplateTitleListResult.fromJson(responseText); + } + + @Override + public List getPubTemplateKeyWordsById(String id) throws WxErrorException { + String responseText = this.wxMaService.get(GET_PUB_TEMPLATE_KEY_WORDS_BY_ID_URL, + Joiner.on("&").withKeyValueSeparator("=").join(ImmutableMap.of("tid", id))); + return WxMaGsonBuilder.create().fromJson(GsonParser.parse(responseText) + .getAsJsonArray("data"), new TypeToken>() { + }.getType()); + } + + @Override + public String addTemplate(String id, List keywordIdList, String sceneDesc) throws WxErrorException { + String responseText = this.wxMaService.post(TEMPLATE_ADD_URL, ImmutableMap.of("tid", id, + "kidList", keywordIdList.toArray(), + "sceneDesc", sceneDesc)); + return GsonParser.parse(responseText).get("priTmplId").getAsString(); + } + + @Override + public List getTemplateList() throws WxErrorException { + String responseText = this.wxMaService.get(TEMPLATE_LIST_URL, null); + return WxMaGsonBuilder.create().fromJson(GsonParser.parse(responseText) + .getAsJsonArray("data"), new TypeToken>() { + }.getType()); + } + + @Override + public boolean delTemplate(String templateId) throws WxErrorException { + this.wxMaService.post(TEMPLATE_DEL_URL, ImmutableMap.of("priTmplId", templateId)); + return true; + } + + @Override + public List getCategory() throws WxErrorException { + String responseText = this.wxMaService.get(GET_CATEGORY_URL, null); + return WxMaGsonBuilder.create().fromJson(GsonParser.parse(responseText) + .getAsJsonArray("data"), new TypeToken>() { + }.getType()); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java new file mode 100644 index 0000000000..ea9df1bf89 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java @@ -0,0 +1,65 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaUserService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.SignUtils; +import org.apache.commons.codec.digest.DigestUtils; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaUserServiceImpl implements WxMaUserService { + private WxMaService service; + + @Override + public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException { + return service.jsCode2SessionInfo(jsCode); + } + + @Override + public WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr) { + return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); + } + + @Override + public void setUserStorage(Map kvMap, String sessionKey, String openid) throws WxErrorException { + final WxMaConfig config = this.service.getWxMaConfig(); + JsonObject param = new JsonObject(); + JsonArray array = new JsonArray(); + for (Map.Entry e : kvMap.entrySet()) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("key", e.getKey()); + jsonObject.addProperty("value", e.getValue()); + array.add(jsonObject); + } + param.add("kv_list", array); + String params = param.toString(); + String signature = SignUtils.createHmacSha256Sign(params, sessionKey); + String url = String.format(SET_USER_STORAGE, config.getAppid(), signature, openid, "hmac_sha256"); + this.service.post(url, params); + } + + @Override + public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) { + return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); + } + + @Override + public boolean checkUserInfo(String sessionKey, String rawData, String signature) { + final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey); + return generatedSignature.equals(signature); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/AbstractWxMaQrcodeWrapper.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/AbstractWxMaQrcodeWrapper.java new file mode 100644 index 0000000000..cb444c94c1 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/AbstractWxMaQrcodeWrapper.java @@ -0,0 +1,19 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; + +/** + * 微信二维码(小程序码)包装器. + * + * @author Element + */ +public abstract class AbstractWxMaQrcodeWrapper { + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + + @Override + public String toString() { + return this.toJson(); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/Watermark.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/Watermark.java new file mode 100644 index 0000000000..4747c77f84 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/Watermark.java @@ -0,0 +1,25 @@ +package cn.binarywang.wx.miniapp.bean; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 数据水印. + * + * @author Binary Wang + * @date 2020-05-25 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = true) +public class Watermark implements Serializable { + private static final long serialVersionUID = 2375642809946928650L; + + private String timestamp; + private String appid; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaCodeLineColor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaCodeLineColor.java new file mode 100644 index 0000000000..2afb4c073e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaCodeLineColor.java @@ -0,0 +1,18 @@ +package cn.binarywang.wx.miniapp.bean; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + *
    + * lineColor 包装类
    + * 用于描述二维码(小程序码)颜色(RGB参数值),
    + * 详情请查看文档 https://mp.weixin.qq.com/debug/wxadoc/dev/api/qrcode.html
    + * 
    + * @author Element + */ +@Data +@AllArgsConstructor +public class WxMaCodeLineColor { + private String r = "0", g = "0", b = "0"; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaDomainAction.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaDomainAction.java new file mode 100644 index 0000000000..19a6a1cde9 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaDomainAction.java @@ -0,0 +1,62 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 域名相关操作 + * + * @author Charming + * @since 2018-04-27 15:45 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaDomainAction implements Serializable { + private static final long serialVersionUID = -2898601966852935708L; + /** + * add添加, delete删除, set覆盖, get获取。当参数是get时不需要填四个域名字段 + */ + private String action; + /** + * request合法域名,当action参数是get时不需要此字段。 + */ + @SerializedName("requestdomain") + private List requestDomain; + /** + * socket合法域名,当action参数是get时不需要此字段。 + */ + @SerializedName("wsrequestdomain") + private List wsRequestDomain; + /** + * uploadFile合法域名,当action参数是get时不需要此字段。 + */ + @SerializedName("uploaddomain") + private List uploadDomain; + /** + * downloadFile合法域名,当action参数是get时不需要此字段。 + */ + @SerializedName("downloaddomain") + private List downloadDomain; + /** + * 小程序业务域名,当action参数是get时不需要此字段。 + */ + @SerializedName("webviewdomain") + private List webViewDomain; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + + public static WxMaDomainAction fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaDomainAction.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java new file mode 100644 index 0000000000..85b4767702 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java @@ -0,0 +1,36 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *
    + * code换取session_key接口的响应
    + * 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject
    + * 微信返回报文:{"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"}
    + * 
    + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxMaJscode2SessionResult implements Serializable { + private static final long serialVersionUID = -1060216618475607933L; + + @SerializedName("session_key") + private String sessionKey; + + @SerializedName("openid") + private String openid; + + @SerializedName("unionid") + private String unionid; + + public static WxMaJscode2SessionResult fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaJscode2SessionResult.class); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java new file mode 100644 index 0000000000..73ca435d62 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java @@ -0,0 +1,118 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.builder.ImageMessageBuilder; +import cn.binarywang.wx.miniapp.builder.LinkMessageBuilder; +import cn.binarywang.wx.miniapp.builder.MaPageMessageBuilder; +import cn.binarywang.wx.miniapp.builder.TextMessageBuilder; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * 客服消息. + * + * @author Binary Wang + */ +@Data +public class WxMaKefuMessage implements Serializable { + private static final long serialVersionUID = -9196732086954365246L; + + @SerializedName("touser") + private String toUser; + + @SerializedName("msgtype") + private String msgType; + + @SerializedName("text") + private KfText text; + + @SerializedName("image") + private KfImage image; + + @SerializedName("link") + private KfLink link; + + @SerializedName("miniprogrampage") + private KfMaPage maPage; + + @Data + @AllArgsConstructor + public static class KfText implements Serializable { + private static final long serialVersionUID = 151122958720941270L; + + private String content; + } + + @Data + @AllArgsConstructor + public static class KfImage implements Serializable { + private static final long serialVersionUID = -5409342945117300782L; + + @SerializedName("media_id") + private String mediaId; + } + + @Data + @Builder + public static class KfLink implements Serializable { + private static final long serialVersionUID = -6728776817556127413L; + + private String title; + private String description; + private String url; + + @SerializedName("thumb_url") + private String thumbUrl; + } + + @Data + @Builder + public static class KfMaPage implements Serializable { + private static final long serialVersionUID = -5633492281871634466L; + + private String title; + + @SerializedName("pagepath") + private String pagePath; + + @SerializedName("thumb_media_id") + private String thumbMediaId; + } + + /** + * 获得文本消息builder. + */ + public static TextMessageBuilder newTextBuilder() { + return new TextMessageBuilder(); + } + + /** + * 获得图片消息builder. + */ + public static ImageMessageBuilder newImageBuilder() { + return new ImageMessageBuilder(); + } + + /** + * 获得图文链接消息builder. + */ + public static LinkMessageBuilder newLinkBuilder() { + return new LinkMessageBuilder(); + } + + /** + * 获得图文链接消息builder. + */ + public static MaPageMessageBuilder newMaPageBuilder() { + return new MaPageMessageBuilder(); + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaLiveInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaLiveInfo.java new file mode 100644 index 0000000000..8a98b4a218 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaLiveInfo.java @@ -0,0 +1,60 @@ +package cn.binarywang.wx.miniapp.bean; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 直播接口入参 + * + * @author yjwang + * @date 2020/4/5 + */ +@Data +public class WxMaLiveInfo implements Serializable { + private static final long serialVersionUID = 7285263767524755887L; + + /** + * 直播列表 + */ + @Data + public static class RoomInfo implements Serializable { + private static final long serialVersionUID = 7745775280267417154L; + private String name; + private Integer roomid; + private String coverImg; + private String shareImg; + private Integer liveStatus; + private Long startTime; + private Long endTime; + private String anchorName; + private String anchorWechat; + private String anchorImg; + private Integer type; + private Integer screenType; + private Integer closeLike; + private Integer closeGoods; + private Integer closeComment; + private List goods; + } + + /** + * 商品列表 + */ + @Data + public static class Goods implements Serializable { + private static final long serialVersionUID = 5769245932149287574L; + private Integer goodsId; + private String coverImgUrl; + private String url; + private Integer priceType; + private String price; + private String price2; + private String name; + /** + * 1, 2:表示是为api添加商品,否则是在MP添加商品 + */ + private String thirdPartyTag; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaLiveResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaLiveResult.java new file mode 100644 index 0000000000..2040b4a525 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaLiveResult.java @@ -0,0 +1,130 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + * 直播间操作返回结果
    + * Created by lipengjun on 2020/6/29.
    + * 
    + * + * @author lipengjun (939961241@qq.com) + */ +@Data +public class WxMaLiveResult implements Serializable { + private static final long serialVersionUID = 1L; + private Integer errcode; + private String errmsg; + private Integer total; + private Integer auditId; + private Integer goodsId; + private List goods; + + /** + * 直播间列表 + */ + @SerializedName("room_info") + private List roomInfos; + + /** + * 获取回放源视频列表 + */ + @SerializedName("live_replay") + private List liveReplay; + + public static WxMaLiveResult fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaLiveResult.class); + } + + /** + * 商品列表 + */ + @Data + public static class Goods implements Serializable { + private static final long serialVersionUID = 5769245932149287574L; + @SerializedName("goods_id") + private Integer goodsId; + /** + * 获取商品列表返回的商品图片 + */ + @SerializedName("cover_img_url") + private String coverImgUrl; + /** + * 获取直播间列表返回的商品图片 + */ + @SerializedName("cover_img") + private String coverImg; + private String name; + private String url; + @SerializedName("price_type") + private Integer priceType; + /** + * 0:未审核,1:审核中,2:审核通过,3审核失败 + */ + @SerializedName("audit_status") + private Integer auditStatus; + private String price; + private String price2; + /** + * 1, 2:表示是为api添加商品,否则是在MP添加商品 + */ + @SerializedName("third_party_tag") + private String thirdPartyTag; + } + + /** + * 直播列表 + */ + @Data + public static class RoomInfo implements Serializable { + private static final long serialVersionUID = 7745775280267417154L; + private String name; + @SerializedName("roomid") + private Integer roomId; + @SerializedName("cover_img") + private String coverImg; + @SerializedName("share_img") + private String shareImg; + @SerializedName("live_status") + private Integer liveStatus; + @SerializedName("start_time") + private Long startTime; + @SerializedName("end_time") + private Long endTime; + @SerializedName("anchor_name") + private String anchorName; + @SerializedName("anchor_wechat") + private String anchorWechat; + @SerializedName("anchor_img") + private String anchorImg; + private Integer type; + @SerializedName("screen_type") + private Integer screenType; + @SerializedName("close_like") + private Integer closeLike; + @SerializedName("closeGoods") + private Integer closeGoods; + @SerializedName("close_comment") + private Integer closeComment; + private List goods; + } + + /** + * 回放数据列表 + */ + @Data + public static class LiveReplay implements Serializable { + private static final long serialVersionUID = 7683927205627536320L; + @SerializedName("expire_time") + private String expireTime; + @SerializedName("create_time") + private String createTime; + @SerializedName("media_url") + private String mediaUrl; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMediaAsyncCheckResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMediaAsyncCheckResult.java new file mode 100644 index 0000000000..e7fda61a02 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMediaAsyncCheckResult.java @@ -0,0 +1,30 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author borisbao + */ +@Data +public class WxMaMediaAsyncCheckResult implements Serializable { + private static final long serialVersionUID = 3928132365399916183L; + + /** + * 任务id,用于匹配异步推送结果 + */ + @SerializedName("trace_id") + private String traceId; + + + public static WxMaMediaAsyncCheckResult fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaMediaAsyncCheckResult.class); + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java new file mode 100644 index 0000000000..4e06e42394 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java @@ -0,0 +1,212 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import cn.binarywang.wx.miniapp.util.xml.XStreamTransformer; +import com.google.gson.annotations.SerializedName; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +/** + * @author Binary Wang + */ +@XStreamAlias("xml") +@Data +public class WxMaMessage implements Serializable { + private static final long serialVersionUID = -3586245291677274914L; + + @SerializedName("Encrypt") + @XStreamAlias("Encrypt") + @XStreamConverter(value = XStreamCDataConverter.class) + private String encrypt; + + @SerializedName("ToUserName") + @XStreamAlias("ToUserName") + @XStreamConverter(value = XStreamCDataConverter.class) + private String toUser; + + @SerializedName("FromUserName") + @XStreamAlias("FromUserName") + @XStreamConverter(value = XStreamCDataConverter.class) + private String fromUser; + + @SerializedName("CreateTime") + @XStreamAlias("CreateTime") + private Integer createTime; + + @SerializedName("MsgType") + @XStreamAlias("MsgType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String msgType; + + @SerializedName("MsgDataFormat") + @XStreamAlias("MsgDataFormat") + @XStreamConverter(value = XStreamCDataConverter.class) + private String msgDataFormat; + + @SerializedName("Content") + @XStreamAlias("Content") + @XStreamConverter(value = XStreamCDataConverter.class) + private String content; + + @SerializedName("MsgId") + @XStreamAlias("MsgId") + private Long msgId; + + @SerializedName("PicUrl") + @XStreamAlias("PicUrl") + @XStreamConverter(value = XStreamCDataConverter.class) + private String picUrl; + + @SerializedName("MediaId") + @XStreamAlias("MediaId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String mediaId; + + @SerializedName("Event") + @XStreamAlias("Event") + @XStreamConverter(value = XStreamCDataConverter.class) + private String event; + + @SerializedName("Title") + @XStreamAlias("Title") + @XStreamConverter(value = XStreamCDataConverter.class) + private String title; + + @SerializedName("AppId") + @XStreamAlias("AppId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String appId; + + @SerializedName("PagePath") + @XStreamAlias("PagePath") + @XStreamConverter(value = XStreamCDataConverter.class) + private String pagePath; + + @SerializedName("ThumbUrl") + @XStreamAlias("ThumbUrl") + @XStreamConverter(value = XStreamCDataConverter.class) + private String thumbUrl; + + @SerializedName("ThumbMediaId") + @XStreamAlias("ThumbMediaId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String thumbMediaId; + + @SerializedName("SessionFrom") + @XStreamAlias("SessionFrom") + @XStreamConverter(value = XStreamCDataConverter.class) + private String sessionFrom; + + /** + * 以下是异步校验图片/音频是否含有违法违规内容的异步检测结果推送报文中的参数 + */ + @SerializedName("isrisky") + @XStreamAlias("isrisky") + @XStreamConverter(value = XStreamCDataConverter.class) + private String isRisky; + + @SerializedName("extra_info_json") + @XStreamAlias("extra_info_json") + @XStreamConverter(value = XStreamCDataConverter.class) + private String extraInfoJson; + + @SerializedName("appid") + @XStreamAlias("appid") + @XStreamConverter(value = XStreamCDataConverter.class) + private String appid; + + @SerializedName("trace_id") + @XStreamAlias("trace_id") + @XStreamConverter(value = XStreamCDataConverter.class) + private String traceId; + + @SerializedName("status_code") + @XStreamAlias("status_code") + @XStreamConverter(value = XStreamCDataConverter.class) + private String statusCode; + + @SerializedName("Scene") + @XStreamAlias("Scene") + private Integer scene; + + @SerializedName("Query") + @XStreamAlias("Query") + @XStreamConverter(value = XStreamCDataConverter.class) + private String query; + + public static WxMaMessage fromXml(String xml) { + return XStreamTransformer.fromXml(WxMaMessage.class, xml); + } + + public static WxMaMessage fromXml(InputStream is) { + return XStreamTransformer.fromXml(WxMaMessage.class, is); + } + + /** + * 从加密字符串转换. + * + * @param encryptedXml 密文 + * @param wxMaConfig 配置存储器对象 + * @param timestamp 时间戳 + * @param nonce 随机串 + * @param msgSignature 签名串 + */ + public static WxMaMessage fromEncryptedXml(String encryptedXml, + WxMaConfig wxMaConfig, String timestamp, String nonce, + String msgSignature) { + String plainText = new WxMaCryptUtils(wxMaConfig).decrypt(msgSignature, timestamp, nonce, encryptedXml); + return fromXml(plainText); + } + + public static WxMaMessage fromEncryptedXml(InputStream is, WxMaConfig wxMaConfig, String timestamp, + String nonce, String msgSignature) { + try { + return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxMaConfig, + timestamp, nonce, msgSignature); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static WxMaMessage fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaMessage.class); + } + + public static WxMaMessage fromEncryptedJson(String encryptedJson, WxMaConfig config) { + try { + WxMaMessage encryptedMessage = fromJson(encryptedJson); + String plainText = new WxMaCryptUtils(config).decrypt(encryptedMessage.getEncrypt()); + return fromJson(plainText); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static WxMaMessage fromEncryptedJson(InputStream inputStream, WxMaConfig config) { + try { + return fromEncryptedJson(IOUtils.toString(inputStream, StandardCharsets.UTF_8), config); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + return this.toJson(); + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaPhoneNumberInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaPhoneNumberInfo.java new file mode 100644 index 0000000000..149ecbebe8 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaPhoneNumberInfo.java @@ -0,0 +1,26 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.Data; + +import java.io.Serializable; + +/** + * 微信用户绑定的手机号相关信息 + * + * @author Binary Wang + */ +@Data +public class WxMaPhoneNumberInfo implements Serializable { + private static final long serialVersionUID = 6719822331555402137L; + + private String phoneNumber; + private String purePhoneNumber; + private String countryCode; + private Watermark watermark; + + public static WxMaPhoneNumberInfo fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaPhoneNumberInfo.class); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaPluginListResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaPluginListResult.java new file mode 100644 index 0000000000..55be8d33e8 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaPluginListResult.java @@ -0,0 +1,35 @@ +package cn.binarywang.wx.miniapp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +@Data +public class WxMaPluginListResult implements Serializable { + private static final long serialVersionUID = -5898572369543593656L; + + @SerializedName("plugin_list") + private List pluginList; + + public static WxMaPluginListResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMaPluginListResult.class); + } + + @Data + public static class PluginInfo { + + @SerializedName("appid") + private String appId; + + private String status; + + @SerializedName("nickname") + private String nickName; + + @SerializedName("headimgurl") + private String headImgUrl; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java new file mode 100644 index 0000000000..5c17cd1e58 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java @@ -0,0 +1,32 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxMaQrcode extends AbstractWxMaQrcodeWrapper implements Serializable { + private static final long serialVersionUID = 5777119669111011584L; + private String path; + private int width = 430; + + public WxMaQrcode(String path, int width) { + this.path = path; + this.width = width; + } + + public static WxMaQrcode fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaQrcode.class); + } + + @Override + public String toString() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaRunStepInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaRunStepInfo.java new file mode 100644 index 0000000000..5e6ff641c4 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaRunStepInfo.java @@ -0,0 +1,38 @@ +package cn.binarywang.wx.miniapp.bean; + +import java.io.Serializable; +import java.util.List; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; + +/** + * 微信运动步数信息. + * + * @author Binary Wang + */ +@Data +public class WxMaRunStepInfo implements Serializable { + + private static final long serialVersionUID = -7496372171398607044L; + + /** + * 时间戳,表示数据对应的时间. + */ + private Long timestamp; + + /** + * 微信运动步数. + */ + private Integer step; + + public static List fromJson(String json) { + JsonObject jsonObject = GsonParser.parse(json); + return WxMaGsonBuilder.create().fromJson(jsonObject.get("stepInfoList").toString(), + new TypeToken>() { + }.getType()); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaShareInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaShareInfo.java new file mode 100644 index 0000000000..91aff519c0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaShareInfo.java @@ -0,0 +1,20 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author zhfish + */ +@Data +public class WxMaShareInfo implements Serializable { + private static final long serialVersionUID = -8053613683499632226L; + + private String openGId; + + public static WxMaShareInfo fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaShareInfo.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMessage.java new file mode 100644 index 0000000000..791687b6c0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMessage.java @@ -0,0 +1,99 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.*; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 订阅消息. + * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html + * + * @author S + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class WxMaSubscribeMessage implements Serializable { + private static final long serialVersionUID = 6846729898251286686L; + + /** + * 接收者(用户)的 openid. + *
    +   * 参数:touser
    +   * 是否必填: 是
    +   * 描述: 接收者(用户)的 openid
    +   * 
    + */ + private String toUser; + + /** + * 所需下发的模板消息的id. + *
    +   * 参数:template_id
    +   * 是否必填: 是
    +   * 描述: 所需下发的模板消息的id
    +   * 
    + */ + private String templateId; + + /** + * 点击模板卡片后的跳转页面,仅限本小程序内的页面. + *
    +   * 参数:page
    +   * 是否必填: 否
    +   * 描述: 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
    +   * 
    + */ + private String page; + + /** + * 模板内容,不填则下发空模板. + *
    +   * 参数:data
    +   * 是否必填: 是
    +   * 描述: 模板内容,不填则下发空模板
    +   * 
    + */ + private List data; + + /** + * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 + */ + private String miniprogramState = WxMaConstants.MiniprogramState.FORMAL; + + /** + * 进入小程序查看的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN + */ + private String lang = WxMaConstants.MiniprogramLang.ZH_CN; + + public WxMaSubscribeMessage addData(Data datum) { + if (this.data == null) { + this.data = new ArrayList<>(); + } + + this.data.add(datum); + + return this; + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + + @lombok.Data + @NoArgsConstructor + @AllArgsConstructor + public static class Data implements Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String value; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateData.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateData.java new file mode 100644 index 0000000000..040edda4d0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateData.java @@ -0,0 +1,33 @@ +package cn.binarywang.wx.miniapp.bean; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *
    + * 参考文档 https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html
    + * Created by Binary Wang on 2018/9/23.
    + * 
    + * + * @author Binary Wang + */ +@Data +@NoArgsConstructor +public class WxMaTemplateData { + private String name; + private String value; + private String color; + + public WxMaTemplateData(String name, String value) { + this.name = name; + this.value = value; + } + + public WxMaTemplateData(String name, String value, String color) { + this.name = name; + this.value = value; + this.color = color; + } + + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUniformMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUniformMessage.java new file mode 100644 index 0000000000..7515bdbe25 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUniformMessage.java @@ -0,0 +1,114 @@ +package cn.binarywang.wx.miniapp.bean; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 模板消息. + * 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 + * + * @author Binary Wang + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class WxMaUniformMessage implements Serializable { + private static final long serialVersionUID = 5063374783759519418L; + + /** + * 是否发送公众号模版消息,否则发送小程序模版消息. + */ + private boolean isMpTemplateMsg; + + /** + * 用户openid. + * 可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid + */ + private String toUser; + + /** + * 公众号appid,要求与小程序有绑定且同主体. + */ + private String appid; + + /** + * 公众号或小程序模板ID. + */ + private String templateId; + + /** + * 公众号模板消息所要跳转的url. + */ + private String url; + + /** + * 小程序页面路径. + */ + private String page; + + /** + * 小程序模板消息formid. + */ + private String formId; + + /** + * 公众号模板消息所要跳转的小程序,小程序的必须与公众号具有绑定关系. + */ + private MiniProgram miniProgram; + + /** + * 小程序模板数据. + */ + private List data; + + /** + * 模板需要放大的关键词,不填则默认无放大. + */ + private String emphasisKeyword; + + public WxMaUniformMessage addData(WxMaTemplateData datum) { + if (this.data == null) { + this.data = new ArrayList<>(); + } + this.data.add(datum); + + return this; + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class MiniProgram implements Serializable { + private static final long serialVersionUID = -7945254706501974849L; + + private String appid; + private String pagePath; + + /** + * 是否使用path,否则使用pagepath. + * 加入此字段是基于微信官方接口变化多端的考虑 + */ + private boolean usePath = false; + + /** + * 是否使用pagePath,否则使用pagepath. + * 加入此字段是基于微信官方接口变化多端的考虑 + */ + private boolean usePagePath = false; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUpdatableMsg.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUpdatableMsg.java new file mode 100644 index 0000000000..98429b850c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUpdatableMsg.java @@ -0,0 +1,76 @@ +package cn.binarywang.wx.miniapp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 动态消息. + * + * @author Binary Wang + * @date 2020-02-17 + */ +@Data +@Accessors(chain = true) +public class WxMaUpdatableMsg implements Serializable { + private static final long serialVersionUID = 6231957192034798165L; + + /** + * 动态消息的 ID,通过 updatableMessage.createActivityId 接口获取 + */ + @SerializedName("activity_id") + private String activityId; + + /** + * 动态消息修改后的状态 + * 0 未开始 + * 1 已开始 + */ + @SerializedName("target_state") + private Integer targetState; + + /** + * 动态消息对应的模板信息 + */ + @SerializedName("template_info") + private TemplateInfo templateInfo; + + @Data + @Accessors(chain = true) + public static class TemplateInfo implements Serializable { + private static final long serialVersionUID = -9218473401759062841L; + + /** + * 模板中需要修改的参数 + */ + @SerializedName("parameter_list") + private List parameterList; + } + + @Data + @Accessors(chain = true) + public static class Parameter implements Serializable { + private static final long serialVersionUID = 7444716050341038046L; + + /** + * 要修改的参数名 + *
    +     * 合法值:
    +     * member_count	target_state = 0 时必填,文字内容模板中 member_count 的值
    +     * room_limit	target_state = 0 时必填,文字内容模板中 room_limit 的值
    +     * path	target_state = 1 时必填,点击「进入」启动小程序时使用的路径。对于小游戏,没有页面的概念,可以用于传递查询字符串(query),如 "?foo=bar"
    +     * version_type	target_state = 1 时必填,点击「进入」启动小程序时使用的版本。
    +     * 有效参数值为:develop(开发版),trial(体验版),release(正式版)
    +     * 
    + */ + private String name; + + /** + * 修改后的参数值 + */ + private String value; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java new file mode 100644 index 0000000000..368fa772cc --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java @@ -0,0 +1,30 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Binary Wang + */ +@Data +public class WxMaUserInfo implements Serializable { + private static final long serialVersionUID = 6719822331555402137L; + + private String openId; + private String nickName; + private String gender; + private String language; + private String city; + private String province; + private String country; + private String avatarUrl; + private String unionId; + private Watermark watermark; + + public static WxMaUserInfo fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaUserInfo.class); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java new file mode 100644 index 0000000000..2d94f05cea --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java @@ -0,0 +1,48 @@ +package cn.binarywang.wx.miniapp.bean; + +import java.io.Serializable; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 小程序码. + * + * @author Element + * @date 2017/7/27 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxaCode extends AbstractWxMaQrcodeWrapper implements Serializable { + private static final long serialVersionUID = 1287399621649210322L; + + private String path; + + @Builder.Default + private int width = 430; + + @SerializedName("auto_color") + @Builder.Default + private boolean autoColor = true; + + @SerializedName("is_hyaline") + @Builder.Default + private boolean isHyaline = false; + + @SerializedName("line_color") + @Builder.Default + private WxMaCodeLineColor lineColor = new WxMaCodeLineColor("0", "0", "0"); + + public static WxaCode fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxaCode.class); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCodeUnlimit.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCodeUnlimit.java new file mode 100644 index 0000000000..05bf134c6b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCodeUnlimit.java @@ -0,0 +1,38 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 小程序码接口B. + * + * @author Element + * @date 2017/7/27 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxaCodeUnlimit extends AbstractWxMaQrcodeWrapper implements Serializable { + private static final long serialVersionUID = 4782193774524960401L; + private String scene; + private String page; + + private int width = 430; + + @SerializedName("auto_color") + private boolean autoColor = true; + + @SerializedName("is_hyaline") + private boolean isHyaline = false; + + @SerializedName("line_color") + private WxMaCodeLineColor lineColor = new WxMaCodeLineColor("0", "0", "0"); + + public static WxaCodeUnlimit fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxaCodeUnlimit.class); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaRetainInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaRetainInfo.java new file mode 100644 index 0000000000..7021a180e9 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaRetainInfo.java @@ -0,0 +1,43 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 访问留存 + * + * @author Charming + * @since 2018-04-28 14:41 + */ +@Data +public class WxMaRetainInfo implements Serializable { + private static final long serialVersionUID = 8986403173656848413L; + /** + * 日留存:日期,yyyyMMdd 格式,如 20170313 + * 周留存:时间,如"20170306-20170312" + * 月留存:时间,如"201702" + */ + @SerializedName(value = "refDate", alternate = "ref_date") + private String refDate; + /** + * 新增用户留存 + * - key: + * - 日留存:标识,0开始,0表示当天,1表示1天后,依此类推,key取值分别是:0,1,2,3,4,5,6,7,14,30 + * - 周留存:标识,0开始,0表示当周,1表示1周后,依此类推,key 取值分别是:0,1,2,3,4 + * - 月留存:标识,0开始,0表示当月,1表示1月后,key取值分别是:0,1 + * - value: key对应日期的新增用户数/活跃用户数(key=0时)或留存用户数(k>0时) + */ + private Map visitUvNew; + /** + * 活跃用户留存 + */ + private Map visitUv; + + public static WxMaRetainInfo fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaRetainInfo.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaSummaryTrend.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaSummaryTrend.java new file mode 100644 index 0000000000..f4ceb0ad95 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaSummaryTrend.java @@ -0,0 +1,37 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 小程序概况趋势 + * + * @author Charming + * @since 2018-04-28 + */ +@Data +public class WxMaSummaryTrend implements Serializable { + private static final long serialVersionUID = 1379688517709317935L; + /** + * 日期,yyyyMMdd 格式,如 20170313 + */ + @SerializedName(value = "refDate", alternate = "ref_date") + private String refDate; + /** + * 累计用户数 + */ + @SerializedName(value = "visitTotal", alternate = "visit_total") + private Long visitTotal; + /** + * 转发次数 + */ + @SerializedName(value = "sharePv", alternate = "share_pv") + private Long sharePv; + /** + * 转发人数 + */ + @SerializedName(value = "shareUv", alternate = "share_uv") + private Long shareUv; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaUserPortrait.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaUserPortrait.java new file mode 100644 index 0000000000..5e1164909e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaUserPortrait.java @@ -0,0 +1,68 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 用户画像 + * + * @author Charming + * @since 2018-04-28 + */ +@Data +public class WxMaUserPortrait implements Serializable { + private static final long serialVersionUID = 5653571047669243178L; + /** + * 时间范围,如: "20170611-20170617" + */ + private String refDate; + /** + * 新用户 + */ + private Item visitUvNew; + /** + * 活跃用户 + */ + private Item visitUv; + + public static WxMaUserPortrait fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaUserPortrait.class); + } + + @Data + public static class Item { + /** + * key: 省份,如北京、广东等 + * value: 活跃用户数或新用户数 + */ + private Map province; + /** + * key: 城市,如北京、广州等 + * value: 活跃用户数或新用户数 + */ + private Map city; + /** + * key: 性别,包括男、女、未知 + * value: 活跃用户数或新用户数 + */ + private Map genders; + /** + * key: 终端类型,包括iPhone, android,其他 + * value: 活跃用户数或新用户数 + */ + private Map platforms; + /** + * key: 机型,如苹果iPhone6, OPPO R9等 + * value: 活跃用户数或新用户数 + */ + private Map devices; + /** + * key: 年龄,包括17岁以下、18-24岁等区间 + * value: 活跃用户数或新用户数 + */ + private Map ages; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitDistribution.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitDistribution.java new file mode 100644 index 0000000000..1655eec286 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitDistribution.java @@ -0,0 +1,83 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 访问分布 + * 访问来源:(index="access_source_session_cnt") + * 1:小程序历史列表 + * 2:搜索 + * 3:会话 + * 4:二维码 + * 5:公众号主页 + * 6:聊天顶部 + * 7:系统桌面 + * 8:小程序主页 + * 9:附近的小程序 + * 10:其他 + * 11:模板消息 + * 12:客服消息 + * 13: 公众号菜单 + * 14: APP分享 + * 15: 支付完成页 + * 16: 长按识别二维码 + * 17: 相册选取二维码 + * 18: 公众号文章 + * 19:钱包 + * 20:卡包 + * 21:小程序内卡券 + * 22:其他小程序 + * 23:其他小程序返回 + * 24:卡券适用门店列表 + * 25:搜索框快捷入口 + * 26:小程序客服消息 + * 27:公众号下发 + * 访问时长:(index="access_staytime_info") + * 1: 0-2s + * 2: 3-5s + * 3: 6-10s + * 4: 11-20s + * 5: 20-30s + * 6: 30-50s + * 7: 50-100s + * 8: > 100s + * 平均访问深度:(index="access_depth_info") + * 1: 1页 + * 2: 2页 + * 3: 3页 + * 4: 4页 + * 5: 5页 + * 6: 6-10页 + * 7: >10页 + * + * @author Charming + * @since 2018-04-28 + */ +@Data +public class WxMaVisitDistribution implements Serializable { + private static final long serialVersionUID = 5404250039495926632L; + /** + * 日期,yyyyMMdd 格式,如 20170313 + */ + @SerializedName(value = "refDate", alternate = "ref_date") + private String refDate; + /** + * key: 分布类型 + * - access_source_session_cnt 访问来源分布 + * - access_staytime_info 访问时长分布 + * - access_depth_info 访问深度的分布 + * value: 场景 ID 下的值 + * - key: 场景 ID + * - value: 场景下的值 + */ + private Map> list; + + public static WxMaVisitDistribution fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaVisitDistribution.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitPage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitPage.java new file mode 100644 index 0000000000..705cf6b49e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitPage.java @@ -0,0 +1,55 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Charming + * @since 2018-04-28 + */ +@Data +public class WxMaVisitPage implements Serializable { + private static final long serialVersionUID = -7006334774516877372L; + /** + * 页面路径 + */ + @SerializedName(value = "pagePath", alternate = "page_path") + private String pagePath; + /** + * 访问次数 + */ + @SerializedName(value = "pageVisitPv", alternate = "page_visit_pv") + private Long pageVisitPv; + /** + * 访问人数 + */ + @SerializedName(value = "pageVisitUv", alternate = "page_visit_uv") + private Long pageVisitUv; + /** + * 次均停留时长 + */ + @SerializedName(value = "pageStayTimePv", alternate = "page_staytime_pv") + private Float pageStayTimePv; + /** + * 进入页次数 + */ + @SerializedName(value = "entryPagePv", alternate = "entrypage_pv") + private Long entryPagePv; + /** + * 退出页次数 + */ + @SerializedName(value = "exitPagePv", alternate = "exitpage_pv") + private Long exitPagePv; + /** + * 转发次数 + */ + @SerializedName(value = "pageSharePv", alternate = "page_share_pv") + private Long pageSharePv; + /** + * 转发人数 + */ + @SerializedName(value = "pageShareUv", alternate = "page_share_uv") + private Long pageShareUv; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitTrend.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitTrend.java new file mode 100644 index 0000000000..72223ef618 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitTrend.java @@ -0,0 +1,59 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 访问趋势 + * + * @author Charming + * @since 2018-04-28 + */ +@Data +public class WxMaVisitTrend implements Serializable { + private static final long serialVersionUID = 1379688517709317935L; + /** + * 日留存:日期,yyyyMMdd 格式,如 20170313 + * 周留存:时间,如"20170306-20170312" + * 月留存:时间,如"201702" + */ + @SerializedName(value = "refDate", alternate = "ref_date") + private String refDate; + /** + * 打开次数 + */ + @SerializedName(value = "sessionCnt", alternate = "session_cnt") + private Long sessionCnt; + /** + * 访问次数 + */ + @SerializedName(value = "visitPv", alternate = "visit_pv") + private Long visitPv; + /** + * 访问人数 + */ + @SerializedName(value = "visitUv", alternate = "visit_uv") + private Long visitUv; + /** + * 新用户数 + */ + @SerializedName(value = "visitUvNew", alternate = "visit_uv_new") + private Long visitUvNew; + /** + * 人均停留时长 (浮点型,单位:秒) + */ + @SerializedName(value = "stayTimeUv", alternate = "stay_time_uv") + private Float stayTimeUv; + /** + * 人均停留时长 (浮点型,单位:秒) + */ + @SerializedName(value = "stayTimeSession", alternate = "stay_time_session") + private Float stayTimeSession; + /** + * 人均停留时长 (浮点型,单位:秒) + */ + @SerializedName(value = "visitDepth", alternate = "visit_depth") + private Float visitDepth; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/package-info.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/package-info.java new file mode 100644 index 0000000000..e4e68c7805 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/analysis/package-info.java @@ -0,0 +1,7 @@ +/** + * 数据分析 + * + * @author Charming + * @since 2018-04-28 + */ +package cn.binarywang.wx.miniapp.bean.analysis; diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDeleteFileResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDeleteFileResult.java new file mode 100644 index 0000000000..52a70037f7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDeleteFileResult.java @@ -0,0 +1,44 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 文件删除结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudBatchDeleteFileResult implements Serializable { + private static final long serialVersionUID = -1133274298839868115L; + + @SerializedName("delete_list") + private List fileList; + + @Data + public static class FileDownloadInfo implements Serializable { + private static final long serialVersionUID = 5812969045277862211L; + + /** + * fileid string 文件ID + */ + @SerializedName("fileid") + private String fileId; + + /** + * status number 状态码 + */ + @SerializedName("status") + private Integer status; + + /** + * errmsg string 该文件错误信息 + */ + @SerializedName("errmsg") + private String errMsg; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDownloadFileResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDownloadFileResult.java new file mode 100644 index 0000000000..1519e72318 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDownloadFileResult.java @@ -0,0 +1,50 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 获取文件下载链接结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudBatchDownloadFileResult implements Serializable { + private static final long serialVersionUID = 646423661372964402L; + + @SerializedName("file_list") + private List fileList; + + @Data + public static class FileDownloadInfo implements Serializable { + private static final long serialVersionUID = 5812969045277862211L; + + /** + * fileid string 文件ID + */ + @SerializedName("fileid") + private String fileId; + + /** + * download_url string 下载链接 + */ + @SerializedName("download_url") + private String downloadUrl; + + /** + * status number 状态码 + */ + @SerializedName("status") + private Integer status; + + /** + * errmsg string 该文件错误信息 + */ + @SerializedName("errmsg") + private String errMsg; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudCloudDatabaseMigrateQueryInfoResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudCloudDatabaseMigrateQueryInfoResult.java new file mode 100644 index 0000000000..b2639c5545 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudCloudDatabaseMigrateQueryInfoResult.java @@ -0,0 +1,47 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发数据库迁移状态查询结果. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Data +public class WxCloudCloudDatabaseMigrateQueryInfoResult implements Serializable { + private static final long serialVersionUID = 2014197503355968243L; + + /** + * status string 导出状态 + */ + private String status; + + /** + * record_success number 导出成功记录数 + */ + @SerializedName("record_success") + private Integer recordSuccess; + + /** + * record_fail number 导出失败记录数 + */ + @SerializedName("record_fail") + private Integer recordFail; + + /** + * err_msg string 导出错误信息 + */ + @SerializedName("err_msg") + private String errMsg; + + /** + * file_url string 导出文件下载地址 + */ + @SerializedName("file_url") + private String fileUrl; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCollectionGetResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCollectionGetResult.java new file mode 100644 index 0000000000..347762b4c1 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCollectionGetResult.java @@ -0,0 +1,86 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发获取集合接口的结果. + * + * @author Binary Wang + * @date 2020-01-28 + */ +@Data +public class WxCloudDatabaseCollectionGetResult implements Serializable { + private static final long serialVersionUID = 3702855196387039823L; + + /** + * 分页信息 + */ + private WxCloudDatabaseQueryResult.Pager pager; + + /** + * 查询结果 + */ + private CollectionInfo[] collections; + + @Data + public static class CollectionInfo implements Serializable { + private static final long serialVersionUID = -3280126948752330438L; + + /** + * name string 集合名 + */ + @SerializedName("name") + private String name; + + /** + * count number 表中文档数量 + */ + @SerializedName("count") + private Long count; + + /** + * size number 表的大小(即表中文档总大小),单位:字节 + */ + @SerializedName("size") + private Long size; + + /** + * index_count number 索引数量 + */ + @SerializedName("index_count") + private Long indexCount; + + /** + * index_size number 索引占用大小,单位:字节 + */ + @SerializedName("index_size") + private Long indexSize; + } + + @Data + public static class Pager implements Serializable { + private static final long serialVersionUID = 5045727687673687839L; + + /** + * Offset number 偏移 + */ + @SerializedName("Offset") + private Long offset; + + /** + * Limit number 单次查询限制 + */ + @SerializedName("Limit") + private Long limit; + + /** + * Total number 符合查询条件的记录总数 + */ + @SerializedName("Total") + private Long total; + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCreateIndexRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCreateIndexRequest.java new file mode 100644 index 0000000000..9b551db255 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCreateIndexRequest.java @@ -0,0 +1,58 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 云开发新增索引的请求对象. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Accessors(chain = true) +@Data +public class WxCloudDatabaseCreateIndexRequest implements Serializable { + private static final long serialVersionUID = -8308393731157121109L; + + /** + * name string 是 索引名 + */ + private String name; + + /** + * unique boolean 是 是否唯一 + */ + private boolean unique; + + /** + * keys Array. 是 索引字段 + */ + private List keys; + + @Data + @Accessors(chain = true) + public static class IndexKey implements Serializable { + private static final long serialVersionUID = -252641130547960325L; + + /** + * name string 是 字段名 + */ + private String name; + + /** + * direction string 是 字段排序 + *
    +     *   direction 的合法值
    +     * 值	说明
    +     * "1"	升序
    +     * "-1"	降序
    +     * "2dsphere"	地理位置
    +     *
    +     * 
    + */ + private String direction; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseQueryResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseQueryResult.java new file mode 100644 index 0000000000..19ca4dce1f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseQueryResult.java @@ -0,0 +1,51 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发数据库查询记录接口请求结果. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Data +public class WxCloudDatabaseQueryResult implements Serializable { + private static final long serialVersionUID = 8291820029137872536L; + + /** + * 分页信息 + */ + private Pager pager; + + /** + * 查询结果 + */ + private String[] data; + + @Data + public static class Pager implements Serializable{ + private static final long serialVersionUID = 8556239063823985674L; + + /** + * Offset number 偏移 + */ + @SerializedName("Offset") + private Long offset; + + /** + * Limit number 单次查询限制 + */ + @SerializedName("Limit") + private Long limit; + + /** + * Total number 符合查询条件的记录总数 + */ + @SerializedName("Total") + private Long total; + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseUpdateResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseUpdateResult.java new file mode 100644 index 0000000000..000774132f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseUpdateResult.java @@ -0,0 +1,32 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发数据库更新记录接口请求结果. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Data +public class WxCloudDatabaseUpdateResult implements Serializable { + private static final long serialVersionUID = -3905429931117999004L; + + /** + * matched number 更新条件匹配到的结果数 + */ + private Long matched; + + /** + * modified number 修改的记录数,注意:使用set操作新插入的数据不计入修改数目 + */ + private Long modified; + + /** + * id string 新插入记录的id,注意:只有使用set操作新插入数据时这个字段会有值 + */ + private String id; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudGetQcloudTokenResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudGetQcloudTokenResult.java new file mode 100644 index 0000000000..f2b8bf0a1d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudGetQcloudTokenResult.java @@ -0,0 +1,41 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 获取腾讯云API调用凭证结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudGetQcloudTokenResult implements Serializable { + private static final long serialVersionUID = -1505786395531138286L; + + /** + * secretid + */ + @SerializedName("secretid") + private String secretId; + + /** + * secretkey + */ + @SerializedName("secretkey") + private String secretKey; + + /** + * token + */ + @SerializedName("token") + private String token; + + /** + * 过期时间戳 + */ + @SerializedName("expired_time") + private Long expiredTime; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudUploadFileResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudUploadFileResult.java new file mode 100644 index 0000000000..300ae86200 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudUploadFileResult.java @@ -0,0 +1,47 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发文件上传接口响应结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudUploadFileResult implements Serializable { + private static final long serialVersionUID = 787346474470048318L; + + /** + * 上传url + */ + @SerializedName("url") + private String url; + + /** + * token + */ + @SerializedName("token") + private String token; + + /** + * authorization + */ + @SerializedName("authorization") + private String authorization; + + /** + * 文件ID + */ + @SerializedName("file_id") + private String fileId; + + /** + * cos文件ID + */ + @SerializedName("cos_file_id") + private String cosFileId; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCategory.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCategory.java new file mode 100644 index 0000000000..5e176cfa61 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCategory.java @@ -0,0 +1,66 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 小程序帐号的可选类目,其中 address / tag / title 是提交审核会用到的 + * + * @author Charming + * @since 2018-04-26 19:44 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaCategory implements Serializable { + private static final long serialVersionUID = -7663757440028175135L; + /** + * 一级类目名称 + */ + @SerializedName("first_class") + private String firstClass; + /** + * 二级类目名称 + */ + @SerializedName("second_class") + private String secondClass; + /** + * 三级类目名称 + */ + @SerializedName("third_class") + private String thirdClass; + /** + * 一级类目的ID编号 + */ + @SerializedName("first_id") + private Long firstId; + /** + * 二级类目的ID编号 + */ + @SerializedName("second_id") + private Long secondId; + /** + * 三级类目的ID编号 + */ + @SerializedName("third_id") + private Long thirdId; + + /** + * 小程序的页面,可通过“获取小程序的第三方提交代码的页面配置”接口获得 + */ + private String address; + /** + * 小程序的标签,多个标签用空格分隔,标签不能多于10个,标签长度不超过20 + */ + private String tag; + /** + * 小程序页面的标题,标题长度不超过32 + */ + private String title; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java new file mode 100644 index 0000000000..3faa18660b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java @@ -0,0 +1,47 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 小程序代码审核状态 + * + * @author Charming + * @since 2018-04-26 19:44:39 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaCodeAuditStatus implements Serializable { + private static final long serialVersionUID = 4655119308692217268L; + /** + * 审核 ID. + */ + @SerializedName(value = "auditId", alternate = {"auditid"}) + private Long auditId; + /** + * 审核状态. + * 其中0为审核成功,1为审核失败,2为审核中 + */ + private Integer status; + /** + * 当status=1,审核被拒绝时,返回的拒绝原因. + */ + private String reason; + /** + * 当status=1,审核被拒绝时,会返回审核失败的小程序截图示例。 xxx丨yyy丨zzz是media_id可通过获取永久素材接口 拉取截图内容). + */ + @SerializedName(value = "screenshot") + private String screenShot; + + public static WxMaCodeAuditStatus fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaCodeAuditStatus.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequest.java new file mode 100644 index 0000000000..f59fb1f039 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequest.java @@ -0,0 +1,43 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信代码请求上传参数 + * + * @author Charming + * @since 2018-04-26 19:44:47 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaCodeCommitRequest implements Serializable { + private static final long serialVersionUID = 7495157056049312108L; + /** + * 代码库中的代码模版ID + */ + private Long templateId; + /** + * 第三方自定义的配置 + */ + private WxMaCodeExtConfig extConfig; + /** + * 代码版本号,开发者可自定义 + */ + private String userVersion; + /** + * 代码描述,开发者可自定义 + */ + private String userDesc; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeExtConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeExtConfig.java new file mode 100644 index 0000000000..c7d4de5491 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeExtConfig.java @@ -0,0 +1,209 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 上传代码需要用到的第三方自定义的配置 + * 详细文档,参考:https://developers.weixin.qq.com/miniprogram/dev/framework/config.html + * + * @author Charming + * @since 2018-04-26 19:44 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaCodeExtConfig implements Serializable { + private static final long serialVersionUID = -7666911367458178753L; + /** + * 配置 ext.json 是否生效. + * 必填:是 + */ + private boolean extEnable; + /** + * 配置 extAppid. + * 必填:是 + */ + private String extAppid; + /** + * 开发自定义的数据字段. + * 必填:否 + */ + private Object ext; + /** + * 单独设置每个页面的 json. + * 必填:否 + * key: page 名称,如 pages/logs/logs + * value: page 配置 + */ + private Map extPages; + /** + * 是否直接提交到待审核列表. + * 必填:否 + */ + private Boolean directCommit; + /** + * 设置页面路径(同 app.json 相同的字段,填写会覆盖 app.json). + * 必填:否 + */ + private List pages; + /** + * 设置默认页面的窗口表现(同 app.json 相同的字段,填写会覆盖 app.json) + * 必填:否 + */ + private PageConfig window; + /** + * 设置各种网络请求的超时时间(同 app.json 相同的字段,填写会覆盖 app.json) + * 必填:否 + */ + private NetworkTimeout networkTimeout; + /** + * 设置是否开启 debug 模式(同 app.json 相同的字段,填写会覆盖 app.json) + * 必填:否 + */ + private Boolean debug; + /** + * 底部 tab 栏的表现. + * 必填:否 + */ + private TabBar tabBar; + + /** + * page.json 配置,页面配置 + * 文档:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/config.html + */ + @Data + @Builder + public static class PageConfig { + /** + * 导航栏背景颜色,如"#000000" HexColor. + * 默认:#000000 + */ + private String navigationBarBackgroundColor; + /** + * 导航栏标题颜色,仅支持 black/white. + * 默认:white + */ + private String navigationBarTextStyle; + /** + * 导航栏标题文字内容. + */ + private String navigationBarTitleText; + /** + * 窗口的背景色 HexColor. + * 默认:#ffffff + */ + private String backgroundColor; + /** + * 下拉背景字体、loading 图的样式,仅支持 dark/light. + * 默认:dark + */ + private String backgroundTextStyle; + /** + * 是否开启下拉刷新,详见页面相关事件处理函数. + * 默认:false + */ + private String enablePullDownRefresh; + /** + * 设置为 true 则页面整体不能上下滚动;只在 page.json 中有效,无法在 app.json 中设置该项. + * 默认:false + */ + private Boolean disableScroll; + /** + * 页面上拉触底事件触发时距页面底部距离,单位为px. + * 默认:50 + */ + private Integer onReachBottomDistance; + } + + /** + * tabBar 配置. + */ + @Data + @Builder + public static class TabBar { + /** + * HexColor, tab 上的文字默认颜色. + */ + private String color; + /** + * HexColor, tab 上的文字选中时的颜色. + */ + private String selectedColor; + /** + * HexColor, tab 的背景色. + */ + private String backgroundColor; + /** + * tabbar 上边框的颜色,仅支持 black/white. + */ + private String borderStyle; + /** + * tab 的列表,最少2个、最多5个 tab. + */ + private List list; + /** + * 可选值 bottom、top. + */ + private String position; + + /** + * list item. + */ + @Data + @Builder + public static class Item { + /** + * 页面路径,必须在 pages 中先定义. + */ + private String pagePath; + /** + * tab 上按钮文字. + */ + private String text; + /** + * 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片. + */ + private String iconPath; + /** + * 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效. + */ + private String selectedIconPath; + } + } + + /** + * 各种网络请求的超时时间. + */ + @Data + @Builder + public static class NetworkTimeout { + /** + * wx.request的超时时间,单位毫秒,默认为:60000. + * 必填:否 + */ + private Integer request; + /** + * wx.connectSocket的超时时间,单位毫秒,默认为:60000. + * 必填:否 + */ + private Integer connectSocket; + /** + * wx.uploadFile的超时时间,单位毫秒,默认为:60000. + * 必填:否 + */ + private Integer uploadFile; + /** + * wx.downloadFile的超时时间,单位毫秒,默认为:60000. + * 必填:否 + */ + private Integer downloadFile; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java new file mode 100644 index 0000000000..b65c4df588 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java @@ -0,0 +1,34 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 提交审核的请求 + * + * @author Charming + * @since 2018-04-26 19:45 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaCodeSubmitAuditRequest implements Serializable { + private static final long serialVersionUID = 8854979405505241314L; + /** + * 提交审核项的一个列表(至少填写1项,至多填写5项) + */ + @SerializedName("item_list") + private List itemList; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistribution.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistribution.java new file mode 100644 index 0000000000..dd0a03a918 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistribution.java @@ -0,0 +1,34 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +/** + * 小程序代码版本号分布 + * + * @author Charming + * @since 2018-04-26 19:45 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaCodeVersionDistribution { + /** + * 当前版本 + */ + private String nowVersion; + /** + * 受影响用户占比 + * key: version, 版本号 + * value: percentage, 受影响比例 + */ + private Map uvInfo; + + public static WxMaCodeVersionDistribution fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaCodeVersionDistribution.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/package-info.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/package-info.java new file mode 100644 index 0000000000..01f6496b99 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/package-info.java @@ -0,0 +1,7 @@ +/** + * 微信小程序代码管理相关的 bean + * + * @author Charming + * @since 2018-04-26 19:44 + */ +package cn.binarywang.wx.miniapp.bean.code; diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressAccount.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressAccount.java new file mode 100644 index 0000000000..950bda3066 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressAccount.java @@ -0,0 +1,107 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.GsonParser; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + * 物流账号对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressAccount implements Serializable { + + + private static final long serialVersionUID = -4991983596742569736L; + + /** + * 快递公司客户编码 + */ + @SerializedName("biz_id") + private String bizId; + + /** + * 快递公司ID + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 账号绑定时间 + */ + @SerializedName("create_time") + private Long createTime; + + /** + * 账号更新时间 + */ + @SerializedName("update_time") + private Long updateTime; + + /** + * 绑定状态 + * status_code 的合法值 : 0-绑定成功;1-审核中;2-绑定失败 + */ + @SerializedName("status_code") + private Integer statusCode; + + /** + * 账号别名 + */ + @SerializedName("alias") + private String alias; + + /** + * 账号绑定失败的错误信息(EMS审核结果) + */ + @SerializedName("remark_wrong_msg") + private String remarkWrongMsg; + + /** + * 账号绑定时的备注内容(提交EMS审核需要) + */ + @SerializedName("remark_content") + private String remarkContent; + + /** + * 电子面单余额 + */ + @SerializedName("quota_num") + private Integer quotaNum; + + /** + * 电子面单余额更新时间 + */ + @SerializedName("quota_update_time") + private Integer quotaUpdateTime; + + /** + * 支持的服务类型 + */ + @SerializedName("service_type") + private List serviceType; + + public static List fromJsonList(String json) { + JsonObject jsonObject = GsonParser.parse(json); + return WxMaGsonBuilder.create().fromJson(jsonObject.get("list").toString(), + new TypeToken>() { + }.getType()); + } + + public static WxMaExpressAccount fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressAccount.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressDelivery.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressDelivery.java new file mode 100644 index 0000000000..dbdb02c113 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressDelivery.java @@ -0,0 +1,87 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.GsonParser; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + * 快递公司对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressDelivery implements Serializable { + + private static final long serialVersionUID = -8394544895730223810L; + + /** + * 快递公司 ID + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 快递公司名称 + */ + @SerializedName("delivery_name") + private String deliveryName; + + /** + * 是否支持散单, 1表示支持 + */ + @SerializedName("can_use_cash") + private Integer canUseCash; + + /** + * 是否支持查询面单余额, 1表示支持 + */ + @SerializedName("can_get_quota") + private Integer canGetQuota; + + /** + * 散单对应的bizid,当can_use_cash=1时有效 + */ + @SerializedName("cash_biz_id") + private String cashBizId; + + /** + * 支持的服务类型 + */ + @SerializedName("service_type") + private List serviceType; + + public static List fromJson(String json) { + JsonObject jsonObject = GsonParser.parse(json); + return WxMaGsonBuilder.create().fromJson(jsonObject.get("data").toString(), + new TypeToken>() { + }.getType()); + } + + @Data + public static class ServiceType{ + + /** + * 服务类型ID + */ + @SerializedName("service_type") + private Integer serviceType; + + /** + * 服务类型名称 + */ + @SerializedName("service_name") + private String serviceName; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPath.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPath.java new file mode 100644 index 0000000000..bbd3feacdb --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPath.java @@ -0,0 +1,79 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 运单轨迹对象 + * + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressPath implements Serializable { + private static final long serialVersionUID = 5643624677715536605L; + + /** + * 用户openid + */ + private String openid; + + /** + * 快递公司 ID + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 运单 ID + */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 轨迹节点数量 + */ + @SerializedName("path_item_num") + private Integer pathItemNum; + + /** + * 轨迹节点列表 + */ + @SerializedName("path_item_list") + private List pathItemList; + + public static WxMaExpressPath fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressPath.class); + } + + @Data + public static class PathItem { + + /** + * 轨迹节点 Unix 时间戳 + */ + @SerializedName("action_time") + private Long actionTime; + + /** + * 轨迹节点类型 + */ + @SerializedName("action_type") + private Integer actionType; + + /** + * 轨迹节点详情 + */ + @SerializedName("action_msg") + private String actionMsg; + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPrinter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPrinter.java new file mode 100644 index 0000000000..b41d33305c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPrinter.java @@ -0,0 +1,47 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + * 面单打印员对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressPrinter implements Serializable { + + + private static final long serialVersionUID = 7164449984700322531L; + + /** + * 数量 + */ + private Integer count; + + /** + * 打印员openid + */ + private List openid; + + /** + * 打印员面单打印权限 + */ + @SerializedName("tagid_list") + private List tagidList; + + + public static WxMaExpressPrinter fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressPrinter.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressAddOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressAddOrderRequest.java new file mode 100644 index 0000000000..e11f5beb6e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressAddOrderRequest.java @@ -0,0 +1,173 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + + +import cn.binarywang.wx.miniapp.bean.express.*; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 生成运单请求对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressAddOrderRequest implements Serializable { + + private static final long serialVersionUID = -7538739003766268386L; + + + /** + * 订单来源 + *
    +   * 是否必填: 是
    +   * 描述: 0为小程序订单,2为App或H5订单,填2则不发送物流服务通知
    +   * 
    + */ + @SerializedName("add_source") + private Integer addSource; + + /** + * App或H5的appid + *
    +   * 是否必填: 否
    +   * 描述: add_source=2时必填,需和开通了物流助手的小程序绑定同一open帐号
    +   * 
    + */ + @SerializedName("wx_appid") + private String wxAppid; + + /** + * 订单ID + *
    +   * 是否必填: 是
    +   * 描述: 须保证全局唯一,不超过512字节
    +   * 
    + */ + @SerializedName("order_id") + private String orderId; + + /** + * 用户openid + *
    +   * 是否必填: 否
    +   * 描述: 当add_source=2时无需填写(不发送物流服务通知)
    +   * 
    + */ + @SerializedName("openid") + private String openid; + + /** + * 快递公司ID + *
    +   * 是否必填: 是
    +   * 描述: 可通过getAllDelivery查询
    +   * 
    + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 快递客户编码或者现付编码 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("biz_id") + private String bizId; + + /** + * 快递备注信息 + *
    +   * 是否必填: 否
    +   * 描述: 比如"易碎物品",不超过1024字节
    +   * 
    + */ + @SerializedName("custom_remark") + private String customRemark; + + /** + * 订单标签id + *
    +   * 是否必填: 否
    +   * 描述: 用于平台型小程序区分平台上的入驻方,tagid须与入驻方账号一一对应,非平台型小程序无需填写该字段
    +   * 
    + */ + @SerializedName("tagid") + private Integer tagid; + + /** + * 预期的上门揽件时间 + *
    +   * 是否必填: 否
    +   * 描述: 顺丰必须传,0表示已事先约定取件时间;否则请传预期揽件时间戳,需大于当前时间,收件员会在预期时间附近上门。例如expect_time为“1557989929”,表示希望收件员将在2019年05月16日14:58:49-15:58:49内上门取货。说明:若选择 了预期揽件时间,请不要自己打单,由上门揽件的时候打印。
    +   * 
    + */ + @SerializedName("expect_time") + private Long expectTime; + + /** + * 发件人信息 + *
    +   * 是否必填: 是
    +   * 
    + */ + private WxMaExpressOrderPerson sender; + + /** + * 收件人信息 + *
    +   * 是否必填: 是
    +   * 
    + */ + private WxMaExpressOrderPerson receiver; + + /** + * 包裹信息 + *
    +   * 是否必填: 是
    +   * 描述: 将传递给快递公司
    +   * 
    + */ + private WxMaExpressOrderCargo cargo; + + /** + * 商品信息 + *
    +   * 是否必填: 否
    +   * 描述: 会展示到物流服务通知和电子面单中
    +   * 
    + */ + private WxMaExpressOrderShop shop; + + /** + * 保价信息 + *
    +   * 是否必填: 是
    +   * 
    + */ + private WxMaExpressOrderInsured insured; + + /** + * 服务类型 + *
    +   * 是否必填: 是
    +   * 
    + */ + private WxMaExpressDelivery.ServiceType service; + + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressBindAccountRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressBindAccountRequest.java new file mode 100644 index 0000000000..be0ef991c1 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressBindAccountRequest.java @@ -0,0 +1,76 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 绑定、解绑物流账号请求对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressBindAccountRequest implements Serializable { + private static final long serialVersionUID = 3868003945297939946L; + + /** + * 类型 + *
    +   * 是否必填: 是
    +   * 描述: bind表示绑定,unbind表示解除绑定
    +   * 
    + */ + @SerializedName("type") + private String type; + + /** + * 快递公司客户编码 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("biz_id") + private String bizId; + + /** + * 快递公司ID + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 快递公司客户密码 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("password") + private String password; + + /** + * 备注内容(提交EMS审核需要) + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("remark_content") + private String remarkContent; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressGetOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressGetOrderRequest.java new file mode 100644 index 0000000000..6fe03ddae9 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressGetOrderRequest.java @@ -0,0 +1,66 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 获取运单请求对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressGetOrderRequest implements Serializable { + private static final long serialVersionUID = 8239315305577639778L; + + /** + * 订单ID + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("order_id") + private String orderId; + + /** + * 用户openid + *
    +   * 是否必填: 否
    +   * 描述: 当add_source=2时无需填写(不发送物流服务通知)
    +   * 
    + */ + private String openid; + + /** + * 快递公司ID + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 运单ID + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("waybill_id") + private String waybillId; + + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java new file mode 100644 index 0000000000..96817a2256 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java @@ -0,0 +1,77 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 包裹信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressOrderCargo implements Serializable { + private static final long serialVersionUID = 6642536671375396150L; + + /** + * 包裹数量 + *
    +   * 是否必填: 是
    +   * 描述: 需要和detail_list size保持一致
    +   * 
    + */ + private Integer count; + /** + * 包裹总重量 + *
    +   * 是否必填: 是
    +   * 描述: 单位是千克(kg)
    +   * 
    + */ + private Integer weight; + + /** + * 包裹长度 + *
    +   * 是否必填: 是
    +   * 描述: 单位是厘米(cm)
    +   * 
    + */ + @SerializedName("space_x") + private Integer spaceLength; + + /** + * 包裹宽度 + *
    +   * 是否必填: 是
    +   * 描述: 单位是厘米(cm)
    +   * 
    + */ + @SerializedName("space_y") + private Integer spaceWidth; + + /** + * 包裹高度 + *
    +   * 是否必填: 是
    +   * 描述: 单位是厘米(cm)
    +   * 
    + */ + @SerializedName("space_z") + private Integer spaceHeight; + + /** + * 包裹中商品详情列表 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("detail_list") + private List detailList; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargoDetail.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargoDetail.java new file mode 100644 index 0000000000..6dbb3c0292 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargoDetail.java @@ -0,0 +1,36 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 包裹商品详情对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressOrderCargoDetail implements Serializable { + private static final long serialVersionUID = 5988620921216969796L; + + /** + * 商品名 + *
    +   * 是否必填: 是
    +   * 描述: 不超过128字节
    +   * 
    + */ + private String name; + + /** + * 商品数量 + *
    +   * 是否必填: 是
    +   * 
    + */ + private Integer count; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderInsured.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderInsured.java new file mode 100644 index 0000000000..d7ccda9aec --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderInsured.java @@ -0,0 +1,46 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + + +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 保价信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressOrderInsured implements Serializable { + private static final long serialVersionUID = -8636857630937445422L; + + /** + * 是否保价 + *
    +   * 是否必填: 是
    +   * 描述: 0 表示不保价,1 表示保价
    +   * 
    + */ + @SerializedName("use_insured") + private final Integer useInsured = WxMaConstants.OrderAddInsured.INSURED_PROGRAM; + + /** + * 保价金额 + *
    +   * 是否必填: 是
    +   * 描述: 单位是分,比如: 10000 表示 100 元
    +   * 
    + */ + @SerializedName("insured_value") + @Builder.Default + private final Integer insuredValue = WxMaConstants.OrderAddInsured.DEFAULT_INSURED_VALUE; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderPerson.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderPerson.java new file mode 100644 index 0000000000..a70119fbcf --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderPerson.java @@ -0,0 +1,113 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 发件人/收件人信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressOrderPerson implements Serializable { + private static final long serialVersionUID = -7816060207882761506L; + + /** + * 发件人/收件人姓名 + *
    +   * 是否必填: 是
    +   * 描述: 不超过64字节
    +   * 
    + */ + private String name; + + /** + * 发件人/收件人座机号码 + *
    +   * 是否必填: 否
    +   * 描述: 若不填写则必须填写 mobile,不超过32字节
    +   * 
    + */ + private String tel; + + /** + * 发件人/收件人手机号码 + *
    +   * 是否必填: 否
    +   * 描述: 若不填写则必须填写 tel,不超过32字节
    +   * 
    + */ + private String mobile; + + /** + * 发件人/收件人公司名 + *
    +   * 是否必填: 否
    +   * 描述: 不超过64字节
    +   * 
    + */ + private String company; + + /** + * 发件人/收件人邮编 + *
    +   * 是否必填: 否
    +   * 描述: 不超过10字节
    +   * 
    + */ + @SerializedName("post_code") + private String postCode; + + /** + * 发件人/收件人所在国家 + *
    +   * 是否必填: 否
    +   * 描述: 不超过64字节
    +   * 
    + */ + private String country; + + /** + * 发件人/收件人省份 + *
    +   * 是否必填: 是
    +   * 描述: 比如:"广东省",不超过64字节
    +   * 
    + */ + private String province; + + /** + * 发件人/收件人地区或市 + *
    +   * 是否必填: 是
    +   * 描述: 比如:"广州市",不超过64字节
    +   * 
    + */ + private String city; + + /** + * 发件人/收件人区或县 + *
    +   * 是否必填: 是
    +   * 描述: 比如:"天河区",不超过64字节
    +   * 
    + */ + private String area; + + /** + * 发件人/收件人详细地址 + *
    +   * 是否必填: 是
    +   * 描述: 比如:"XX路XX号XX大厦XX",不超过512字节
    +   * 
    + */ + private String address; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderShop.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderShop.java new file mode 100644 index 0000000000..41c08f9e8e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderShop.java @@ -0,0 +1,58 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 商品信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressOrderShop implements Serializable { + private static final long serialVersionUID = 7256509453502211830L; + + /** + * 商家小程序的路径 + *
    +   * 是否必填: 是
    +   * 描述: 建议为订单页面
    +   * 
    + */ + @SerializedName("wxa_path") + private String wxaPath; + + /** + * 商品缩略图url + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("img_url") + private String imgUrl; + + /** + * 商品名称 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("goods_name") + private String goodsName; + + /** + * 商品数量 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("goods_count") + private Integer goodsCount; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressPrinterUpdateRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressPrinterUpdateRequest.java new file mode 100644 index 0000000000..da47f77042 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressPrinterUpdateRequest.java @@ -0,0 +1,57 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 配置面单打印员请求对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressPrinterUpdateRequest implements Serializable { + private static final long serialVersionUID = 9119040050963924127L; + + /** + * 打印员 openid + *
    +   * 是否必填: 是
    +   * 
    + */ + private String openid; + + /** + * 更新类型 + *
    +   * 是否必填: 是
    +   * 描述: bind表示绑定,unbind表示解除绑定
    +   * 
    + */ + @SerializedName("update_type") + private String updateType; + + /** + * 打印员面单打印权限 + *
    +   * 是否必填: 否
    +   * 描述: 用于平台型小程序设置入驻方的打印员面单打印权限,同一打印员最多支持10个tagid,使用逗号分隔,如填写123,456,表示该打印员可以拉取到tagid为123和456的下的单,非平台型小程序无需填写该字段
    +   * 
    + */ + @SerializedName("tagid_list") + private String tagidList; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressTestUpdateOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressTestUpdateOrderRequest.java new file mode 100644 index 0000000000..c0a8561243 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressTestUpdateOrderRequest.java @@ -0,0 +1,97 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 模拟快递公司更新订单状态请求对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressTestUpdateOrderRequest implements Serializable { + private static final long serialVersionUID = -3701602332580704140L; + + /** + * 商户id + *
    +   * 是否必填: 是
    +   * 描述: 需填test_biz_id,默认值已设置
    +   * 
    + */ + @SerializedName("biz_id") + @Builder.Default + private final String bizId = "test_biz_id"; + + /** + * 快递公司id + *
    +   * 是否必填: 是
    +   * 描述: 需填TEST,默认值已设置
    +   * 
    + */ + @SerializedName("delivery_id") + @Builder.Default + private final String deliveryId = "TEST"; + + /** + * 订单号 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("order_id") + private String orderId; + + /** + * 运单号 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 轨迹变化 Unix 时间戳 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("action_time") + private Long actionTime; + + /** + * 轨迹变化类型 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("action_type") + private Integer actionType; + + /** + * 轨迹变化具体信息说明,使用UTF-8编码 + *
    +   * 是否必填: 是
    +   * 
    + */ + @SerializedName("action_msg") + private String actionMsg; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/result/WxMaExpressOrderInfoResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/result/WxMaExpressOrderInfoResult.java new file mode 100644 index 0000000000..fb47057d87 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/result/WxMaExpressOrderInfoResult.java @@ -0,0 +1,74 @@ +package cn.binarywang.wx.miniapp.bean.express.result; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.GsonParser; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + *
    + * 运单信息返回结果对象
    + * 
    + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxMaExpressOrderInfoResult implements Serializable { + + private static final long serialVersionUID = -9166603059965942285L; + + /** + * 错误码 + */ + private Integer errcode; + + /** + * 错误信息 + */ + private String errmsg; + /** + * 订单ID + */ + @SerializedName("order_id") + private String orderId; + + /** + * 运单ID + */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 运单 html 的 BASE64 结果 + */ + @SerializedName("print_html") + private String printHtml; + + /** + * 运单信息 + */ + @SerializedName("waybill_data") + private List> waybillData; + + + public static WxMaExpressOrderInfoResult fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressOrderInfoResult.class); + } + + public static List toList(String json) { + JsonObject jsonObject = GsonParser.parse(json); + return WxMaGsonBuilder.create().fromJson(jsonObject.get("order_list").toString(), + new TypeToken>() { + }.getType()); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaPubTemplateTitleListResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaPubTemplateTitleListResult.java new file mode 100644 index 0000000000..d2dacc6e61 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaPubTemplateTitleListResult.java @@ -0,0 +1,35 @@ +package cn.binarywang.wx.miniapp.bean.template; + +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author ArBing + */ +@Data +public class WxMaPubTemplateTitleListResult implements Serializable { + private static final long serialVersionUID = -7718911668757837527L; + + private int count; + + private List data; + + public static WxMaPubTemplateTitleListResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMaPubTemplateTitleListResult.class); + } + + @Data + public static class TemplateItem { + + private Integer type; + + private Integer tid; + + private String categoryId; + + private String title; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateAddResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateAddResult.java new file mode 100644 index 0000000000..e9f68c0973 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateAddResult.java @@ -0,0 +1,22 @@ +package cn.binarywang.wx.miniapp.bean.template; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * @author ArBing + */ +@Data +public class WxMaTemplateAddResult implements Serializable { + private static final long serialVersionUID = 872250961973834465L; + + @SerializedName("template_id") + private String templateId; + + public static WxMaTemplateAddResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMaTemplateAddResult.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateLibraryGetResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateLibraryGetResult.java new file mode 100644 index 0000000000..9a73d3b638 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateLibraryGetResult.java @@ -0,0 +1,33 @@ +package cn.binarywang.wx.miniapp.bean.template; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author ArBing + */ +@Data +public class WxMaTemplateLibraryGetResult implements Serializable { + private static final long serialVersionUID = -190847592776636744L; + private String id; + private String title; + @SerializedName("keyword_list") + private List keywordList; + + @Data + public static class KeywordInfo { + + @SerializedName("keyword_id") + private int keywordId; + private String name; + private String example; + } + + public static WxMaTemplateLibraryGetResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMaTemplateLibraryGetResult.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateLibraryListResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateLibraryListResult.java new file mode 100644 index 0000000000..0714378b96 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateLibraryListResult.java @@ -0,0 +1,31 @@ +package cn.binarywang.wx.miniapp.bean.template; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author ArBing + */ +@Data +public class WxMaTemplateLibraryListResult implements Serializable{ + private static final long serialVersionUID = -2780782521447602209L; + + @SerializedName("total_count") + private int totalCount; + private List list; + + public static WxMaTemplateLibraryListResult fromJson(String json){ + return WxGsonBuilder.create().fromJson(json, WxMaTemplateLibraryListResult.class); + } + + @Data + public static class TemplateItem{ + + private String id; + private String title; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateListResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateListResult.java new file mode 100644 index 0000000000..98708ff172 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaTemplateListResult.java @@ -0,0 +1,31 @@ +package cn.binarywang.wx.miniapp.bean.template; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author ArBing + */ +@Data +public class WxMaTemplateListResult implements Serializable { + private static final long serialVersionUID = -7430535579782184537L; + private List list; + + public static WxMaTemplateListResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMaTemplateListResult.class); + } + + @Data + public static class TemplateInfo { + + @SerializedName("template_id") + private String templateId; + private String title; + private String content; + private String example; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java new file mode 100644 index 0000000000..c353534c3f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java @@ -0,0 +1,27 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; + +/** + * @author Binary Wang + */ +public class BaseBuilder { + protected String msgType; + protected String toUser; + + @SuppressWarnings("unchecked") + public T toUser(String toUser) { + this.toUser = toUser; + return (T) this; + } + + /** + * 构造器方法. + */ + public WxMaKefuMessage build() { + WxMaKefuMessage m = new WxMaKefuMessage(); + m.setMsgType(this.msgType); + m.setToUser(this.toUser); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageMessageBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageMessageBuilder.java new file mode 100644 index 0000000000..bcd2290f10 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageMessageBuilder.java @@ -0,0 +1,30 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; + +import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; + +/** + * 图片消息builder. + * + * @author Binary Wang + */ +public final class ImageMessageBuilder extends BaseBuilder { + private String mediaId; + + public ImageMessageBuilder() { + this.msgType = KefuMsgType.IMAGE; + } + + public ImageMessageBuilder mediaId(String mediaId) { + this.mediaId = mediaId; + return this; + } + + @Override + public WxMaKefuMessage build() { + WxMaKefuMessage m = super.build(); + m.setImage(new WxMaKefuMessage.KfImage(this.mediaId)); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/LinkMessageBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/LinkMessageBuilder.java new file mode 100644 index 0000000000..f4927e16dc --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/LinkMessageBuilder.java @@ -0,0 +1,53 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; + +import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; + +/** + * 图文链接消息builder + * + * @author Binary Wang + */ +public class LinkMessageBuilder extends BaseBuilder { + private String title; + private String description; + private String url; + private String thumbUrl; + + public LinkMessageBuilder() { + this.msgType = KefuMsgType.LINK; + } + + public LinkMessageBuilder title(String title) { + this.title = title; + return this; + } + + public LinkMessageBuilder description(String description) { + this.description = description; + return this; + } + + public LinkMessageBuilder url(String url) { + this.url = url; + return this; + } + + public LinkMessageBuilder thumbUrl(String thumbUrl) { + this.thumbUrl = thumbUrl; + return this; + } + + @Override + public WxMaKefuMessage build() { + WxMaKefuMessage m = super.build(); + m.setLink(WxMaKefuMessage.KfLink.builder().title(this.title) + .description(this.description) + .url(this.url) + .thumbUrl(this.thumbUrl) + .build() + ); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/MaPageMessageBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/MaPageMessageBuilder.java new file mode 100644 index 0000000000..238a60146e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/MaPageMessageBuilder.java @@ -0,0 +1,47 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; + +import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; + +/** + * 小程序卡片消息builder + * + * @author Binary Wang + */ +public class MaPageMessageBuilder extends BaseBuilder { + private String title; + private String pagePath; + private String thumbMediaId; + + public MaPageMessageBuilder() { + this.msgType = KefuMsgType.MA_PAGE; + } + + public MaPageMessageBuilder title(String title) { + this.title = title; + return this; + } + + public MaPageMessageBuilder pagePath(String pagePath) { + this.pagePath = pagePath; + return this; + } + + public MaPageMessageBuilder thumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + return this; + } + + @Override + public WxMaKefuMessage build() { + WxMaKefuMessage m = super.build(); + m.setMaPage(WxMaKefuMessage.KfMaPage.builder() + .title(this.title) + .pagePath(this.pagePath) + .thumbMediaId(this.thumbMediaId) + .build() + ); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextMessageBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextMessageBuilder.java new file mode 100644 index 0000000000..d0da32573c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextMessageBuilder.java @@ -0,0 +1,30 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; + +import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; + +/** + * 文本消息builder. + * + * @author Binary Wang + */ +public final class TextMessageBuilder extends BaseBuilder { + private String content; + + public TextMessageBuilder() { + this.msgType = KefuMsgType.TEXT; + } + + public TextMessageBuilder content(String content) { + this.content = content; + return this; + } + + @Override + public WxMaKefuMessage build() { + WxMaKefuMessage m = super.build(); + m.setText(new WxMaKefuMessage.KfText(this.content)); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java new file mode 100644 index 0000000000..6854c87e74 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java @@ -0,0 +1,222 @@ +package cn.binarywang.wx.miniapp.config; + +import java.util.concurrent.locks.Lock; + +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; + +/** + * 小程序配置 + * + * @author Binary Wang + */ +public interface WxMaConfig { + + /** + * Gets access token. + * + * @return the access token + */ + String getAccessToken(); + + /** + * Gets access token lock. + * + * @return the access token lock + */ + Lock getAccessTokenLock(); + + /** + * Is access token expired boolean. + * + * @return the boolean + */ + boolean isAccessTokenExpired(); + + /** + * 强制将access token过期掉 + */ + void expireAccessToken(); + + /** + * 应该是线程安全的 + * + * @param accessToken 要更新的WxAccessToken对象 + */ + void updateAccessToken(WxAccessToken accessToken); + + /** + * 应该是线程安全的 + * + * @param accessToken 新的accessToken值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateAccessToken(String accessToken, int expiresInSeconds); + + /** + * Gets jsapi ticket. + * + * @return the jsapi ticket + */ + String getJsapiTicket(); + + /** + * Gets jsapi ticket lock. + * + * @return the jsapi ticket lock + */ + Lock getJsapiTicketLock(); + + /** + * Is jsapi ticket expired boolean. + * + * @return the boolean + */ + boolean isJsapiTicketExpired(); + + /** + * 强制将jsapi ticket过期掉 + */ + void expireJsapiTicket(); + + /** + * 应该是线程安全的 + * + * @param jsapiTicket 新的jsapi ticket值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + + /** + * 卡券api_ticket. + * + * @return the card api ticket + */ + String getCardApiTicket(); + + /** + * Gets card api ticket lock. + * + * @return the card api ticket lock + */ + Lock getCardApiTicketLock(); + + /** + * Is card api ticket expired boolean. + * + * @return the boolean + */ + boolean isCardApiTicketExpired(); + + /** + * 强制将卡券api ticket过期掉. + */ + void expireCardApiTicket(); + + /** + * 应该是线程安全的. + * + * @param apiTicket 新的卡券api ticket值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateCardApiTicket(String apiTicket, int expiresInSeconds); + + /** + * Gets appid. + * + * @return the appid + */ + String getAppid(); + + /** + * Gets secret. + * + * @return the secret + */ + String getSecret(); + + /** + * Gets token. + * + * @return the token + */ + String getToken(); + + /** + * Gets aes key. + * + * @return the aes key + */ + String getAesKey(); + + /** + * Gets original id. + * + * @return the original id + */ + String getOriginalId(); + + /** + * Gets cloud env. + * + * @return the cloud env + */ + String getCloudEnv(); + + /** + * Gets msg data format. + * + * @return the msg data format + */ + String getMsgDataFormat(); + + /** + * Gets expires time. + * + * @return the expires time + */ + long getExpiresTime(); + + /** + * Gets http proxy host. + * + * @return the http proxy host + */ + String getHttpProxyHost(); + + /** + * Gets http proxy port. + * + * @return the http proxy port + */ + int getHttpProxyPort(); + + /** + * Gets http proxy username. + * + * @return the http proxy username + */ + String getHttpProxyUsername(); + + /** + * Gets http proxy password. + * + * @return the http proxy password + */ + String getHttpProxyPassword(); + + /** + * http client builder + * + * @return ApacheHttpClientBuilder apache http client builder + */ + ApacheHttpClientBuilder getApacheHttpClientBuilder(); + + /** + * 是否自动刷新token + * + * @return the boolean + */ + boolean autoRefreshToken(); + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/AbstractWxMaRedisConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/AbstractWxMaRedisConfig.java new file mode 100644 index 0000000000..ac75b3697d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/AbstractWxMaRedisConfig.java @@ -0,0 +1,280 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import com.github.jedis.lock.JedisLock; +import redis.clients.jedis.Jedis; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +/** + * @author winter + */ +public abstract class AbstractWxMaRedisConfig extends WxMaDefaultConfigImpl { + + public interface JedisConfig { + Jedis config(Jedis jedis); + } + + private static final String ACCESS_TOKEN = "accessToken"; + private static final String JSAPI_TICKET = "jsapiTicket"; + private static final String CARD_API_TICKET = "cardApiTicket"; + + private static final String HASH_VALUE_FIELD = "value"; + private static final String HASH_EXPIRE_FIELD = "expire"; + + /** + * Redis Key 的前缀,默认为 maConfig + */ + private String redisKeyPrefix = "maConfig"; + + /** + * 微信小程序唯一id,用于拼接存储到redis时的key,防止key重复. + */ + private String maId; + + private Lock accessTokenLock; + private Lock jsapiTicketLock; + private Lock cardApiTicketLock; + + /** + * 临时文件目录. + */ + protected volatile File tmpDirFile; + + /** + * 对从 JedisPool.getResource() 获取到的每个 Jedis 实例进行配置 + */ + private JedisConfig jedisConfig; + + protected abstract Jedis getJedis(); + + private Jedis getConfiguredJedis() { + Jedis jedis = getJedis(); + if (jedisConfig != null) { + return jedisConfig.config(jedis); + } else { + return jedis; + } + } + + private String getRedisKey(String key) { + StringBuilder redisKey = new StringBuilder(redisKeyPrefix).append(":"); + if (maId == null) { + return redisKey.append(key).toString(); + } else { + return redisKey.append(maId).append(":").append(key).toString(); + } + } + + private String getValueFromRedis(String key) { + try (Jedis jedis = getConfiguredJedis()) { + return jedis.hget(getRedisKey(key), HASH_VALUE_FIELD); + } + } + + private void setValueToRedis(String key, long expiresTime, String value) { + try (Jedis jedis = getConfiguredJedis()) { + Map hash = new HashMap(); + hash.put(HASH_VALUE_FIELD, value); + hash.put(HASH_EXPIRE_FIELD, String.valueOf(expiresTime)); + jedis.hmset(getRedisKey(key), hash); + } + } + + private long getExpireFromRedis(String key) { + try (Jedis jedis = getConfiguredJedis()) { + String expire = jedis.hget(getRedisKey(key), HASH_EXPIRE_FIELD); + return expire == null ? 0 : Long.parseLong(expire); + } + } + + private void setExpire(String key, long expiresTime) { + try (Jedis jedis = getConfiguredJedis()) { + jedis.hset(getRedisKey(key), HASH_EXPIRE_FIELD, String.valueOf(expiresTime)); + } + } + + public void setRedisKeyPrefix(String redisKeyPrefix) { + this.redisKeyPrefix = redisKeyPrefix; + } + + public void setJedisConfig(JedisConfig jedisConfig) { + this.jedisConfig = jedisConfig; + } + + public void setMaId(String maId) { + this.maId = maId; + } + + @Override + public String getAccessToken() { + return getValueFromRedis(ACCESS_TOKEN); + } + + @Override + public Lock getAccessTokenLock() { + if (accessTokenLock == null) { + synchronized (this) { + if (accessTokenLock == null) { + accessTokenLock = new DistributedLock(getRedisKey("accessTokenLock")); + } + } + } + return accessTokenLock; + } + + @Override + public boolean isAccessTokenExpired() { + return isExpired(getExpireFromRedis(ACCESS_TOKEN)); + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + setValueToRedis(ACCESS_TOKEN, expiresAheadInMillis(expiresInSeconds), accessToken); + } + + @Override + public String getJsapiTicket() { + return getValueFromRedis(JSAPI_TICKET); + } + + @Override + public Lock getJsapiTicketLock() { + if (jsapiTicketLock == null) { + synchronized (this) { + if (jsapiTicketLock == null) { + jsapiTicketLock = new DistributedLock(getRedisKey("jsapiTicketLock")); + } + } + } + return jsapiTicketLock; + } + + @Override + public boolean isJsapiTicketExpired() { + return isExpired(getExpireFromRedis(JSAPI_TICKET)); + } + + @Override + public void expireJsapiTicket() { + setExpire(JSAPI_TICKET, 0); + } + + @Override + public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + setValueToRedis(JSAPI_TICKET, expiresAheadInMillis(expiresInSeconds), jsapiTicket); + } + + + @Override + public String getCardApiTicket() { + return getValueFromRedis(CARD_API_TICKET); + } + + @Override + public Lock getCardApiTicketLock() { + if (cardApiTicketLock == null) { + synchronized (this) { + if (cardApiTicketLock == null) { + cardApiTicketLock = new DistributedLock(getRedisKey("cardApiTicketLock")); + } + } + } + return cardApiTicketLock; + } + + @Override + public boolean isCardApiTicketExpired() { + return isExpired(getExpireFromRedis(CARD_API_TICKET)); + } + + @Override + public void expireCardApiTicket() { + setExpire(CARD_API_TICKET, 0); + } + + @Override + public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + setValueToRedis(CARD_API_TICKET, expiresAheadInMillis(expiresInSeconds), cardApiTicket); + } + + @Override + public void expireAccessToken() { + setExpiresTime(0); + } + + @Override + public long getExpiresTime() { + return getExpireFromRedis(ACCESS_TOKEN); + } + + @Override + public void setExpiresTime(long expiresTime) { + setExpire(ACCESS_TOKEN, expiresTime); + } + + /** + * 基于redis的简单分布式锁. + */ + private class DistributedLock implements Lock { + + private JedisLock lock; + + private DistributedLock(String key) { + this.lock = new JedisLock(getRedisKey(key)); + } + + @Override + public void lock() { + try (Jedis jedis = getConfiguredJedis()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + try (Jedis jedis = getConfiguredJedis()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } + } + + @Override + public boolean tryLock() { + try (Jedis jedis = getConfiguredJedis()) { + return lock.acquire(jedis); + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + try (Jedis jedis = getConfiguredJedis()) { + return lock.acquire(jedis); + } + } + + @Override + public void unlock() { + try (Jedis jedis = getConfiguredJedis()) { + lock.release(jedis); + } + } + + @Override + public Condition newCondition() { + throw new RuntimeException("unsupported method"); + } + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java new file mode 100644 index 0000000000..94e09bc5ca --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java @@ -0,0 +1,285 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; + +import java.io.File; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 + * + * @author Binary Wang + */ +public class WxMaDefaultConfigImpl implements WxMaConfig { + protected volatile String appid; + protected volatile String token; + /** + * 小程序原始ID + */ + protected volatile String originalId; + protected Lock accessTokenLock = new ReentrantLock(); + /** + * 临时文件目录. + */ + protected volatile File tmpDirFile; + private volatile String msgDataFormat; + private volatile String secret; + private volatile String accessToken; + private volatile String aesKey; + private volatile long expiresTime; + /** + * 云环境ID + */ + private volatile String cloudEnv; + private volatile String httpProxyHost; + private volatile int httpProxyPort; + private volatile String httpProxyUsername; + private volatile String httpProxyPassword; + private volatile String jsapiTicket; + private volatile long jsapiTicketExpiresTime; + /** + * 微信卡券的ticket单独缓存. + */ + private volatile String cardApiTicket; + private volatile long cardApiTicketExpiresTime; + protected volatile Lock jsapiTicketLock = new ReentrantLock(); + protected volatile Lock cardApiTicketLock = new ReentrantLock(); + private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; + + /** + * 会过期的数据提前过期时间,默认预留200秒的时间 + */ + protected long expiresAheadInMillis(int expiresInSeconds) { + return System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + + /** + * 判断 expiresTime 是否已经过期 + */ + protected boolean isExpired(long expiresTime) { + return System.currentTimeMillis() > expiresTime; + } + + @Override + public String getAccessToken() { + return this.accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + @Override + public Lock getAccessTokenLock() { + return this.accessTokenLock; + } + + public void setAccessTokenLock(Lock accessTokenLock) { + this.accessTokenLock = accessTokenLock; + } + + @Override + public boolean isAccessTokenExpired() { + return isExpired(this.expiresTime); + } + + @Override + public synchronized void updateAccessToken(WxAccessToken accessToken) { + updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + setAccessToken(accessToken); + setExpiresTime(expiresAheadInMillis(expiresInSeconds)); + } + + @Override + public String getJsapiTicket() { + return this.jsapiTicket; + } + + @Override + public Lock getJsapiTicketLock() { + return this.jsapiTicketLock; + } + + @Override + public boolean isJsapiTicketExpired() { + return isExpired(this.jsapiTicketExpiresTime); + } + + @Override + public void expireJsapiTicket() { + this.jsapiTicketExpiresTime = 0; + } + + @Override + public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + this.jsapiTicket = jsapiTicket; + this.jsapiTicketExpiresTime = expiresAheadInMillis(expiresInSeconds); + } + + + @Override + public String getCardApiTicket() { + return this.cardApiTicket; + } + + @Override + public Lock getCardApiTicketLock() { + return this.cardApiTicketLock; + } + + @Override + public boolean isCardApiTicketExpired() { + return isExpired(this.cardApiTicketExpiresTime); + } + + @Override + public void expireCardApiTicket() { + this.cardApiTicketExpiresTime = 0; + } + + @Override + public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + this.cardApiTicket = cardApiTicket; + this.cardApiTicketExpiresTime = expiresAheadInMillis(expiresInSeconds); + } + + @Override + public void expireAccessToken() { + this.expiresTime = 0; + } + + @Override + public String getSecret() { + return this.secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + @Override + public String getToken() { + return this.token; + } + + public void setToken(String token) { + this.token = token; + } + + @Override + public long getExpiresTime() { + return this.expiresTime; + } + + public void setExpiresTime(long expiresTime) { + this.expiresTime = expiresTime; + } + + @Override + public String getAesKey() { + return this.aesKey; + } + + public void setAesKey(String aesKey) { + this.aesKey = aesKey; + } + + @Override + public String getOriginalId() { + return originalId; + } + + public void setOriginalId(String originalId) { + this.originalId = originalId; + } + + @Override + public String getCloudEnv() { + return this.cloudEnv; + } + + public void setCloudEnv(String cloudEnv) { + this.cloudEnv = cloudEnv; + } + + @Override + public String getMsgDataFormat() { + return this.msgDataFormat; + } + + public void setMsgDataFormat(String msgDataFormat) { + this.msgDataFormat = msgDataFormat; + } + + @Override + public String getHttpProxyHost() { + return this.httpProxyHost; + } + + public void setHttpProxyHost(String httpProxyHost) { + this.httpProxyHost = httpProxyHost; + } + + @Override + public int getHttpProxyPort() { + return this.httpProxyPort; + } + + public void setHttpProxyPort(int httpProxyPort) { + this.httpProxyPort = httpProxyPort; + } + + @Override + public String getHttpProxyUsername() { + return this.httpProxyUsername; + } + + public void setHttpProxyUsername(String httpProxyUsername) { + this.httpProxyUsername = httpProxyUsername; + } + + @Override + public String getHttpProxyPassword() { + return this.httpProxyPassword; + } + + public void setHttpProxyPassword(String httpProxyPassword) { + this.httpProxyPassword = httpProxyPassword; + } + + @Override + public String toString() { + return WxMaGsonBuilder.create().toJson(this); + } + + @Override + public ApacheHttpClientBuilder getApacheHttpClientBuilder() { + return this.apacheHttpClientBuilder; + } + + public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { + this.apacheHttpClientBuilder = apacheHttpClientBuilder; + } + + @Override + public boolean autoRefreshToken() { + return true; + } + + @Override + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java new file mode 100644 index 0000000000..d8876f6aef --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java @@ -0,0 +1,126 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.common.redis.JedisWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import redis.clients.jedis.JedisPool; + +import java.util.concurrent.TimeUnit; + +/** + * 基于redis存储的微信小程序配置类 + */ +public class WxMaRedisBetterConfigImpl extends WxMaDefaultConfigImpl { + private static final String ACCESS_TOKEN_KEY_TPL = "%s:access_token:%s"; + private static final String TICKET_KEY_TPL = "%s:ticket:key:%s:%s"; + private static final String LOCK_KEY_TPL = "%s:lock:%s:"; + + private final WxRedisOps redisOps; + private final String keyPrefix; + + private volatile String accessTokenKey; + private volatile String lockKey; + + public WxMaRedisBetterConfigImpl(WxRedisOps redisOps, String keyPrefix) { + this.redisOps = redisOps; + this.keyPrefix = keyPrefix; + } + + @Override + public void setAppid(String appId) { + super.setAppid(appId); + this.accessTokenKey = String.format(ACCESS_TOKEN_KEY_TPL, this.keyPrefix, appId); + this.lockKey = String.format(LOCK_KEY_TPL, this.keyPrefix, appId); + super.accessTokenLock = this.redisOps.getLock(lockKey.concat("accessTokenLock")); + super.jsapiTicketLock = this.redisOps.getLock(lockKey.concat("jsapiTicketLock")); + super.cardApiTicketLock = this.redisOps.getLock(lockKey.concat("cardApiTicketLock")); + } + + //------------------------------------------------------------------------ + // token相关 + //------------------------------------------------------------------------ + @Override + public String getAccessToken() { + return redisOps.getValue(this.accessTokenKey); + } + + @Override + public boolean isAccessTokenExpired() { + Long expire = redisOps.getExpire(this.accessTokenKey); + return expire == null || expire < 2; + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + redisOps.setValue(this.accessTokenKey, accessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public void expireAccessToken() { + redisOps.expire(this.accessTokenKey, 0, TimeUnit.SECONDS); + } + + //------------------------------------------------------------------------ + // ticket相关 + //------------------------------------------------------------------------ + @Override + public String getJsapiTicket() { + return doGetTicket(TicketType.JSAPI); + } + + @Override + public boolean isJsapiTicketExpired() { + return doIsTicketExpired(TicketType.JSAPI); + } + + @Override + public void expireJsapiTicket() { + doExpireTicket(TicketType.JSAPI); + } + + @Override + public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + doUpdateTicket(TicketType.JSAPI, jsapiTicket, expiresInSeconds); + } + + @Override + public String getCardApiTicket() { + return doGetTicket(TicketType.WX_CARD); + } + + @Override + public boolean isCardApiTicketExpired() { + return doIsTicketExpired(TicketType.WX_CARD); + } + + @Override + public void expireCardApiTicket() { + doExpireTicket(TicketType.WX_CARD); + } + + @Override + public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + doUpdateTicket(TicketType.WX_CARD, cardApiTicket, expiresInSeconds); + } + + private String getTicketRedisKey(TicketType type) { + return String.format(TICKET_KEY_TPL, this.keyPrefix, this.appid, type.getCode()); + } + + private String doGetTicket(TicketType type) { + return redisOps.getValue(this.getTicketRedisKey(type)); + } + + private boolean doIsTicketExpired(TicketType type) { + return redisOps.getExpire(this.getTicketRedisKey(type)) < 2; + } + + private void doUpdateTicket(TicketType type, String ticket, int expiresInSeconds) { + redisOps.setValue(this.getTicketRedisKey(type), ticket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + private void doExpireTicket(TicketType type) { + redisOps.expire(this.getTicketRedisKey(type), 0, TimeUnit.SECONDS); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConfigImpl.java new file mode 100644 index 0000000000..b2ef782d42 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConfigImpl.java @@ -0,0 +1,83 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +/** + * 基于Redis的微信配置provider. 使用连接池 JedisPool 进行 Redis 操作。 + * + *
    + * 需要引入依赖jedis-lock,才能使用该类。
    + * 
    + * + * @author winter + */ +public class WxMaRedisConfigImpl extends AbstractWxMaRedisConfig { + + private JedisPool jedisPool; + + private static final String ACCESS_TOKEN_KEY = "wa:access_token:"; + + private String accessTokenKey; + + /** + * JedisPool 在此配置类是必须项,使用 WxMaRedisConfigImpl(JedisPool) 构造方法来构造实例 + */ + @Deprecated + public WxMaRedisConfigImpl() { + } + + public WxMaRedisConfigImpl(JedisPool jedisPool) { + this.jedisPool = jedisPool; + } + + /** + * 使用 WxMaRedisConfigImpl(JedisPool) 构造方法来设置 JedisPool + */ + @Deprecated + public void setJedisPool(JedisPool jedisPool) { + this.jedisPool = jedisPool; + } + + @Override + protected Jedis getJedis() { + return jedisPool.getResource(); + } + + /** + * 每个公众号生成独有的存储key. + */ + @Override + public void setAppid(String appId) { + super.setAppid(appId); + this.accessTokenKey = ACCESS_TOKEN_KEY.concat(appId); + } + + @Override + public String getAccessToken() { + try (Jedis jedis = this.jedisPool.getResource()) { + return jedis.get(this.accessTokenKey); + } + } + + @Override + public boolean isAccessTokenExpired() { + try (Jedis jedis = this.jedisPool.getResource()) { + return jedis.ttl(accessTokenKey) < 2; + } + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + try (Jedis jedis = this.jedisPool.getResource()) { + jedis.setex(this.accessTokenKey, expiresInSeconds - 200, accessToken); + } + } + + @Override + public void expireAccessToken() { + try (Jedis jedis = this.jedisPool.getResource()) { + jedis.expire(this.accessTokenKey, 0); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConnectionConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConnectionConfigImpl.java new file mode 100644 index 0000000000..0b710aeec5 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConnectionConfigImpl.java @@ -0,0 +1,21 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import lombok.RequiredArgsConstructor; +import redis.clients.jedis.Jedis; + +/** + * 基于Redis的微信配置provider. 使用自己管理的 Jedis 实例进行 Redis 操作。 + * + *
    + * 需要引入依赖jedis-lock,才能使用该类。
    + * 
    + */ +@RequiredArgsConstructor +public class WxMaRedisConnectionConfigImpl extends AbstractWxMaRedisConfig { + private final Jedis jedis; + + @Override + protected Jedis getJedis() { + return jedis; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java new file mode 100644 index 0000000000..c45ad50946 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java @@ -0,0 +1,150 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import lombok.NonNull; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.redis.RedissonWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import org.apache.commons.lang3.StringUtils; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * 基于Redisson的实现 + * + * @author yuanqixun + * @date 2020/5/3 + */ +public class WxMaRedissonConfigImpl extends WxMaDefaultConfigImpl { + + protected final static String LOCK_KEY = "wechat_ma_lock:"; + protected final static String MA_ACCESS_TOKEN_KEY = "wechat_ma_access_token_key:"; + protected final static String MA_JSAPI_TICKET_KEY = "wechat_ma_jsapi_ticket_key:"; + protected final static String MA_CARD_API_TICKET_KEY = "wechat_ma_card_api_ticket_key:"; + + /** + * redis 存储的 key 的前缀,可为空 + */ + protected String keyPrefix; + protected String accessTokenKey; + protected String jsapiTicketKey; + protected String cardApiTicketKey; + protected String lockKey; + + private final WxRedisOps redisOps; + + public WxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient, String keyPrefix) { + this(new RedissonWxRedisOps(redissonClient), keyPrefix); + } + + public WxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient) { + this(redissonClient, null); + } + + private WxMaRedissonConfigImpl(@NonNull WxRedisOps redisOps, String keyPrefix) { + this.redisOps = redisOps; + this.keyPrefix = keyPrefix; + } + + @Override + public void setAppid(String appid) { + super.setAppid(appid); + String prefix = StringUtils.isBlank(keyPrefix) ? "" : + (StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":")); + lockKey = prefix + LOCK_KEY.concat(appid); + accessTokenKey = prefix + MA_ACCESS_TOKEN_KEY.concat(appid); + jsapiTicketKey = prefix + MA_JSAPI_TICKET_KEY.concat(appid); + cardApiTicketKey = prefix + MA_CARD_API_TICKET_KEY.concat(appid); + } + + protected Lock getLockByKey(String key) { + return redisOps.getLock(key); + } + + @Override + public Lock getAccessTokenLock() { + return getLockByKey(this.lockKey.concat(":").concat("accessToken")); + } + + @Override + public Lock getCardApiTicketLock() { + return getLockByKey(this.lockKey.concat(":").concat("cardApiTicket")); + + } + + @Override + public Lock getJsapiTicketLock() { + return getLockByKey(this.lockKey.concat(":").concat("jsapiTicket")); + } + + @Override + public String getAccessToken() { + return redisOps.getValue(this.accessTokenKey); + } + + @Override + public boolean isAccessTokenExpired() { + Long expire = redisOps.getExpire(this.accessTokenKey); + return expire == null || expire < 2; + } + + @Override + public void updateAccessToken(WxAccessToken accessToken) { + redisOps.setValue(this.accessTokenKey, accessToken.getAccessToken(), accessToken.getExpiresIn(), TimeUnit.SECONDS); + } + + @Override + public void updateAccessToken(String accessToken, int expiresInSeconds) { + redisOps.setValue(this.accessTokenKey, accessToken, expiresInSeconds, TimeUnit.SECONDS); + } + + @Override + public String getJsapiTicket() { + return redisOps.getValue(this.jsapiTicketKey); + } + + @Override + public boolean isJsapiTicketExpired() { + Long expire = redisOps.getExpire(this.jsapiTicketKey); + return expire == null || expire < 2; + } + + @Override + public void expireJsapiTicket() { + redisOps.expire(this.jsapiTicketKey, 0, TimeUnit.SECONDS); + } + + @Override + public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + redisOps.setValue(this.jsapiTicketKey, jsapiTicket, expiresInSeconds, TimeUnit.SECONDS); + + } + + @Override + public String getCardApiTicket() { + return redisOps.getValue(cardApiTicketKey); + } + + @Override + public boolean isCardApiTicketExpired() { + Long expire = redisOps.getExpire(this.cardApiTicketKey); + return expire == null || expire < 2; + } + + @Override + public void expireCardApiTicket() { + redisOps.expire(this.cardApiTicketKey, 0, TimeUnit.SECONDS); + } + + @Override + public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + redisOps.setValue(this.cardApiTicketKey, cardApiTicket, expiresInSeconds, TimeUnit.SECONDS); + } + + @Override + public void expireAccessToken() { + redisOps.expire(this.accessTokenKey, 0, TimeUnit.SECONDS); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java new file mode 100644 index 0000000000..2ca92d084c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java @@ -0,0 +1,173 @@ +package cn.binarywang.wx.miniapp.constant; + +/** + *
    + *  小程序常量.
    + * 
    + * + * @author Binary Wang + */ +public class WxMaConstants { + /** + * 微信接口返回的参数errcode. + */ + public static final String ERRCODE = "errcode"; + + /** + * 素材类型. + */ + public static class MediaType { + /** + * 图片. + */ + public static final String IMAGE = "image"; + } + + /** + * 消息格式. + */ + public static class MsgDataFormat { + public static final String XML = "XML"; + public static final String JSON = "JSON"; + } + + /** + * 客服消息的消息类型. + */ + public static class KefuMsgType { + /** + * 文本消息. + */ + public static final String TEXT = "text"; + /** + * 图片消息. + */ + public static final String IMAGE = "image"; + /** + * 图文链接. + */ + public static final String LINK = "link"; + /** + * 小程序卡片消息. + */ + public static final String MA_PAGE = "miniprogrampage"; + } + + /** + * 内容安全检测的媒体类型 + */ + public static final class SecCheckMediaType { + + /** + * 音频 + */ + public static final int VOICE = 1; + + /** + * 图片 + */ + public static final int IMAGE = 2; + } + + /** + * 快递账号绑定类型 + */ + public static final class BindAccountType{ + + /** + * 绑定 + */ + public static final String BIND = "bind"; + + /** + * 解绑 + */ + public static final String UNBIND = "unbind"; + } + + /** + * 快递下单订单来源 + */ + public static final class OrderAddSource{ + + /** + * 小程序 + */ + public static final int MINI_PROGRAM = 0; + + /** + * APP或H5 + */ + public static final int APP_OR_H5 = 2; + } + + /** + * 快递下单保价 + */ + public static final class OrderAddInsured{ + /** + * 不保价 + */ + public static final int INSURED_PROGRAM = 0; + + /** + * 保价 + */ + public static final int USE_INSURED = 1; + + /** + * 默认保价金额 + */ + public static final int DEFAULT_INSURED_VALUE = 0; + } + + + /** + * 小程序订阅消息跳转小程序类型 + * + * developer为开发版;trial为体验版;formal为正式版;默认为正式版 + */ + public static final class MiniprogramState{ + /** + * 开发版 + */ + public static final String DEVELOPER = "developer"; + + /** + * 体验版 + */ + public static final String TRIAL = "trial"; + + /** + * 正式版 + */ + public static final String FORMAL = "formal"; + } + + + /** + * 进入小程序查看的语言类型 + * 支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN + */ + public static final class MiniprogramLang{ + /** + * 简体中文 + */ + public static final String ZH_CN = "zh_CN"; + + /** + * 英文 + */ + public static final String EN_US = "en_US"; + + /** + * 繁体中文 + */ + public static final String ZH_HK = "zh_HK"; + + /** + * 繁体中文 + */ + public static final String ZH_TW = "zh_TW"; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java new file mode 100644 index 0000000000..9fdd956934 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java @@ -0,0 +1,29 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; + +import java.util.Map; + +/** + * 处理小程序推送消息的处理器接口. + * + * @author Binary Wang + */ +public interface WxMaMessageHandler { + /** + * 处理消息. + * + * @param message 输入消息 + * @param context 上下文 + * @param service 服务类 + * @param sessionManager session管理器 + * @return 输出消息 + * @throws WxErrorException 异常 + */ + WxMaXmlOutMessage handle(WxMaMessage message, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java new file mode 100644 index 0000000000..31600a231b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java @@ -0,0 +1,32 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; + +import java.util.Map; + +/** + * 微信消息拦截器,可以用来做验证. + * + * @author Binary Wang + */ +public interface WxMaMessageInterceptor { + + /** + * 拦截微信消息. + * + * @param wxMessage . + * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @param wxMaService . + * @param sessionManager . + * @return true代表OK,false代表不OK + * @throws WxErrorException . + */ + boolean intercept(WxMaMessage wxMessage, + Map context, + WxMaService wxMaService, + WxSessionManager sessionManager) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java new file mode 100644 index 0000000000..5283713c5d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java @@ -0,0 +1,20 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.bean.WxMaMessage; + +/** + * 消息匹配器,用在消息路由的时候. + * + * @author Binary Wang + */ +public interface WxMaMessageMatcher { + + /** + * 消息是否匹配某种模式. + * + * @param message 消息 + * @return 是否匹配 + */ + boolean match(WxMaMessage message); + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java new file mode 100644 index 0000000000..e932da641d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java @@ -0,0 +1,159 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import lombok.Data; +import me.chanjar.weixin.common.api.WxErrorExceptionHandler; +import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; +import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; +import me.chanjar.weixin.common.session.InternalSession; +import me.chanjar.weixin.common.session.InternalSessionManager; +import me.chanjar.weixin.common.session.StandardSessionManager; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.common.util.LogExceptionHandler; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; + +/** + * @author Binary Wang + */ +@Data +public class WxMaMessageRouter { + private static final int DEFAULT_THREAD_POOL_SIZE = 100; + private final Logger log = LoggerFactory.getLogger(WxMaMessageRouter.class); + private final List rules = new ArrayList<>(); + + private final WxMaService wxMaService; + + private ExecutorService executorService; + + private WxSessionManager sessionManager; + + private WxErrorExceptionHandler exceptionHandler; + + private WxMessageDuplicateChecker messageDuplicateChecker; + + public WxMaMessageRouter(WxMaService wxMaService) { + this.wxMaService = wxMaService; + ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("WxMaMessageRouter-pool-%d").build(); + this.executorService = new ThreadPoolExecutor(DEFAULT_THREAD_POOL_SIZE, DEFAULT_THREAD_POOL_SIZE, + 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), namedThreadFactory); + this.sessionManager = new StandardSessionManager(); + this.exceptionHandler = new LogExceptionHandler(); + this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); + } + + /** + * 开始一个新的Route规则. + */ + public WxMaMessageRouterRule rule() { + return new WxMaMessageRouterRule(this); + } + + /** + * 处理微信消息. + */ + private WxMaXmlOutMessage route(final WxMaMessage wxMessage, final Map context) { + if (isMsgDuplicated(wxMessage)) { + // 如果是重复消息,那么就不做处理 + return null; + } + + final List matchRules = new ArrayList<>(); + // 收集匹配的规则 + for (final WxMaMessageRouterRule rule : this.rules) { + if (rule.test(wxMessage)) { + matchRules.add(rule); + if (!rule.isReEnter()) { + break; + } + } + } + + if (matchRules.size() == 0) { + return null; + } + + final List> futures = new ArrayList<>(); + WxMaXmlOutMessage result = null; + for (final WxMaMessageRouterRule rule : matchRules) { + // 返回最后一个非异步的rule的执行结果 + if (rule.isAsync()) { + futures.add( + this.executorService.submit(new Runnable() { + @Override + public void run() { + rule.service(wxMessage, context, WxMaMessageRouter.this.wxMaService, WxMaMessageRouter.this.sessionManager, WxMaMessageRouter.this.exceptionHandler); + } + }) + ); + } else { + result = rule.service(wxMessage, context, this.wxMaService, this.sessionManager, this.exceptionHandler); + // 在同步操作结束,session访问结束 + this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); + sessionEndAccess(wxMessage); + } + } + + if (futures.size() > 0) { + this.executorService.submit(new Runnable() { + @Override + public void run() { + for (Future future : futures) { + try { + future.get(); + WxMaMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser()); + // 异步操作结束,session访问结束 + sessionEndAccess(wxMessage); + } catch (InterruptedException | ExecutionException e) { + WxMaMessageRouter.this.log.error("Error happened when wait task finish", e); + } + } + } + }); + } + return result; + } + + public WxMaXmlOutMessage route(final WxMaMessage wxMessage) { + return this.route(wxMessage, new HashMap(2)); + } + + private boolean isMsgDuplicated(WxMaMessage wxMessage) { + StringBuilder messageId = new StringBuilder(); + if (wxMessage.getMsgId() == null) { + messageId.append(wxMessage.getCreateTime()) + .append("-").append(wxMessage.getFromUser()) + .append("-").append(StringUtils.trimToEmpty(wxMessage.getEvent())); + } else { + messageId.append(wxMessage.getMsgId()) + .append("-").append(wxMessage.getCreateTime()) + .append("-").append(wxMessage.getFromUser()); + } + + if (StringUtils.isNotEmpty(wxMessage.getToUser())) { + messageId.append("-").append(wxMessage.getToUser()); + } + + return this.messageDuplicateChecker.isDuplicate(messageId.toString()); + } + + /** + * 对session的访问结束. + */ + private void sessionEndAccess(WxMaMessage wxMessage) { + InternalSession session = ((InternalSessionManager) this.sessionManager).findSession(wxMessage.getFromUser()); + if (session != null) { + session.endAccess(); + } + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java new file mode 100644 index 0000000000..41f3e99574 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java @@ -0,0 +1,335 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import me.chanjar.weixin.common.api.WxErrorExceptionHandler; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * @author Binary Wang + */ +public class WxMaMessageRouterRule { + private final WxMaMessageRouter routerBuilder; + + private boolean async = true; + + private String fromUser; + + private String msgType; + + private String event; + + private String eventKey; + + private String content; + + private String rContent; + + private String title; + + private WxMaMessageMatcher matcher; + + private boolean reEnter = false; + + private List handlers = new ArrayList<>(); + + private List interceptors = new ArrayList<>(); + + public WxMaMessageRouterRule(WxMaMessageRouter routerBuilder) { + this.routerBuilder = routerBuilder; + } + + /** + * 设置是否异步执行,默认是true. + */ + public WxMaMessageRouterRule async(boolean async) { + this.async = async; + return this; + } + + /** + * 如果msgType等于某值. + */ + public WxMaMessageRouterRule msgType(String msgType) { + this.msgType = msgType; + return this; + } + + /** + * 标题,发送小程序页卡时有效 + * @param title + * @return + */ + public WxMaMessageRouterRule title(String title){ + this.title = title; + return this; + } + + /** + * 如果event等于某值. + */ + public WxMaMessageRouterRule event(String event) { + this.event = event; + return this; + } + + /** + * 如果eventKey等于某值. + */ + public WxMaMessageRouterRule eventKey(String eventKey) { + this.eventKey = eventKey; + return this; + } + + /** + * 如果content等于某值. + */ + public WxMaMessageRouterRule content(String content) { + this.content = content; + return this; + } + + /** + * 如果content匹配该正则表达式. + */ + public WxMaMessageRouterRule rContent(String regex) { + this.rContent = regex; + return this; + } + + /** + * 如果fromUser等于某值. + */ + public WxMaMessageRouterRule fromUser(String fromUser) { + this.fromUser = fromUser; + return this; + } + + + + /** + * 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候. + */ + public WxMaMessageRouterRule matcher(WxMaMessageMatcher matcher) { + this.matcher = matcher; + return this; + } + + /** + * 设置微信消息拦截器. + */ + public WxMaMessageRouterRule interceptor(WxMaMessageInterceptor interceptor) { + return interceptor(interceptor, (WxMaMessageInterceptor[]) null); + } + + /** + * 设置微信消息拦截器. + */ + public WxMaMessageRouterRule interceptor(WxMaMessageInterceptor interceptor, WxMaMessageInterceptor... otherInterceptors) { + this.interceptors.add(interceptor); + if (otherInterceptors != null && otherInterceptors.length > 0) { + for (WxMaMessageInterceptor i : otherInterceptors) { + this.interceptors.add(i); + } + } + return this; + } + + /** + * 设置微信消息处理器. + */ + public WxMaMessageRouterRule handler(WxMaMessageHandler handler) { + return handler(handler, (WxMaMessageHandler[]) null); + } + + /** + * 设置微信消息处理器. + */ + public WxMaMessageRouterRule handler(WxMaMessageHandler handler, WxMaMessageHandler... otherHandlers) { + this.handlers.add(handler); + if (otherHandlers != null && otherHandlers.length > 0) { + for (WxMaMessageHandler i : otherHandlers) { + this.handlers.add(i); + } + } + return this; + } + + /** + * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则. + */ + public WxMaMessageRouter end() { + this.routerBuilder.getRules().add(this); + return this.routerBuilder; + } + + /** + * 规则结束,但是消息还会进入其他规则. + */ + public WxMaMessageRouter next() { + this.reEnter = true; + return end(); + } + + + + /** + * 将微信自定义的事件修正为不区分大小写. + * 比如框架定义的事件常量为click,但微信传递过来的却是CLICK + */ + protected boolean test(WxMaMessage wxMessage) { + return + (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUser())) + && + (this.msgType == null || this.msgType.toLowerCase().equals((wxMessage.getMsgType() == null ? null : wxMessage.getMsgType().toLowerCase()))) + && + (this.event == null || this.event.toLowerCase().equals((wxMessage.getEvent() == null ? null : wxMessage.getEvent().toLowerCase()))) + && + (this.content == null || this.content + .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) + && + (this.rContent == null || Pattern + .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) + && + (this.matcher == null || this.matcher.match(wxMessage)) + && + (this.title == null || this.title + .equals(wxMessage.getTitle() == null ? null : wxMessage.getTitle().trim())) + ; + } + + /** + * 处理微信推送过来的消息. + */ + protected WxMaXmlOutMessage service(WxMaMessage wxMessage, + Map context, + WxMaService wxMaService, + WxSessionManager sessionManager, + WxErrorExceptionHandler exceptionHandler) { + if (context == null) { + context = new HashMap<>(16); + } + + WxMaXmlOutMessage outMessage = null; + try { + // 如果拦截器不通过 + for (WxMaMessageInterceptor interceptor : this.interceptors) { + if (!interceptor.intercept(wxMessage, context, wxMaService, sessionManager)) { + return null; + } + } + + // 交给handler处理 + for (WxMaMessageHandler handler : this.handlers) { + // 返回最后handler的结果 + if (handler == null) { + continue; + } + outMessage = handler.handle(wxMessage, context, wxMaService, sessionManager); + } + } catch (WxErrorException e) { + exceptionHandler.handle(e); + } + + return outMessage; + } + + public WxMaMessageRouter getRouterBuilder() { + return this.routerBuilder; + } + + public boolean isAsync() { + return this.async; + } + + public void setAsync(boolean async) { + this.async = async; + } + + public String getFromUser() { + return this.fromUser; + } + + public void setFromUser(String fromUser) { + this.fromUser = fromUser; + } + + public String getMsgType() { + return this.msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getEvent() { + return this.event; + } + + public void setEvent(String event) { + this.event = event; + } + + public String getEventKey() { + return this.eventKey; + } + + public void setEventKey(String eventKey) { + this.eventKey = eventKey; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getrContent() { + return this.rContent; + } + + public void setrContent(String rContent) { + this.rContent = rContent; + } + + public WxMaMessageMatcher getMatcher() { + return this.matcher; + } + + public void setMatcher(WxMaMessageMatcher matcher) { + this.matcher = matcher; + } + + public boolean isReEnter() { + return this.reEnter; + } + + public void setReEnter(boolean reEnter) { + this.reEnter = reEnter; + } + + public List getHandlers() { + return this.handlers; + } + + public void setHandlers(List handlers) { + this.handlers = handlers; + } + + public List getInterceptors() { + return this.interceptors; + } + + public void setInterceptors(List interceptors) { + this.interceptors = interceptors; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaXmlOutMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaXmlOutMessage.java new file mode 100644 index 0000000000..7211e0531a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaXmlOutMessage.java @@ -0,0 +1,60 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import cn.binarywang.wx.miniapp.util.xml.XStreamTransformer; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; + +import java.io.Serializable; + +/** + * 微信小程序输出给微信服务器的消息. + * + * @author Binary Wang + * @date 2019-06-22 + */ +@Data +@XStreamAlias("xml") +@Accessors(chain = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class WxMaXmlOutMessage implements Serializable { + private static final long serialVersionUID = 4241135225946919153L; + + @XStreamAlias("ToUserName") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String toUserName; + + @XStreamAlias("FromUserName") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String fromUserName; + + @XStreamAlias("CreateTime") + protected Long createTime; + + @XStreamAlias("MsgType") + @XStreamConverter(value = XStreamCDataConverter.class) + protected String msgType; + + @SuppressWarnings("unchecked") + public String toXml() { + return XStreamTransformer.toXml((Class) this.getClass(), this); + } + + /** + * 转换成加密的xml格式. + */ + public String toEncryptedXml(WxMaConfig config) { + String plainXml = toXml(); + WxMaCryptUtils pc = new WxMaCryptUtils(config); + return pc.encrypt(plainXml); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/JoinerUtils.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/JoinerUtils.java new file mode 100644 index 0000000000..3ef3eb915d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/JoinerUtils.java @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2019,sunnybs. + * All Rights Reserved. + *

    + * Project Name:yigou + */ +package cn.binarywang.wx.miniapp.util; + +import com.google.common.base.Joiner; + +/** + * ClassName: JoinerUtils
    + * Description: 字符串连接器
    + * Date: 2019年10月18日 下午1:42:59
    + * + * @author wsp + */ + +public class JoinerUtils { + private static final String NULL = "null"; + + /** + *

    + * 空白连接器,忽略null + *

    + * + *
    +     * JoinerUtils.blankJoiner.join("a", "b", "c");
    +     * result : abc
    +     * 
    +     * JoinerUtils.blankJoiner.join("a", null, "c");
    +     * result : ac
    +     * 
    + */ + public static final Joiner blankJoiner = Joiner.on("").skipNulls(); + /** + *

    + * 空白连接器 + *

    + * + *
    +     * JoinerUtils.blankJoinerWithNull.join("a", "b", "c");
    +     * result : abc
    +     * 
    +     * JoinerUtils.blankJoinerWithNull.join("a", null, "c");
    +     * result : anullc
    +     * 
    + */ + public static final Joiner blankJoinerWithNull = Joiner.on("").useForNull(NULL); + + /** + *

    + * 空格连接器,忽略null + *

    + * + *
    +     * JoinerUtils.spaceJoiner.join("a", "b", "c");
    +     * result : a b c
    +     * 
    +     * JoinerUtils.spaceJoiner.join("a", null, "c");
    +     * result : a c
    +     * 
    + */ + public static final Joiner spaceJoiner = Joiner.on(" ").skipNulls(); + /** + *

    + * 空格连接器 + *

    + * + *
    +     * JoinerUtils.spaceJoinerWithNull.join("a", "b", "c");
    +     * result : a b c
    +     * 
    +     * JoinerUtils.spaceJoinerWithNull.join("a", null, "c");
    +     * result : a null c
    +     * 
    + */ + public static final Joiner spaceJoinerWithNull = Joiner.on(" ").useForNull(NULL); + + /** + *

    + * 逗号分隔符连接器,忽略null + *

    + * + *
    +   * JoinerUtils.commaJoiner.join("a", "b", "c");
    +   * result : a,b,c
    +   *
    +   * JoinerUtils.commaJoiner.join("a", null, "c");
    +   * result : a,c
    +   * 
    + */ + public static final Joiner commaJoiner = Joiner.on(",").skipNulls(); + /** + *

    + * 逗号分隔符连接器 + *

    + * + *
    +   * JoinerUtils.commaJoinerWithNull.join("a", "b", "c");
    +   * result : a,b,c
    +   *
    +   * JoinerUtils.commaJoinerWithNull.join("a", null, "c");
    +   * result : a,null,c
    +   * 
    + */ + public static final Joiner commaJoinerWithNull = Joiner.on(",").useForNull(NULL); + + /** + *

    + * 等号分隔符连接器,忽略null + *

    + * + *
    +   * JoinerUtils.equalJoiner.join("a", "b", "c");
    +   * result : a=b=c
    +   *
    +   * JoinerUtils.equalJoiner.join("a", null, "c");
    +   * result : a=c
    +   * 
    + */ + public static final Joiner equalJoiner = Joiner.on("=").skipNulls(); + /** + *

    + * 等号分隔符连接器 + *

    + * + *
    +   * JoinerUtils.equalJoinerWithNull.join("a", "b", "c");
    +   * result : a=b=c
    +   *
    +   * JoinerUtils.equalJoinerWithNull.join("a", null, "c");
    +   * result : a=null=c
    +   * 
    + */ + public static final Joiner equalJoinerWithNull = Joiner.on("=").useForNull(NULL); + + /** + *

    + * 竖线分隔符连接器,忽略null + *

    + * + *
    +     * JoinerUtils.vLineJoiner.join("a", "b", "c");
    +     * result : a|b|c
    +     * 
    +     * JoinerUtils.vLineJoiner.join("a", null, "c");
    +     * result : a|c
    +     * 
    + */ + public static final Joiner vLineJoiner = Joiner.on("|").skipNulls(); + /** + *

    + * 竖线分隔符连接器 + *

    + * + *
    +     * JoinerUtils.vLineJoinerWithNull.join("a", "b", "c");
    +     * result : a|b|c
    +     * 
    +     * JoinerUtils.vLineJoinerWithNull.join("a", null, "c");
    +     * result : a|null|c
    +     * 
    + */ + public static final Joiner vLineJoinerWithNull = Joiner.on("|").useForNull(NULL); + + /** + *

    + * 中横线分隔符连接器,忽略null + *

    + * + *
    +     * JoinerUtils.hLineJoiner.join("a", "b", "c");
    +     * result : a-b-c
    +     * 
    +     * JoinerUtils.hLineJoiner.join("a", null, "c");
    +     * result : a-c
    +     * 
    + */ + public static final Joiner hLineJoiner = Joiner.on("-").skipNulls(); + /** + *

    + * 中横线分隔符连接器 + *

    + * + *
    +     * JoinerUtils.hLineJoinerWithNull.join("a", "b", "c");
    +     * result : a-b-c
    +     * 
    +     * JoinerUtils.hLineJoinerWithNull.join("a", null, "c");
    +     * result : a-null-c
    +     * 
    + */ + public static final Joiner hLineJoinerWithNull = Joiner.on("-").useForNull(NULL); + + /** + *

    + * 下划线分隔符连接器,忽略null + *

    + * + *
    +     * JoinerUtils.underlineJoiner.join("a", "b", "c");
    +     * result : a_b_c
    +     * 
    +     * JoinerUtils.underlineJoiner.join("a", null, "c");
    +     * result : a_c
    +     * 
    + */ + public static final Joiner underlineJoiner = Joiner.on("_").skipNulls(); + /** + *

    + * 下划线分隔符连接器 + *

    + * + *
    +     * JoinerUtils.underlineJoinerWithNull.join("a", "b", "c");
    +     * result : a_b_c
    +     * 
    +     * JoinerUtils.underlineJoinerWithNull.join("a", null, "c");
    +     * result : a_null_c
    +     * 
    + */ + public static final Joiner underlineJoinerWithNull = Joiner.on("_").useForNull(NULL); + + /** + *

    + * 斜线分隔符连接器,忽略null + *

    + * + *
    +     * JoinerUtils.pathJoiner.join("a", "b", "c");
    +     * result : a/b/c
    +     * 
    +     * JoinerUtils.pathJoiner.join("a", null, "c");
    +     * result : a/c
    +     * 
    + */ + public static final Joiner pathJoiner = Joiner.on("/").skipNulls(); + /** + *

    + * 斜线分隔符连接器 + *

    + * + *
    +     * JoinerUtils.pathJoinerWithNull.join("a", "b", "c");
    +     * result : a/b/c
    +     * 
    +     * JoinerUtils.pathJoinerWithNull.join("a", null, "c");
    +     * result : a/null/c
    +     * 
    + */ + public static final Joiner pathJoinerWithNull = Joiner.on("/").useForNull(NULL); +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeBytesRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeBytesRequestExecutor.java new file mode 100644 index 0000000000..bd473fb21c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeBytesRequestExecutor.java @@ -0,0 +1,66 @@ +package cn.binarywang.wx.miniapp.util; + +import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author Binary Wang + */ +public class QrcodeBytesRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public QrcodeBytesRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + @Override + public byte[] execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + httpPost.setConfig( + RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() + ); + } + + httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson())); + + try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); + final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0 + && ContentType.APPLICATION_JSON.getMimeType() + .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent, wxType)); + } + + return IOUtils.toByteArray(inputStream); + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeRequestExecutor.java new file mode 100644 index 0000000000..d3b764ff1a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeRequestExecutor.java @@ -0,0 +1,68 @@ +package cn.binarywang.wx.miniapp.util; + +import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +/** + * @author Binary Wang + */ +public class QrcodeRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public QrcodeRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + @Override + public File execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + httpPost.setConfig( + RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() + ); + } + + httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson(), ContentType.APPLICATION_JSON)); + + try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); + final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0 + && ContentType.APPLICATION_JSON.getMimeType() + .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent, wxType)); + } + + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/WxMaConfigHolder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/WxMaConfigHolder.java new file mode 100644 index 0000000000..e73cb2282d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/WxMaConfigHolder.java @@ -0,0 +1,31 @@ +package cn.binarywang.wx.miniapp.util; + +/** + * 小程序存储值存放类. + * + * @author Binary Wang + * @date 2020-08-16 + */ +public class WxMaConfigHolder { + private final static ThreadLocal THREAD_LOCAL = new ThreadLocal() { + @Override + protected String initialValue() { + return "default"; + } + }; + + public static String get() { + return THREAD_LOCAL.get(); + } + + public static void set(String label) { + THREAD_LOCAL.set(label); + } + + /** + * 此方法需要用户根据自己程序代码,在适当位置手动触发调用,本SDK里无法判断调用时机 + */ + public static void remove() { + THREAD_LOCAL.remove(); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java new file mode 100644 index 0000000000..6913541de7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java @@ -0,0 +1,85 @@ +package cn.binarywang.wx.miniapp.util.crypt; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.security.Key; +import java.security.Security; +import java.util.Arrays; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import me.chanjar.weixin.common.util.crypto.PKCS7Encoder; + +/** + * @author Binary Wang + */ +public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCryptUtil { + private static final Charset UTF_8 = StandardCharsets.UTF_8; + + public WxMaCryptUtils(WxMaConfig config) { + this.appidOrCorpid = config.getAppid(); + this.token = config.getToken(); + this.aesKey = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(config.getAesKey())); + } + + /** + * AES解密. + * + * @param sessionKey session_key + * @param encryptedData 消息密文 + * @param ivStr iv字符串 + */ + public static String decrypt(String sessionKey, String encryptedData, String ivStr) { + try { + AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); + params.init(new IvParameterSpec(Base64.decodeBase64(ivStr))); + + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params); + + return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), UTF_8); + } catch (Exception e) { + throw new RuntimeException("AES解密失败!", e); + } + } + + + /** + * AES解密. + * + * @param sessionKey session_key + * @param encryptedData 消息密文 + * @param ivStr iv字符串 + */ + public static String decryptAnotherWay(String sessionKey, String encryptedData, String ivStr) { + byte[] keyBytes = Base64.decodeBase64(sessionKey.getBytes(UTF_8)); + + int base = 16; + if (keyBytes.length % base != 0) { + int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0); + byte[] temp = new byte[groups * base]; + Arrays.fill(temp, (byte) 0); + System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length); + keyBytes = temp; + } + + Security.addProvider(new BouncyCastleProvider()); + Key key = new SecretKeySpec(keyBytes, "AES"); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(Base64.decodeBase64(ivStr.getBytes(UTF_8)))); + return new String(cipher.doFinal(Base64.decodeBase64(encryptedData.getBytes(UTF_8))), UTF_8); + } catch (Exception e) { + throw new RuntimeException("AES解密失败!", e); + } + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeCommitRequestGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeCommitRequestGsonAdapter.java new file mode 100755 index 0000000000..410f86ca1f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeCommitRequestGsonAdapter.java @@ -0,0 +1,28 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; + +/** + * @author Charming + * @since 2018-04-26 19:47 + */ +public class WxMaCodeCommitRequestGsonAdapter implements JsonSerializer { + + @Override + public JsonElement serialize(WxMaCodeCommitRequest request, Type typeOfSrc, JsonSerializationContext context) { + JsonObject requestJson = new JsonObject(); + requestJson.addProperty("template_id", request.getTemplateId()); + requestJson.addProperty("user_version", request.getUserVersion()); + requestJson.addProperty("user_desc", request.getUserDesc()); + if (request.getExtConfig() != null) { + requestJson.addProperty("ext_json", WxMaGsonBuilder.create().toJson(request.getExtConfig())); + } + return requestJson; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeVersionDistributionGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeVersionDistributionGsonAdapter.java new file mode 100644 index 0000000000..027ca6a959 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeVersionDistributionGsonAdapter.java @@ -0,0 +1,50 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; + +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Charming + * @since 2018-04-26 19:47 + */ +public class WxMaCodeVersionDistributionGsonAdapter implements JsonDeserializer { + @Override + public WxMaCodeVersionDistribution deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + if (json == null) { + return null; + } + + WxMaCodeVersionDistribution distribution = new WxMaCodeVersionDistribution(); + JsonObject object = json.getAsJsonObject(); + distribution.setNowVersion(GsonHelper.getString(object, "now_version")); + distribution.setUvInfo(getAsMap(object.getAsJsonObject("uv_info"), "items")); + return distribution; + } + + private Map getAsMap(JsonObject object, String memberName) { + JsonArray array = object.getAsJsonArray(memberName); + if (array != null && array.size() > 0) { + Map map = new LinkedHashMap<>(array.size()); + for (JsonElement element : array) { + JsonObject elementObject = element.getAsJsonObject(); + String version = GsonHelper.getString(elementObject, "version"); + if (version != null) { + Float percentage = GsonHelper.getFloat(elementObject, "percentage"); + map.put(version, percentage); + } + } + return map; + } + return null; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java new file mode 100644 index 0000000000..21b582d5bd --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java @@ -0,0 +1,34 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; +import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitDistribution; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * @author Binary Wang + */ +public class WxMaGsonBuilder { + private static final GsonBuilder INSTANCE = new GsonBuilder(); + + static { + INSTANCE.disableHtmlEscaping(); + INSTANCE.registerTypeAdapter(WxMaSubscribeMessage.class, new WxMaSubscribeMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaUniformMessage.class, new WxMaUniformMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaCodeCommitRequest.class, new WxMaCodeCommitRequestGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaCodeVersionDistribution.class, new WxMaCodeVersionDistributionGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaVisitDistribution.class, new WxMaVisitDistributionGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaRetainInfo.class, new WxMaRetainInfoGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaUserPortrait.class, new WxMaUserPortraitGsonAdapter()); + } + + public static Gson create() { + return INSTANCE.create(); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaRetainInfoGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaRetainInfoGsonAdapter.java new file mode 100644 index 0000000000..a51972b4bd --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaRetainInfoGsonAdapter.java @@ -0,0 +1,52 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; + +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Charming + * @since 2018-04-28 + */ +public class WxMaRetainInfoGsonAdapter implements JsonDeserializer { + @Override + public WxMaRetainInfo deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + if (json == null) { + return null; + } + + WxMaRetainInfo retainInfo = new WxMaRetainInfo(); + JsonObject object = json.getAsJsonObject(); + String refDate = GsonHelper.getString(object, "ref_date"); + retainInfo.setRefDate(refDate); + retainInfo.setVisitUvNew(getAsMap(object, "visit_uv_new")); + retainInfo.setVisitUv(getAsMap(object, "visit_uv")); + return retainInfo; + } + + private Map getAsMap(JsonObject object, String memberName) { + JsonArray array = object.getAsJsonArray(memberName); + if (array != null && array.size() > 0) { + Map map = new LinkedHashMap<>(array.size()); + for (JsonElement element : array) { + JsonObject elementObject = element.getAsJsonObject(); + Integer key = GsonHelper.getInteger(elementObject, "key"); + if (key != null) { + Integer value = GsonHelper.getInteger(elementObject, "value"); + map.put(key, value); + } + } + return map; + } + return null; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaSubscribeMessageGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaSubscribeMessageGsonAdapter.java new file mode 100644 index 0000000000..89f8bad8d3 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaSubscribeMessageGsonAdapter.java @@ -0,0 +1,50 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; + +/** + * . + * + * @author S + */ +public class WxMaSubscribeMessageGsonAdapter implements JsonSerializer { + @Override + public JsonElement serialize(WxMaSubscribeMessage message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("touser", message.getToUser()); + messageJson.addProperty("template_id", message.getTemplateId()); + if (message.getPage() != null) { + messageJson.addProperty("page", message.getPage()); + } + + if (message.getMiniprogramState() != null) { + messageJson.addProperty("miniprogram_state", message.getMiniprogramState()); + } + + if (message.getLang() != null) { + messageJson.addProperty("lang", message.getLang()); + } + + JsonObject data = new JsonObject(); + messageJson.add("data", data); + + if (message.getData() == null) { + return messageJson; + } + + for (WxMaSubscribeMessage.Data datum : message.getData()) { + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("value", datum.getValue()); + data.add(datum.getName(), dataJson); + } + + return messageJson; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapter.java new file mode 100644 index 0000000000..75ccd68aaa --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapter.java @@ -0,0 +1,97 @@ +package cn.binarywang.wx.miniapp.util.json; + +import java.lang.reflect.Type; + +import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; +import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * @author Binary Wang + */ +public class WxMaUniformMessageGsonAdapter implements JsonSerializer { + + @Override + public JsonElement serialize(WxMaUniformMessage message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("touser", message.getToUser()); + if (message.isMpTemplateMsg()) { + JsonObject msg = new JsonObject(); + if (message.getAppid() != null) { + msg.addProperty("appid", message.getAppid()); + } + + msg.addProperty("template_id", message.getTemplateId()); + + if (message.getUrl() != null) { + msg.addProperty("url", message.getUrl()); + } + + final WxMaUniformMessage.MiniProgram miniProgram = message.getMiniProgram(); + if (miniProgram != null) { + JsonObject miniProgramJson = new JsonObject(); + miniProgramJson.addProperty("appid", miniProgram.getAppid()); + if (miniProgram.isUsePath()) { + miniProgramJson.addProperty("path", miniProgram.getPagePath()); + } else if (miniProgram.isUsePagePath()) { + miniProgramJson.addProperty("pagePath", miniProgram.getPagePath()); + } else { + miniProgramJson.addProperty("pagepath", miniProgram.getPagePath()); + } + msg.add("miniprogram", miniProgramJson); + } + + if (message.getData() != null) { + JsonObject data = new JsonObject(); + for (WxMaTemplateData templateData : message.getData()) { + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("value", templateData.getValue()); + if (templateData.getColor() != null) { + dataJson.addProperty("color", templateData.getColor()); + } + data.add(templateData.getName(), dataJson); + } + msg.add("data", data); + } + + + messageJson.add("mp_template_msg", msg); + return messageJson; + } + + //小程序模版消息 + JsonObject msg = new JsonObject(); + msg.addProperty("template_id", message.getTemplateId()); + + if (message.getPage() != null) { + msg.addProperty("page", message.getPage()); + } + + if (message.getFormId() != null) { + msg.addProperty("form_id", message.getFormId()); + } + + JsonObject data = new JsonObject(); + msg.add("data", data); + + if (message.getData() != null) { + for (WxMaTemplateData templateData : message.getData()) { + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("value", templateData.getValue()); + data.add(templateData.getName(), dataJson); + } + } + + if (message.getEmphasisKeyword() != null) { + msg.addProperty("emphasis_keyword", message.getEmphasisKeyword()); + } + + messageJson.add("weapp_template_msg", msg); + + return messageJson; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUserPortraitGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUserPortraitGsonAdapter.java new file mode 100644 index 0000000000..b8a7c448ff --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUserPortraitGsonAdapter.java @@ -0,0 +1,67 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Charming + * @since 2018-04-28 + */ +public class WxMaUserPortraitGsonAdapter implements JsonDeserializer { + @Override + public WxMaUserPortrait deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + if (json == null) { + return null; + } + + WxMaUserPortrait portrait = new WxMaUserPortrait(); + JsonObject object = json.getAsJsonObject(); + String refDate = GsonHelper.getString(object, "ref_date"); + portrait.setRefDate(refDate); + portrait.setVisitUvNew(getPortraitItem(object.getAsJsonObject("visit_uv_new"))); + portrait.setVisitUv(getPortraitItem(object.getAsJsonObject("visit_uv"))); + return portrait; + } + + private WxMaUserPortrait.Item getPortraitItem(JsonObject object) { + if (object == null) { + return null; + } + WxMaUserPortrait.Item item = new WxMaUserPortrait.Item(); + item.setProvince(getAsMap(object, "province")); + item.setCity(getAsMap(object, "city")); + item.setGenders(getAsMap(object, "genders")); + item.setPlatforms(getAsMap(object, "platforms")); + item.setDevices(getAsMap(object, "devices")); + item.setAges(getAsMap(object, "ages")); + return item; + } + + private Map getAsMap(JsonObject object, String memberName) { + JsonArray array = object.getAsJsonArray(memberName); + if (array != null && array.size() > 0) { + Map map = new LinkedHashMap<>(array.size()); + for (JsonElement element : array) { + JsonObject elementObject = element.getAsJsonObject(); + String name = GsonHelper.getString(elementObject, "name"); + if (StringUtils.isNotBlank(name)) { + Long value = GsonHelper.getLong(elementObject, "value"); + map.put(name, value); + } + } + return map; + } + return null; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaVisitDistributionGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaVisitDistributionGsonAdapter.java new file mode 100644 index 0000000000..0fc79d44bd --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaVisitDistributionGsonAdapter.java @@ -0,0 +1,67 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitDistribution; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; + +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Charming + * @since 2018-04-28 + */ +public class WxMaVisitDistributionGsonAdapter implements JsonDeserializer { + @Override + public WxMaVisitDistribution deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + if (json == null) { + return null; + } + + WxMaVisitDistribution distribution = new WxMaVisitDistribution(); + JsonObject object = json.getAsJsonObject(); + String refDate = GsonHelper.getString(object, "ref_date"); + distribution.setRefDate(refDate); + + boolean hasList = object.has("list"); + if (!hasList) { + return distribution; + } + + JsonArray listArray = object.getAsJsonArray("list"); + Map> list = new ConcurrentHashMap<>(listArray.size()); + for (JsonElement indexElement : listArray) { + JsonObject indexObject = indexElement.getAsJsonObject(); + String index = GsonHelper.getString(indexObject, "index"); + if (index == null) { + continue; + } + + Map itemList = new LinkedHashMap<>(); + JsonArray itemArray = indexObject.getAsJsonArray("item_list"); + if (itemArray == null || itemArray.size() <= 0) { + list.put(index, itemList); + continue; + } + + for (JsonElement itemElement : itemArray) { + JsonObject itemObject = itemElement.getAsJsonObject(); + Integer key = GsonHelper.getInteger(itemObject, "key"); + Integer value = GsonHelper.getInteger(itemObject, "value"); + if (key != null) { + itemList.put(key, value); + } + } + list.put(index, itemList); + } + distribution.setList(list); + return distribution; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java new file mode 100644 index 0000000000..f0961d5edf --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java @@ -0,0 +1,95 @@ +package cn.binarywang.wx.miniapp.util.xml; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import cn.binarywang.wx.miniapp.message.WxMaXmlOutMessage; +import com.thoughtworks.xstream.XStream; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; + +/** + * @author Binary Wang + */ +public class XStreamTransformer { + private static final Map, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>(); + + static { + registerClass(WxMaMessage.class); + registerClass(WxMaXmlOutMessage.class); + } + + /** + * xml -> pojo. + */ + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, String xml) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); + return object; + } + + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, InputStream is) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); + return object; + } + + /** + * pojo -> xml. + */ + public static String toXml(Class clazz, T object) { + return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); + } + + /** + * 注册扩展消息的解析器. + * + * @param clz 类型 + * @param xStream xml解析器 + */ + public static void register(Class clz, XStream xStream) { + CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); + } + + /** + * 会自动注册该类及其子类. + * + * @param clz 要注册的类 + */ + private static void registerClass(Class clz) { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(clz); + xstream.processAnnotations(getInnerClasses(clz)); + if (clz.equals(WxMaMessage.class)) { + // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId + xstream.aliasField("MsgID", WxMaMessage.class, "msgId"); + } + + register(clz, xstream); + } + + private static Class[] getInnerClasses(Class clz) { + Class[] innerClasses = clz.getClasses(); + if (innerClasses == null) { + return null; + } + + List> result = new ArrayList<>(); + result.addAll(Arrays.asList(innerClasses)); + for (Class inner : innerClasses) { + Class[] innerClz = getInnerClasses(inner); + if (innerClz == null) { + continue; + } + + result.addAll(Arrays.asList(innerClz)); + } + + return result.toArray(new Class[0]); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaAnalysisServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaAnalysisServiceImplTest.java new file mode 100644 index 0000000000..9c1c2f00e5 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaAnalysisServiceImplTest.java @@ -0,0 +1,155 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaSummaryTrend; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitDistribution; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitPage; +import cn.binarywang.wx.miniapp.bean.analysis.WxMaVisitTrend; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import static org.testng.Assert.*; + +/** + * @author Charming + * @since 2018-04-28 + */ +@Guice(modules = ApiTestModule.class) +public class WxMaAnalysisServiceImplTest { + @Inject + private WxMaService wxMaService; + + @Test + public void testGetDailySummaryTrend() throws Exception { + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + Date twoDaysAgo = DateUtils.addDays(new Date(), -2); + List trends = service.getDailySummaryTrend(twoDaysAgo, twoDaysAgo); + assertEquals(1, trends.size()); + System.out.println(trends); + } + + @Test + public void testGetDailyVisitTrend() throws Exception { + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + Date twoDaysAgo = DateUtils.addDays(new Date(), -2); + List trends = service.getDailyVisitTrend(twoDaysAgo, twoDaysAgo); + assertEquals(1, trends.size()); + System.out.println(trends); + } + + @Test + public void testGetWeeklyVisitTrend() throws Exception { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + Date now = new Date(); + Date lastSunday = calendar.getTime(); + if (DateUtils.isSameDay(lastSunday, now)) { + lastSunday = DateUtils.addDays(lastSunday, -7); + } + Date lastMonday = DateUtils.addDays(lastSunday, -6); + + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + List trends = service.getWeeklyVisitTrend(lastMonday, lastSunday); + assertEquals(1, trends.size()); + System.out.println(trends); + } + + @Test + public void testGetMonthlyVisitTrend() throws Exception { + Date now = new Date(); + Date firstDayOfThisMonth = DateUtils.setDays(now, 1); + Date lastDayOfLastMonth = DateUtils.addDays(firstDayOfThisMonth, -1); + Date firstDayOfLastMonth = DateUtils.addMonths(firstDayOfThisMonth, -1); + + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + List trends = service.getMonthlyVisitTrend(firstDayOfLastMonth, lastDayOfLastMonth); + assertEquals(1, trends.size()); + System.out.println(trends); + } + + @Test + public void testGetVisitDistribution() throws Exception { + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + Date twoDaysAgo = DateUtils.addDays(new Date(), -2); + WxMaVisitDistribution distribution = service.getVisitDistribution(twoDaysAgo, twoDaysAgo); + assertNotNull(distribution); + String date = DateFormatUtils.format(twoDaysAgo, "yyyyMMdd"); + assertEquals(date, distribution.getRefDate()); + assertTrue(distribution.getList().containsKey("access_source_session_cnt")); + assertTrue(distribution.getList().containsKey("access_staytime_info")); + assertTrue(distribution.getList().containsKey("access_depth_info")); + System.out.println(distribution); + } + + @Test + public void testGetDailyRetainInfo() throws Exception { + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + Date twoDaysAgo = DateUtils.addDays(new Date(), -2); + WxMaRetainInfo retainInfo = service.getDailyRetainInfo(twoDaysAgo, twoDaysAgo); + assertNotNull(retainInfo); + System.out.println(retainInfo); + } + + @Test + public void testGetWeeklyRetainInfo() throws Exception { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + Date now = new Date(); + Date lastSunday = calendar.getTime(); + if (DateUtils.isSameDay(lastSunday, now)) { + lastSunday = DateUtils.addDays(lastSunday, -7); + } + Date lastMonday = DateUtils.addDays(lastSunday, -6); + + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + WxMaRetainInfo retainInfo = service.getWeeklyRetainInfo(lastMonday, lastSunday); + assertNotNull(retainInfo); + System.out.println(retainInfo); + } + + @Test + public void testGetMonthlyRetainInfo() throws Exception { + Date now = new Date(); + Date firstDayOfThisMonth = DateUtils.setDays(now, 1); + Date lastDayOfLastMonth = DateUtils.addDays(firstDayOfThisMonth, -1); + Date firstDayOfLastMonth = DateUtils.addMonths(firstDayOfThisMonth, -1); + + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + WxMaRetainInfo retainInfo = service.getMonthlyRetainInfo(firstDayOfLastMonth, lastDayOfLastMonth); + assertNotNull(retainInfo); + System.out.println(retainInfo); + } + + @Test + public void testGetVisitPage() throws Exception { + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + Date twoDaysAgo = DateUtils.addDays(new Date(), -2); + List visitPages = service.getVisitPage(twoDaysAgo, twoDaysAgo); + assertNotNull(visitPages); + System.out.println(visitPages); + System.out.println(visitPages.get(0).getPagePath()); + System.out.println(visitPages.get(0).getPageVisitPv()); + } + + @Test + public void testGetUserPortrait() throws Exception { + Date twoDaysAgo = DateUtils.addDays(new Date(), -2); + Date eightDaysAgo = DateUtils.addDays(new Date(), -8); + + final WxMaAnalysisService service = wxMaService.getAnalysisService(); + WxMaUserPortrait portrait = service.getUserPortrait(eightDaysAgo, twoDaysAgo); + assertNotNull(portrait); + System.out.println(portrait); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImplTest.java new file mode 100644 index 0000000000..69d6cc990e --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImplTest.java @@ -0,0 +1,397 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.cloud.*; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; +import com.google.gson.JsonArray; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.math.BigDecimal; +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2020-01-22 + */ +@Guice(modules = ApiTestModule.class) +public class WxMaCloudServiceImplTest { + + private static final String COLLECTION = "geo"; + @Inject + private WxMaService wxMaService; + + @BeforeTest + public void before() { + /** + * 用以解决:javax.net.ssl.SSLHandshakeException: PKIX path building failed + * 参考:https://www.cnblogs.com/cloudapps/p/5022544.html + */ + String mpCert = ClassLoader.getSystemResource("wx-mp-jssecacerts").getPath(); + String maCert = ClassLoader.getSystemResource("wx-ma-jssecacerts").getPath(); + System.setProperty("javax.net.ssl.trustStore", mpCert + "," + maCert); + String property = System.getProperty("javax.net.ssl.trustStore"); + System.out.println("javax.net.ssl.trustStore=" + property); + } + + + @Test + public void testInvokeCloudFunction() throws WxErrorException { + final String result = this.wxMaService.getCloudService().invokeCloudFunction("login", "{}"); + assertThat(result).isNotNull(); + } + + @Test + public void testAddList() throws WxErrorException { + List stuList = new ArrayList<>(); + + Map product1 = new HashMap<>(); + product1.put("description", "item1"); + product1.put("price", BigDecimal.valueOf(1.2)); + product1.put("due", new Date()); + + /** + * 等价于new db.Geo.Point(113, 23) + * 参见:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/geo/Geo.Point.html + */ + Map point = new HashMap<>(); + point.put("type", "Point"); + point.put("coordinates", new Integer[]{113, 23}); + + Map product2 = new HashMap<>(); + product2.put("tags", new String[]{"cloud", "database"}); + product2.put("location", point); + product2.put("done", false); + + stuList.add(product1); + stuList.add(product2); + List idList = this.wxMaService.getCloudService().add(COLLECTION, stuList); + + System.out.println(idList.size()); + assertThat(idList).isNotEmpty(); + } + + @Test + public void testAddObject() throws WxErrorException { + /** + * 等价于new db.Geo.Point(113, 23) + * 参见:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/geo/Geo.Point.html + */ + Map point = new HashMap<>(); + point.put("type", "Point"); + point.put("coordinates", new Integer[]{113, 23}); + + Map product = new HashMap<>(); + product.put("description", "item1"); + product.put("price", BigDecimal.valueOf(1.2)); + product.put("due", new Date()); + product.put("tags", new String[]{"cloud", "database"}); + product.put("location", point); + product.put("done", false); + + String id = this.wxMaService.getCloudService().add(COLLECTION, product); + + System.out.println(id); + assertThat(id).isNotBlank(); + } + + + @Test + public void testDatabaseAdd() throws WxErrorException { + JsonArray array = this.wxMaService.getCloudService().databaseAdd("db.collection(\"geo\").add({\n" + + " data: [{\n" + + " description: \"item1\",\n" + + " due:\n" + + " new Date(\"2019-09-09\"),\n" + + " tags: [\n" + + " \"cloud\",\n" + + " \"database\"\n" + + " ],\n" + + " location:\n" + + " new db.Geo.Point(113, 23),\n" + + " done:false\n" + + " },\n" + + " {\n" + + " description: \"item2\",\n" + + " due:\n" + + " new Date(\"2019-09-09\"),\n" + + " tags: [\n" + + " \"cloud\",\n" + + " \"database\"\n" + + " ],\n" + + " location:\n" + + " new db.Geo.Point(113, 23),\n" + + " done:false\n" + + " }\n" + + " ]\n" + + " })"); + + System.out.println(array); + assertThat(array).isNotEmpty(); + } + + @Test + public void testDelete() throws WxErrorException { + StringBuilder whereParamSb = new StringBuilder(); + whereParamSb.append("{") + // 等于 + .append("_id: _.eq('79a2c43f5e7e9e8e001a120e494d51b8'),") + // in + .append("age: _.in([0, 1, 2, 3]),") + // 小于 + .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),") + // 存在属性 + .append("price: _.exists(true)") + .append("}"); + + final int result = this.wxMaService.getCloudService().delete( + COLLECTION, whereParamSb.toString()); + assertThat(result).isEqualTo(0); + } + + @Test + public void testDatabaseDelete() throws WxErrorException { + final int result = this.wxMaService.getCloudService().databaseDelete( + "db.collection(\"geo\").doc(\"056120a7-c89e-4616-95bf-dfc9a11daa3b\").remove()"); + assertThat(result).isEqualTo(0); + } + + @Test + public void testUpdate() throws WxErrorException { + StringBuilder whereParamSb = new StringBuilder(); + whereParamSb.append("{") + // in + .append("age: _.in([0, 1, 2, 3]),") + // 小于 + .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),") + // 存在属性 + .append("price: _.exists(true)") + .append("}"); + + StringBuilder updateSb = new StringBuilder(); + updateSb.append("{age: _.inc(1)}"); + + final WxCloudDatabaseUpdateResult result = this.wxMaService.getCloudService().update(COLLECTION, + whereParamSb.toString(), updateSb.toString()); + + assertThat(result).isNotNull(); + assertThat(result.getMatched()).isGreaterThan(0); + assertThat(result.getId()).isEmpty(); + assertThat(result.getModified()).isGreaterThan(0); + } + + @Test + public void testDatabaseUpdate() throws WxErrorException { + final WxCloudDatabaseUpdateResult result = this.wxMaService.getCloudService().databaseUpdate( + "db.collection(\"geo\").where({description:\"item1\"}).update({data:{age: _.inc(1)}})"); + assertThat(result).isNotNull(); + assertThat(result.getMatched()).isGreaterThan(0); + assertThat(result.getId()).isEmpty(); + assertThat(result.getModified()).isGreaterThan(0); + } + + @Test + public void testQuery() throws WxErrorException { + StringBuilder whereParamSb = new StringBuilder(); + whereParamSb.append("{") + // in + .append("age: _.in([0, 1, 2, 3]),") + // 小于 + .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),") + // 存在属性 + .append("price: _.exists(true)") + .append("}"); + +// // Hutool创建有序map,返回LinkedHashMap +// Map map2 = MapUtil.newHashMap(true); +// map2.put("_id", "asc"); +// map2.put("price", "desc"); + + // 有序map + ImmutableSortedMap orderBy = new ImmutableSortedMap + .Builder(Ordering.natural()) + .put("_id", "asc") + .put("price", "desc") + .build(); + + final WxCloudDatabaseQueryResult result = this.wxMaService.getCloudService().query(COLLECTION, + whereParamSb.toString(), orderBy, 20, 20); + assertThat(result).isNotNull(); + assertThat(result.getPager()).isNotNull(); + assertThat(result.getPager().getLimit()).isEqualTo(10); + assertThat(result.getPager().getOffset()).isEqualTo(1); + assertThat(result.getPager().getTotal()).isGreaterThan(0); + assertThat(result.getData().length).isGreaterThan(0); + } + + @Test + public void testDatabaseQuery() throws WxErrorException { + final WxCloudDatabaseQueryResult result = this.wxMaService.getCloudService().databaseQuery( + "db.collection(\"geo\").where({done:false}).limit(10).skip(1).get()"); + assertThat(result).isNotNull(); + assertThat(result.getPager()).isNotNull(); + assertThat(result.getPager().getLimit()).isEqualTo(10); + assertThat(result.getPager().getOffset()).isEqualTo(1); + assertThat(result.getPager().getTotal()).isGreaterThan(0); + assertThat(result.getData().length).isGreaterThan(0); + } + + @Test + public void testDatabaseAggregate() throws WxErrorException { + final JsonArray result = this.wxMaService.getCloudService().databaseAggregate( + "db.collection(\"geo\").aggregate().match({tags:\"cloud\"}).limit(10).end()"); + assertThat(result).isNotNull(); + } + + @Test + public void testCount() throws WxErrorException { + StringBuilder whereParamSb = new StringBuilder(); + whereParamSb.append("{") + // in + .append("age: _.in([0, 1, 2, 3]),") + // 小于 + .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),") + // 存在属性 + .append("price: _.exists(true)") + .append("}"); + final Long result = this.wxMaService.getCloudService().count(COLLECTION, whereParamSb.toString()); + assertThat(result).isGreaterThan(0); + } + + @Test + public void testDatabaseCount() throws WxErrorException { + final Long result = this.wxMaService.getCloudService().databaseCount( + "db.collection(\"geo\").where({done:false}).count()"); + assertThat(result).isGreaterThan(0); + } + + @Test + public void testUpdateIndex() throws WxErrorException { + this.wxMaService.getCloudService().updateIndex(COLLECTION, + Lists.newArrayList(new WxCloudDatabaseCreateIndexRequest() + .setName("drop_index") + .setUnique(true) + .setKeys(Lists.newArrayList(new WxCloudDatabaseCreateIndexRequest.IndexKey().setDirection("2dsphere").setName("test")) + )), + Lists.newArrayList("add_index2")); + } + + @Test + public void testDatabaseMigrateImport() throws WxErrorException { + final Long result = this.wxMaService.getCloudService().databaseMigrateImport(COLLECTION, "test.json", + 1, true, 1); + assertThat(result).isGreaterThan(0); + } + + @Test + public void testDatabaseMigrateExport() throws WxErrorException { + final Long result = this.wxMaService.getCloudService().databaseMigrateExport("test.json", 1, + "const Point = db.Geo.Point;db.collection('geo').where({age: _.gt(1)}).limit(10).skip(1).orderBy('age','asc')" + + ".orderBy('name', 'desc')" + + ".field({ name: true }).get()"); + assertThat(result).isGreaterThan(0); + } + + @Test + public void testDatabaseMigrateQueryInfo() throws WxErrorException { + final WxCloudCloudDatabaseMigrateQueryInfoResult result = this.wxMaService.getCloudService() + .databaseMigrateQueryInfo(476629L); + assertThat(result).isNotNull(); + System.out.println(result.getFileUrl()); + } + + @Test + public void testUploadFile() throws WxErrorException { + final WxCloudUploadFileResult result = this.wxMaService.getCloudService().uploadFile("E:\\MyDocs\\Desktop" + + "\\test.json"); + + assertThat(result).isNotNull(); + assertThat(result.getAuthorization()).isNotNull(); + assertThat(result.getToken()).isNotNull(); + assertThat(result.getUrl()).isNotNull(); + assertThat(result.getFileId()).isNotNull(); + assertThat(result.getCosFileId()).isNotNull(); + + } + + @Test + public void testBatchDownloadFile() throws WxErrorException { + final WxCloudBatchDownloadFileResult result = this.wxMaService.getCloudService().batchDownloadFile( + new String[]{"cloud://rcn.7263-rcn-1258788140/Snipaste_2019-11-13_00-21-27.png", "cloud://rcn" + + ".7263-rcn-1258788140/avatar.jpg"}, + new long[]{7200, 6800}); + + assertThat(result).isNotNull(); + assertThat(result.getFileList()).isNotNull(); + assertThat(result.getFileList().size()).isGreaterThan(0); + assertThat(result.getFileList().get(0).getDownloadUrl()).isNotNull(); + assertThat(result.getFileList().get(0).getErrMsg()).isEqualTo("ok"); + assertThat(result.getFileList().get(0).getStatus()).isEqualTo(0); + assertThat(result.getFileList().get(0).getFileId()).isNotNull(); + + } + + @Test + public void testBatchDeleteFile() throws WxErrorException { + final WxCloudBatchDeleteFileResult result = this.wxMaService.getCloudService().batchDeleteFile( + new String[]{"cloud://rcn.7263-rcn-1258788140/test.json"}); + + assertThat(result).isNotNull(); + assertThat(result.getFileList()).isNotNull(); + assertThat(result.getFileList().size()).isGreaterThan(0); + assertThat(result.getFileList().get(0).getErrMsg()).isEqualTo("ok"); + assertThat(result.getFileList().get(0).getStatus()).isEqualTo(0); + assertThat(result.getFileList().get(0).getFileId()).isNotNull(); + } + + @Test + public void testGetQcloudToken() throws WxErrorException { + final WxCloudGetQcloudTokenResult result = this.wxMaService.getCloudService().getQcloudToken(1800); + + assertThat(result).isNotNull(); + assertThat(result.getSecretId()).isNotNull(); + assertThat(result.getSecretKey()).isNotNull(); + assertThat(result.getToken()).isNotNull(); + assertThat(result.getExpiredTime()).isNotNull(); + } + + @Test + public void testDatabaseCollectionAdd() throws WxErrorException { + this.wxMaService.getCloudService().databaseCollectionAdd("test_add_collection"); + } + + @Test + public void testDatabaseCollectionDelete() throws WxErrorException { + this.wxMaService.getCloudService().databaseCollectionAdd("test_delete_collection"); + this.wxMaService.getCloudService().databaseCollectionDelete("test_delete_collection"); + } + + @Test + public void testDatabaseCollectionGet() throws WxErrorException { + final WxCloudDatabaseCollectionGetResult result = this.wxMaService.getCloudService().databaseCollectionGet( + null, null); + + assertThat(result).isNotNull(); + assertThat(result.getPager()).isNotNull(); + assertThat(result.getPager().getLimit()).isEqualTo(10); + assertThat(result.getPager().getOffset()).isEqualTo(0); + assertThat(result.getPager().getTotal()).isGreaterThan(0); + + assertThat(result.getCollections().length).isGreaterThan(0); + assertThat(result.getCollections()[0].getCount()).isGreaterThan(0); + assertThat(result.getCollections()[0].getName()).isNotNull(); + assertThat(result.getCollections()[0].getSize()).isGreaterThan(0); + assertThat(result.getCollections()[0].getIndexCount()).isGreaterThan(0); + assertThat(result.getCollections()[0].getIndexSize()).isGreaterThan(0); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImplTest.java new file mode 100644 index 0000000000..0a4aca45e3 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImplTest.java @@ -0,0 +1,155 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.testng.annotations.*; + +import cn.binarywang.wx.miniapp.api.WxMaCodeService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeExtConfig; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest; +import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; + +import static org.testng.Assert.*; + +/** + * @author Charming + * @since 2018-04-26 20:18 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaCodeServiceImplTest { + @Inject + private WxMaService wxService; + @Inject + private WxMaConfig wxMaConfig; + + @Test + public void testGetCategory() throws Exception { + List categories = wxService.getCodeService().getCategory(); + System.out.println(String.valueOf(categories)); + } + + @Test + public void testCommit() throws Exception { + String themeColor = "#0074d9"; + String themeFontColor = "#ffffff"; + + Map ext = new HashMap<>(); + ext.put("appName", "xxx"); + ext.put("verified", true); + ext.put("navigationBarBackgroundColor", themeColor); + ext.put("navigationBarTextStyle", themeFontColor); + ext.put("companyId", 4128); + ext.put("companyFullName", "xxx有限公司"); + + WxMaCodeService wxMaCodeService = wxService.getCodeService(); + WxMaCodeCommitRequest commitRequest = WxMaCodeCommitRequest + .builder() + .templateId(6L) + .userVersion("v0.1.0") + .userDesc("init") + .extConfig(WxMaCodeExtConfig.builder() + .extAppid(wxMaConfig.getAppid()) + .extEnable(true) + .ext(ext) + .window( + WxMaCodeExtConfig.PageConfig + .builder() + .navigationBarBackgroundColor(themeColor) + .navigationBarTextStyle(themeFontColor) + .build() + ) + .build()) + .build(); + wxMaCodeService.commit(commitRequest); + } + + @Test + public void testGetQrCode() throws Exception { + byte[] qrCode = wxService.getCodeService().getQrCode(null); + assertTrue(qrCode.length > 0); + } + + @Test + public void testGetPage() throws Exception { + List pageList = wxService.getCodeService().getPage(); + System.out.println(String.valueOf(pageList)); + } + + @Test + public void testSubmitAudit() throws Exception { + WxMaCodeSubmitAuditRequest auditRequest = WxMaCodeSubmitAuditRequest + .builder() + .itemList(Arrays.asList( + WxMaCategory + .builder() + .address("pages/logs/logs") + .tag("工具 效率") + .firstClass("工具") + .firstId(287L) + .secondClass("效率") + .secondId(616L) + .title("日志") + .build() + )).build(); + long auditId = wxService.getCodeService().submitAudit(auditRequest); + assertTrue(auditId > 0); + // 421937937 + System.out.println(auditId); + } + + @Test + public void testGetAuditStatus() throws Exception { + WxMaCodeAuditStatus auditStatus = wxService.getCodeService().getAuditStatus(421937937L); + System.out.println(auditStatus); + assertNotNull(auditStatus); + } + + @Test + public void testGetLatestAuditStatus() throws Exception { + WxMaCodeAuditStatus auditStatus = wxService.getCodeService().getLatestAuditStatus(); + System.out.println(auditStatus); + assertNotNull(auditStatus); + } + + @Test + public void testRelease() throws Exception { + wxService.getCodeService().release(); + } + + @Test + public void testChangeVisitStatus() throws Exception { + wxService.getCodeService().changeVisitStatus("open"); + } + + @Test + public void testRevertCodeRelease() throws Exception { + wxService.getCodeService().revertCodeRelease(); + } + + @Test + public void testGetSupportVersion() throws Exception { + WxMaCodeVersionDistribution distribution = wxService.getCodeService().getSupportVersion(); + System.out.println(distribution); + } + + @Test + public void testSetSupportVersion() throws Exception { + wxService.getCodeService().setSupportVersion("1.2.0"); + } + + @Test + public void testUndoCodeAudit() throws Exception { + + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java new file mode 100644 index 0000000000..6991ad9c25 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java @@ -0,0 +1,231 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaExpressService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.express.*; +import cn.binarywang.wx.miniapp.bean.express.request.*; +import cn.binarywang.wx.miniapp.bean.express.result.WxMaExpressOrderInfoResult; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.apache.commons.lang3.StringUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; + + +@Guice(modules = ApiTestModule.class) +public class WxMaExpressServiceImplTest { + + @Inject + private WxMaService wxMaService; + + @Test + public void testGetAllDelivery() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + List list = service.getAllDelivery(); + System.out.println(WxMaGsonBuilder.create().toJson(list)); + } + + @Test + public void testGetAllAccount() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + List list = service.getAllAccount(); + System.out.println(WxMaGsonBuilder.create().toJson(list)); + } + + @Test + public void testBindAccount() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressBindAccountRequest request = WxMaExpressBindAccountRequest.builder() + .deliveryId("YUNDA") + .bizId("******") + .password("*********") + .remarkContent("####") + .type(WxMaConstants.BindAccountType.BIND) + .build(); + service.bindAccount(request); + } + + @Test + public void testGetQuota() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressBindAccountRequest request = WxMaExpressBindAccountRequest.builder() + .deliveryId("YUNDA") + .bizId("******") + .build(); + Integer quotaNum = service.getQuota(request); + System.out.println(quotaNum); + } + + + + @Test + public void testUpdatePrinter() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressPrinterUpdateRequest request = WxMaExpressPrinterUpdateRequest.builder() + .openid("*************") + .updateType(WxMaConstants.BindAccountType.UNBIND) + .build(); + service.updatePrinter(request); + } + + @Test + public void testGetPrinter() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressPrinter printer = service.getPrinter(); + System.out.println(WxMaGsonBuilder.create().toJson(printer)); + } + + @Test + public void testAddOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + + WxMaExpressOrderPerson sender = new WxMaExpressOrderPerson(); + sender.setName("张三"); + sender.setMobile("177****9809"); + sender.setProvince("北京市"); + sender.setCity("朝阳区"); + sender.setArea("朝阳区"); + sender.setAddress("西坝河****C-102"); + + WxMaExpressOrderPerson receiver = new WxMaExpressOrderPerson(); + receiver.setName("李四"); + receiver.setMobile("180****8772"); + receiver.setProvince("北京市"); + receiver.setCity("朝阳区"); + receiver.setArea("朝阳区"); + receiver.setAddress("西坝河北里****101"); + + + WxMaExpressOrderCargo cargo = new WxMaExpressOrderCargo(); + List detailList = new ArrayList<>(); + List shopNames = new ArrayList<>(); + Integer goodsCount = 0; + for (int i = 0; i < 4; i++) { + WxMaExpressOrderCargoDetail detail = new WxMaExpressOrderCargoDetail(); + String shopName = "商品_"+i; + detail.setName(shopName); + detail.setCount(1); + detailList.add(detail); + shopNames.add(shopName); + goodsCount ++; + } + cargo.setCount(detailList.size()); + cargo.setWeight(5); + cargo.setSpaceHeight(10); + cargo.setSpaceLength(10); + cargo.setSpaceWidth(10); + cargo.setDetailList(detailList); + + + WxMaExpressOrderShop shop = new WxMaExpressOrderShop(); + shop.setWxaPath("/index/index?from=waybill&id=01234567890123456789"); + shop.setGoodsName(StringUtils.join(shopNames,"&")); + shop.setGoodsCount(goodsCount); + shop.setImgUrl("https://mmbiz.qpic.cn/mmbiz_png/OiaFLUqewuIDNQnTiaCInIG8ibdosYHhQHPbXJUrqYSNIcBL60vo4LIjlcoNG1QPkeH5GWWEB41Ny895CokeAah8A/640"); + + WxMaExpressDelivery.ServiceType serviceType = new WxMaExpressDelivery.ServiceType(); + serviceType.setServiceName("test_service_name"); + serviceType.setServiceType(1); + + WxMaExpressAddOrderRequest request = WxMaExpressAddOrderRequest.builder() + .addSource(WxMaConstants.OrderAddSource.MINI_PROGRAM) + .orderId("test201911271429004") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .deliveryId("TEST") + .bizId("test_biz_id") + .customRemark("") + .expectTime(0L) + .sender(sender) + .receiver(receiver) + .cargo(cargo) + .shop(shop) + .insured(WxMaExpressOrderInsured.builder().build()) + .service(serviceType) + .build(); + + WxMaExpressOrderInfoResult result = service.addOrder(request); + System.out.println(WxMaGsonBuilder.create().toJson(result)); + } + + @Test + public void testBatchGetOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + List requests = new ArrayList<>(); + + List orderIds = new ArrayList<>(); + orderIds.add("test201911271429000"); + + List waybillIds = new ArrayList<>(); + waybillIds.add("test201911271429000_1574836404_waybill_id"); + for (int i = 0; i < orderIds.size(); i++) { + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId(orderIds.get(i)) + .deliveryId("TEST") + .waybillId(waybillIds.get(i)) + .build(); + requests.add(request); + } + + List results = service.batchGetOrder(requests); + System.out.println(WxMaGsonBuilder.create().toJson(results)); + } + + @Test + public void testCancelOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId("test201911271429000") + .deliveryId("TEST") + .waybillId("test201911271429000_1574836404_waybill_id") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .build(); + service.cancelOrder(request); + } + + + @Test + public void testGetOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId("test201911271429000") + .deliveryId("TEST") + .waybillId("test201911271429000_1574836404_waybill_id") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .build(); + WxMaExpressOrderInfoResult result = service.getOrder(request); + System.out.println(WxMaGsonBuilder.create().toJson(result)); + } + + + @Test + public void testGetPath() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId("test201911271429000") + .deliveryId("TEST") + .waybillId("test201911271429000_1574836404_waybill_id") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .build(); + WxMaExpressPath path = service.getPath(request); + System.out.println(WxMaGsonBuilder.create().toJson(path)); + } + + @Test + public void testTestUpdateOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressTestUpdateOrderRequest request = WxMaExpressTestUpdateOrderRequest.builder() + .orderId("test201911271429000") + .waybillId("test201911271429000_1574836404_waybill_id") + .actionTime(1574850455L) + .actionType(300002) + .actionMsg("开始派送") + .build(); + service.testUpdateOrder(request); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImplTest.java new file mode 100644 index 0000000000..07f4358db5 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImplTest.java @@ -0,0 +1,45 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *  Created by BinaryWang on 2018/8/5.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaJsapiServiceImplTest { + @Inject + private WxMaService wxService; + @Inject + private WxMaConfig wxMaConfig; + + @Test + public void testGetJsapiTicket() throws WxErrorException { + assertThat(this.wxService.getJsapiService().getJsapiTicket()).isNotBlank(); + } + + @Test + public void testGetJsapiTicket1() throws WxErrorException { + assertThat(this.wxService.getJsapiService().getJsapiTicket(true)).isNotBlank(); + } + + @Test + public void testCreateJsapiSignature() throws WxErrorException { + final WxJsapiSignature jsapiSignature = this.wxService.getJsapiService().createJsapiSignature("http://www.qq.com"); + System.out.println(jsapiSignature); + assertThat(jsapiSignature).isNotNull(); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImplTest.java new file mode 100644 index 0000000000..769d82919e --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImplTest.java @@ -0,0 +1,92 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; +import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.Arrays; + +import static org.testng.Assert.assertNotNull; + +/** + * 测试直播商品管理相关的接口 + * + * @author lipengjun (939961241@qq.com) + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaLiveGoodsServiceImplTest { + + @Inject + private WxMaService wxService; + + @Test + public void addGoods() throws Exception { + //上传临时素材 + WxMediaUploadResult mediaUpload = this.wxService.getMediaService().uploadMedia("image", new File("E:\\1.png")); + + WxMaLiveInfo.Goods goods = new WxMaLiveInfo.Goods(); + goods.setCoverImgUrl(mediaUpload.getMediaId()); + goods.setName("宫廷奢华真丝四件套"); + goods.setPrice("1599"); + goods.setPrice2("0"); + goods.setPriceType(1); + goods.setUrl("pages/goods/goods?id=b7c4fbf95493bd294054fe4296d0d9ad"); + WxMaLiveResult liveResult = this.wxService.getLiveGoodsService().addGoods(goods); + assertNotNull(liveResult); + System.out.println(liveResult.toString()); + } + + @Test + public void resetAudit() throws Exception { + boolean result = this.wxService.getLiveGoodsService().resetAudit(715138516, 9); + System.out.println(result); + } + + @Test + public void auditGoods() throws Exception { + String result = this.wxService.getLiveGoodsService().auditGoods(9); + System.out.println(result); + } + + @Test + public void deleteGoods() throws Exception { + boolean result = this.wxService.getLiveGoodsService().deleteGoods(9); + System.out.println(result); + } + + @Test + public void updateGoods() throws Exception { + + WxMaLiveInfo.Goods goods = new WxMaLiveInfo.Goods(); + goods.setGoodsId(8); + goods.setName("宫廷奢华真丝四件套"); + goods.setCoverImgUrl("http://mmbiz.qpic.cn/mmbiz_png/omYktZNGamuUQE0WPVfqdnLV61JDhluXOac7PiaoZeticFpcR7wvicC0aXUC2VXkl7r1gN0QSKosv2satn6oCFeiaQ/0"); + goods.setPrice("2299"); + goods.setPrice2("0"); + goods.setPriceType(1); + goods.setUrl("pages/goods/goods?id=b7c4fbf95493bd294054fe4296d0d9ad"); + boolean maLiveInfo = this.wxService.getLiveGoodsService().updateGoods(goods); + System.out.println(maLiveInfo); + } + + @Test + public void getGoodsWareHouse() throws Exception { + WxMaLiveResult liveResult = this.wxService.getLiveGoodsService().getGoodsWareHouse(Arrays.asList(1, 2)); + assertNotNull(liveResult); + System.out.println(liveResult.toString()); + } + + @Test + public void getApprovedGoods() throws Exception { + WxMaLiveResult liveResult = this.wxService.getLiveGoodsService().getApprovedGoods(0, 4, 2); + assertNotNull(liveResult); + System.out.println(liveResult.toString()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImplTest.java new file mode 100644 index 0000000000..e92913366a --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImplTest.java @@ -0,0 +1,83 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaLiveInfo; +import cn.binarywang.wx.miniapp.bean.WxMaLiveResult; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; + +import static org.testng.Assert.assertNotNull; + +/** + * 测试直播相关的接口 + * + * @author yjwang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaLiveServiceImplTest { + + @Inject + private WxMaService wxService; + + @Test + public void createRoom() throws Exception { + //上传临时素材 + WxMediaUploadResult mediaUpload = this.wxService.getMediaService().uploadMedia("image", new File("E:\\1.png")); + + WxMaLiveInfo.RoomInfo roomInfo = new WxMaLiveInfo.RoomInfo(); + roomInfo.setName("订阅通知直播间"); + roomInfo.setCoverImg(mediaUpload.getMediaId()); + Calendar c = Calendar.getInstance(); + c.set(2020, Calendar.SEPTEMBER, 10, 8, 0); + roomInfo.setStartTime(c.getTimeInMillis() / 1000); + c.set(2020, Calendar.SEPTEMBER, 10, 12, 0); + roomInfo.setEndTime(c.getTimeInMillis() / 1000); + roomInfo.setAnchorName("鹏军_专业小程序开发"); + roomInfo.setAnchorWechat("pengjun939961241"); + roomInfo.setShareImg(mediaUpload.getMediaId()); + roomInfo.setType(1); + roomInfo.setScreenType(1); + roomInfo.setCloseLike(0); + roomInfo.setCloseGoods(0); + roomInfo.setCloseComment(0); + Integer roomId = this.wxService.getLiveService().createRoom(roomInfo); + System.out.println(roomId); + } + + @Test + public void getLiveInfo() throws Exception { + WxMaLiveResult list = this.wxService.getLiveService().getLiveInfo(0, 10); + assertNotNull(list); + System.out.println(list.toString()); + } + + @Test + public void getLiveReplay() throws Exception { + // [12, 11, 10, 9, 8, 7, 6, 5, 3, 2] + WxMaLiveResult list = this.wxService.getLiveService().getLiveReplay(3, 0, 10); + assertNotNull(list); + System.out.println(list.toString()); + } + + @Test + public void getLiveinfos() throws Exception { + List list = this.wxService.getLiveService().getLiveInfos(); + assertNotNull(list); + System.out.println(list.toString()); + } + + @Test + public void addGoodsToRoom() throws Exception { + boolean result = this.wxService.getLiveService().addGoodsToRoom(5, Arrays.asList(8, 7, 5, 4, 10)); + System.out.println(result); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java new file mode 100644 index 0000000000..6c9420be0e --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java @@ -0,0 +1,52 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * 临时素材接口的测试 + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaMediaServiceImplTest { + @Inject + protected WxMaService wxService; + + private String mediaId; + + @Test + public void testUploadMedia() throws WxErrorException, IOException { + String mediaType = "image"; + String fileType = "png"; + String fileName = "tmp.png"; + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)) { + WxMediaUploadResult res = this.wxService.getMediaService().uploadMedia(mediaType, fileType, inputStream); + assertNotNull(res.getType()); + assertNotNull(res.getCreatedAt()); + assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null); + this.mediaId = res.getMediaId(); + System.out.println(res); + } + } + + @Test(dependsOnMethods = {"testUploadMedia"}) + public void testGetMedia() throws WxErrorException { + File file = this.wxService.getMediaService().getMedia(this.mediaId); + assertNotNull(file); + System.out.println(file.getAbsolutePath()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java new file mode 100644 index 0000000000..cf127970fd --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java @@ -0,0 +1,95 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.bean.*; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import org.testng.annotations.*; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import cn.binarywang.wx.miniapp.test.TestConfig; +import com.google.common.collect.Lists; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试消息相关接口 + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaMsgServiceImplTest { + + @Inject + private WxMaService wxService; + + @Test + public void testSendKefuMsg() throws WxErrorException { + TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); + WxMaKefuMessage message = WxMaKefuMessage.newTextBuilder() + .toUser(config.getOpenid()) + .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World") + .build(); + + this.wxService.getMsgService().sendKefuMsg(message); + } + + @Test + public void testSendSubscribeMsg() throws WxErrorException { + TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); + + WxMaSubscribeMessage message = new WxMaSubscribeMessage(); + message.setTemplateId(config.getTemplateId()); + message.setToUser(config.getOpenid()); + message.setLang(WxMaConstants.MiniprogramLang.ZH_CN); + message.setMiniprogramState(WxMaConstants.MiniprogramState.FORMAL); + message.addData(new WxMaSubscribeMessage.Data("thing1", "苹果到货啦")); + message.addData(new WxMaSubscribeMessage.Data("amount3", "¥5")); + message.addData(new WxMaSubscribeMessage.Data("thing5", "记得领取哦")); + this.wxService.getMsgService().sendSubscribeMsg(message); + } + + + @Test + public void testSendUniformMsg() throws WxErrorException { + TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); + WxMaUniformMessage message = WxMaUniformMessage.builder() + .isMpTemplateMsg(false) + .toUser(config.getOpenid()) + .page("page/page/index") + .templateId("TEMPLATE_ID") + .formId("FORMID") + .emphasisKeyword("keyword1.DATA") + .build(); + message.addData(new WxMaTemplateData("keyword1", "339208499")) + .addData(new WxMaTemplateData("keyword2", "2015年01月05日 12:30")) + .addData(new WxMaTemplateData("keyword3", "腾讯微信总部")) + .addData(new WxMaTemplateData("keyword4", "广州市海珠区新港中路397号")); + + this.wxService.getMsgService().sendUniformMsg(message); + } + + @Test + public void testCreateUpdatableMessageActivityId() throws WxErrorException { + final JsonObject jsonObject = this.wxService.getMsgService().createUpdatableMessageActivityId(); + assertThat(jsonObject).isNotNull(); + assertThat(jsonObject.get("activity_id")).isNotNull(); + System.out.println(jsonObject.get("activity_id")); + assertThat(jsonObject.get("expiration_time")).isNotNull(); + } + + @Test + public void testSetUpdatableMsg() throws WxErrorException { + this.wxService.getMsgService().setUpdatableMsg(new WxMaUpdatableMsg() + .setActivityId("1048_4f61uDloWPZl9pAs1dGx07vDiHKZ7FwJ0suohS1iMH5z8zhFktYk4nRqqBY~") + .setTargetState(1) + .setTemplateInfo(new WxMaUpdatableMsg.TemplateInfo() + .setParameterList(Lists.newArrayList(new WxMaUpdatableMsg.Parameter().setName("member_count").setValue("1"))))); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaOcrServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaOcrServiceImplTest.java new file mode 100644 index 0000000000..b52476fb92 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaOcrServiceImplTest.java @@ -0,0 +1,385 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import cn.binarywang.wx.miniapp.test.TestConstants; +import me.chanjar.weixin.common.bean.ocr.*; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Binary Wang + * @date 2020-07-05 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaOcrServiceImplTest { + @Inject + private WxMaService service; + + @Test + public void testIdCard() throws WxErrorException { + final WxOcrIdCardResult result = this.service.getOcrService().idCard( + "https://res.wx.qq.com/op_res/E_oqdHqP4ETOJsT46sQnXz1HbeHOpqDQTuhkYeaLaJTf-JKld7de3091dwxCQwa6"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testIdCard2() throws Exception { + InputStream inputStream = this.getImageStream("https://res.wx.qq.com/op_res/E_oqdHqP4ETOJsT46sQnXz1HbeHOpqDQTuhkYeaLaJTf-JKld7de3091dwxCQwa6"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrIdCardResult result = this.service.getOcrService().idCard(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBankCard() throws WxErrorException { + final WxOcrBankCardResult result = this.service.getOcrService().bankCard("https://res.wx.qq.com/op_res/eP7PObYbBJj-_19EbGBL4PWe_zQ1NwET5NXSugjEWc-4ayns4Q-HFJrp-AOog8ih"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBankCard2() throws Exception { + InputStream inputStream = this.getImageStream("https://res.wx.qq.com/op_res/eP7PObYbBJj-_19EbGBL4PWe_zQ1NwET5NXSugjEWc-4ayns4Q-HFJrp-AOog8ih"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrBankCardResult result = this.service.getOcrService().bankCard(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDriving() throws WxErrorException { + final WxOcrDrivingResult result = this.service.getOcrService().driving("https://res.wx.qq.com/op_res/T051P5uWvh9gSJ9j78tWib53WiNi2pHSSZhoO8wnY3Av-djpsA4kA9whbtt6_Tb6"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDriving2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrDrivingResult result = this.service.getOcrService().driving(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDrivingLicense() throws WxErrorException { + final WxOcrDrivingLicenseResult result = this.service.getOcrService().drivingLicense("https://res.wx.qq.com/op_res/kD4YXjYVAW1eaQqn9uTA0rrOFoZRvVINitNDSGo5gJ7SzTCezNq_ZDDmU1I08kGn"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDrivingLicense2() throws Exception { + InputStream inputStream = this.getImageStream("https://res.wx.qq.com/op_res/kD4YXjYVAW1eaQqn9uTA0rrOFoZRvVINitNDSGo5gJ7SzTCezNq_ZDDmU1I08kGn"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrDrivingLicenseResult result = this.service.getOcrService().drivingLicense(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBizLicense() throws WxErrorException { + final WxOcrBizLicenseResult result = this.service.getOcrService().bizLicense("https://res.wx.qq.com/op_res/apCy0YbnEdjYsa_cjW6x3FlpCc20uQ-2BYE7aXnFsrB-ALHZNgdKXhzIUcrRnDoL"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBizLicense2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrBizLicenseResult result = this.service.getOcrService().bizLicense(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testComm() throws WxErrorException { + final WxOcrCommResult result = this.service.getOcrService().comm("https://res.wx.qq.com/op_res/apCy0YbnEdjYsa_cjW6x3FlpCc20uQ-2BYE7aXnFsrB-ALHZNgdKXhzIUcrRnDoL"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testComm2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrCommResult result = this.service.getOcrService().comm(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + private InputStream getImageStream(String url) { + try { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setReadTimeout(5000); + connection.setConnectTimeout(5000); + connection.setRequestMethod("GET"); + if (HttpURLConnection.HTTP_OK == connection.getResponseCode()) { + return connection.getInputStream(); + } + } catch (IOException e) { + System.out.println("获取网络图片出现异常,图片路径为:" + url); + } + return null; + } + + public static class MockTest { + private final WxMaService wxService = mock(WxMaService.class); + + @Test + public void testIdCard() throws Exception { + String returnJson = "{\"type\":\"Back\",\"name\":\"张三\",\"id\":\"110101199909090099\",\"valid_date\":\"20110101-20210201\"}"; + + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxMaOcrServiceImpl wxMpOcrService = new WxMaOcrServiceImpl(wxService); + + final WxOcrIdCardResult result = wxMpOcrService.idCard("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBankCard() throws Exception { + String returnJson = "{\"number\":\"24234234345234\"}"; + + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxMaOcrServiceImpl ocrService = new WxMaOcrServiceImpl(wxService); + final WxOcrBankCardResult result = ocrService.bankCard("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDriving() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"plate_num\": \"粤xxxxx\", //车牌号码\n" + + " \"vehicle_type\": \"小型普通客车\", //车辆类型\n" + + " \"owner\": \"东莞市xxxxx机械厂\", //所有人\n" + + " \"addr\": \"广东省东莞市xxxxx号\", //住址\n" + + " \"use_character\": \"非营运\", //使用性质\n" + + " \"model\": \"江淮牌HFCxxxxxxx\", //品牌型号\n" + + " \"vin\": \"LJ166xxxxxxxx51\", //车辆识别代号\n" + + " \"engine_num\": \"J3xxxxx3\", //发动机号码\n" + + " \"register_date\": \"2018-07-06\", //注册日期\n" + + " \"issue_date\": \"2018-07-01\", //发证日期\n" + + " \"plate_num_b\": \"粤xxxxx\", //车牌号码\n" + + " \"record\": \"441xxxxxx3\", //号牌\n" + + " \"passengers_num\": \"7人\", //核定载人数\n" + + " \"total_quality\": \"2700kg\", //总质量\n" + + " \"prepare_quality\": \"1995kg\", //整备质量\n" + + " \"overall_size\": \"4582x1795x1458mm\", //外廓尺寸\n" + + " \"card_position_front\": {//卡片正面位置(检测到卡片正面才会返回)\n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 119, \n" + + " \"y\": 2925\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 1435, \n" + + " \"y\": 2887\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 1435, \n" + + " \"y\": 3793\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 119, \n" + + " \"y\": 3831\n" + + " }\n" + + " }\n" + + " }, \n" + + " \"card_position_back\": {//卡片反面位置(检测到卡片反面才会返回)\n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 1523, \n" + + " \"y\": 2849\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 2898, \n" + + " \"y\": 2887\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 2927, \n" + + " \"y\": 3831\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 1523, \n" + + " \"y\": 3831\n" + + " }\n" + + " }\n" + + " }, \n" + + " \"img_size\": {//图片大小\n" + + " \"w\": 3120, \n" + + " \"h\": 4208\n" + + " }\n" + + "}"; + + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxMaOcrServiceImpl ocrService = new WxMaOcrServiceImpl(wxService); + + final WxOcrDrivingResult result = ocrService.driving("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDrivingLicense() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"id_num\": \"660601xxxxxxxx1234\", //证号\n" + + " \"name\": \"张三\", //姓名\n" + + " \"sex\": \"男\", //性别\n" + + " \"nationality\": \"中国\", //国籍\n" + + " \"address\": \"广东省东莞市xxxxx号\", //住址\n" + + " \"birth_date\": \"1990-12-21\", //出生日期\n" + + " \"issue_date\": \"2012-12-21\", //初次领证日期\n" + + " \"car_class\": \"C1\", //准驾车型\n" + + " \"valid_from\": \"2018-07-06\", //有效期限起始日\n" + + " \"valid_to\": \"2020-07-01\", //有效期限终止日\n" + + " \"official_seal\": \"xx市公安局公安交通管理局\" //印章文字\n" + + "}"; + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxMaOcrServiceImpl wxMpOcrService = new WxMaOcrServiceImpl(wxService); + + final WxOcrDrivingLicenseResult result = wxMpOcrService.drivingLicense("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBizLicense() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"reg_num\": \"123123\",//注册号\n" + + " \"serial\": \"123123\",//编号\n" + + " \"legal_representative\": \"张三\", //法定代表人姓名\n" + + " \"enterprise_name\": \"XX饮食店\", //企业名称\n" + + " \"type_of_organization\": \"个人经营\", //组成形式\n" + + " \"address\": \"XX市XX区XX路XX号\", //经营场所/企业住所\n" + + " \"type_of_enterprise\": \"xxx\", //公司类型\n" + + " \"business_scope\": \"中型餐馆(不含凉菜、不含裱花蛋糕,不含生食海产品)。\", //经营范围\n" + + " \"registered_capital\": \"200万\", //注册资本\n" + + " \"paid_in_capital\": \"200万\", //实收资本\n" + + " \"valid_period\": \"2019年1月1日\", //营业期限\n" + + " \"registered_date\": \"2018年1月1日\", //注册日期/成立日期\n" + + " \"cert_position\": { //营业执照位置\n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 155, \n" + + " \"y\": 191\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 725, \n" + + " \"y\": 157\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 743, \n" + + " \"y\": 512\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 164, \n" + + " \"y\": 525\n" + + " }\n" + + " }\n" + + " }, \n" + + " \"img_size\": { //图片大小\n" + + " \"w\": 966, \n" + + " \"h\": 728\n" + + " }\n" + + "}"; + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxMaOcrServiceImpl ocrService = new WxMaOcrServiceImpl(wxService); + + final WxOcrBizLicenseResult result = ocrService.bizLicense("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testComm() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"items\": [ //识别结果\n" + + " {\n" + + " \"text\": \"腾讯\", \n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 575, \n" + + " \"y\": 519\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 744, \n" + + " \"y\": 519\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 744, \n" + + " \"y\": 532\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 573, \n" + + " \"y\": 532\n" + + " }\n" + + " }\n" + + " }, \n" + + " {\n" + + " \"text\": \"微信团队\", \n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 670, \n" + + " \"y\": 516\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 762, \n" + + " \"y\": 517\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 762, \n" + + " \"y\": 532\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 670, \n" + + " \"y\": 531\n" + + " }\n" + + " }\n" + + " }\n" + + " ], \n" + + " \"img_size\": { //图片大小\n" + + " \"w\": 1280, \n" + + " \"h\": 720\n" + + " }\n" + + "}"; + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxMaOcrServiceImpl ocrService = new WxMaOcrServiceImpl(wxService); + + final WxOcrCommResult result = ocrService.comm("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPluginServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPluginServiceImplTest.java new file mode 100644 index 0000000000..ef5882e8f5 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPluginServiceImplTest.java @@ -0,0 +1,39 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaPluginListResult; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; + +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaPluginServiceImplTest { + @Inject + private WxMaService wxService; + + @Test + public void testApplyPlugin() throws Exception { + this.wxService.getPluginService().applyPlugin("wx4418e3e031e551be", null); + } + + @Test + public void testGetPluginList() throws Exception { + WxMaPluginListResult result = this.wxService.getPluginService().getPluginList(); + assertNotNull(result); + System.out.println(result.toString()); + } + + @Test + public void testUnbindPlugin() throws Exception { + this.wxService.getPluginService().unbindPlugin("wx4418e3e031e551be"); + } + + @Test + public void testUpdatePlugin() throws Exception { + this.wxService.getPluginService().updatePlugin("wx4418e3e031e551be", "2.0.2"); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java new file mode 100644 index 0000000000..e73fec4270 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java @@ -0,0 +1,58 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import java.io.File; + +import org.testng.annotations.*; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaQrcodeServiceImplTest { + @Inject + private WxMaService wxService; + + @Test + public void testCreateQrcode() throws Exception { + final File qrCode = this.wxService.getQrcodeService().createQrcode("111", 122); + assertThat(qrCode).isNotNull(); + } + + @Test + public void testCreateWxaCode() throws Exception { + final File wxCode = this.wxService.getQrcodeService().createWxaCode("111", 122); + assertThat(wxCode).isNotNull(); + } + + @Test + public void testCreateWxaCodeUnlimit() throws Exception { + final File wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimit("111", null); + assertThat(wxCode).isNotNull(); + } + + @Test + public void testCreateQrcodeBytes() throws WxErrorException { + final byte[] qrCode = this.wxService.getQrcodeService().createQrcodeBytes("111", 122); + assertThat(qrCode).isNotNull(); + } + + @Test + public void testCreateWxaCodeBytes() throws WxErrorException { + final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeBytes("111", 122, true, null, false); + assertThat(wxCode).isNotNull(); + } + + @Test + public void testCreateWxaCodeUnlimitBytes() throws WxErrorException { + final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimitBytes("111", null, 122, true, null, false); + assertThat(wxCode).isNotNull(); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImplTest.java new file mode 100644 index 0000000000..19bca1ca4f --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImplTest.java @@ -0,0 +1,52 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import java.io.File; + +import org.testng.annotations.*; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.*; + +/** + *
    + *
    + * Created by Binary Wang on 2018/11/24.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaSecCheckServiceImplTest { + @Inject + private WxMaService wxService; + + @Test + public void testCheckImage() throws WxErrorException { + boolean result = this.wxService.getSecCheckService() + .checkImage(new File(ClassLoader.getSystemResource("tmp.png").getFile())); + assertTrue(result); + } + + @DataProvider + public Object[][] secData() { + return new Object[][]{ + {"特3456书yuuo莞6543李zxcz蒜7782法fgnv级", false}, + {"完2347全dfji试3726测asad感3847知qwez到", false}, + {"提现&下载&棋牌游戏&网页", false}, + {"hello world!", true} + }; + } + + @Test(dataProvider = "secData") + public void testCheckMessage(String msg, boolean result) throws WxErrorException { + assertThat(this.wxService.getSecCheckService() + .checkMessage(msg)) + .isEqualTo(result); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java new file mode 100644 index 0000000000..4e0a886f74 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java @@ -0,0 +1,183 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.apache.commons.lang3.StringUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertTrue; + +/** + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaServiceImplTest { + + @Inject + private WxMaService wxService; + + public void testRefreshAccessToken() throws WxErrorException { + WxMaConfig configStorage = this.wxService.getWxMaConfig(); + String before = configStorage.getAccessToken(); + this.wxService.getAccessToken(false); + + String after = configStorage.getAccessToken(); + assertNotEquals(before, after); + assertTrue(StringUtils.isNotBlank(after)); + } + + @Test(expectedExceptions = {WxErrorException.class}) + public void testGetPaidUnionId() throws WxErrorException { + final String unionId = this.wxService.getPaidUnionId("1", null, "3", "4"); + assertThat(unionId).isNotEmpty(); + } + + @Test + public void testPost() throws WxErrorException { + final String postResult = this.wxService.post("https://api.weixin.qq.com/wxa/setdynamicdata", "{\n" + + " \"data\": \"{\\\"items\\\": [{\\\"from\\\":{\\\"city_name_cn\\\":\\\"广州市\\\"},\\\"to\\\":{\\\"city_name_cn\\\":\\\"北京市\\\"}}], \\\"attribute\\\": {\\\"count\\\": 1, \\\"totalcount\\\": 100, \\\"id\\\": \\\"1\\\", \\\"seq\\\": 0}}\",\n" + + " \"lifespan\": 86400,\n" + + " \"query\": \"{\\\"type\\\":100005}\",\n" + + " \"scene\": 1\n" + + "}"); + + System.out.println(postResult); + } + + @Test + public void testGetRequestHttpClient() { + } + + @Test + public void testGetRequestHttpProxy() { + } + + @Test + public void testGetRequestType() { + } + + @Test + public void testInitHttp() { + } + + @Test + public void testGetRequestHttp() { + } + + @Test + public void testGetAccessToken() { + } + + @Test + public void testJsCode2SessionInfo() { + } + + @Test + public void testSetDynamicData() throws WxErrorException { + this.wxService.setDynamicData(86400, "1000001", 1, + "{\"items\":[{\"stock_name\":\"腾讯控股\", \"stock_code\":\"00700\", \"stock_market\":\"hk\"}], \"attribute\":{\"count\":2, \"totalcount\": 100, \"id\": \"XXX\", \"seq\": 0}}"); + } + + @Test + public void testCheckSignature() { + } + + @Test + public void testTestGetAccessToken() { + } + + @Test + public void testGet() { + } + + @Test + public void testExecute() { + } + + @Test + public void testGetWxMaConfig() { + } + + @Test + public void testSetWxMaConfig() { + } + + @Test + public void testSetRetrySleepMillis() { + } + + @Test + public void testSetMaxRetryTimes() { + } + + @Test + public void testGetMsgService() { + } + + @Test + public void testGetMediaService() { + } + + @Test + public void testGetUserService() { + } + + @Test + public void testGetQrcodeService() { + } + + @Test + public void testGetTemplateService() { + } + + @Test + public void testGetSubscribeService() { + } + + @Test + public void testGetAnalysisService() { + } + + @Test + public void testGetCodeService() { + } + + @Test + public void testGetJsapiService() { + } + + @Test + public void testGetSettingService() { + } + + @Test + public void testGetShareService() { + } + + @Test + public void testGetRunService() { + } + + @Test + public void testGetSecCheckService() { + } + + @Test + public void testGetPluginService() { + } + + @Test + public void testGetExpressService() { + } + + @Test + public void testGetCloudService() { + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSettingServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSettingServiceImplTest.java new file mode 100644 index 0000000000..8da5f19208 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSettingServiceImplTest.java @@ -0,0 +1,54 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaDomainAction; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; + +/** + * @author Charming + * @since 2018-04-27 15:38 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaSettingServiceImplTest { + @Inject + private WxMaService wxService; + + @Test + public void testModifyDomain() throws Exception { + WxMaDomainAction domainAction = wxService.getSettingService().modifyDomain(WxMaDomainAction + .builder() + .action("get") + .build()); + System.out.println(domainAction); + assertNotNull(domainAction); + + domainAction.setAction("set"); + WxMaDomainAction result = wxService.getSettingService().modifyDomain(domainAction); + System.out.println(result); + } + + @Test + public void testBindTester() throws Exception { + wxService.getSettingService().bindTester("WeChatId"); + } + + @Test + public void testUnbindTester() throws Exception { + wxService.getSettingService().unbindTester("WeChatId"); + } + + @Test + public void testSetWebViewDomain() throws Exception { + WxMaDomainAction domainAction = wxService.getSettingService().setWebViewDomain(WxMaDomainAction + .builder() + .action("get") + .build()); + System.out.println(domainAction); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaShareServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaShareServiceImplTest.java new file mode 100644 index 0000000000..dcf3726e33 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaShareServiceImplTest.java @@ -0,0 +1,40 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaShareInfo; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; + +/** + * 测试分享相关的接口 + * + * @author zhfish + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaShareServiceImplTest { + + @Inject + private WxMaService wxService; + + @Test + public void testGetSessionKey() throws Exception { + assertNotNull(this.wxService.getUserService().getSessionInfo("aaa")); + } + + /** + * TODO 测试数据有问题,需要替换为正确的数据 + */ + @Test + public void testGetShareInfo() { + WxMaShareInfo shareInfo = this.wxService.getShareService().getShareInfo("tiihtNczf5v6AKRyjwEUhQ==", + "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==", + "r7BXXKkLb8qrSNn05n0qiA=="); + assertNotNull(shareInfo); + System.out.println(shareInfo.toString()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java new file mode 100644 index 0000000000..e40cf10e71 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java @@ -0,0 +1,65 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; +import cn.binarywang.wx.miniapp.bean.template.WxMaPubTemplateTitleListResult; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2019-12-15 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaSubscribeServiceImplTest { + @Inject + protected WxMaService wxService; + + @Test + public void testGetPubTemplateTitleList() throws WxErrorException { + WxMaPubTemplateTitleListResult result = this.wxService.getSubscribeService().getPubTemplateTitleList(new String[]{"2", "616"}, 0, 30); + System.out.println(result); + + } + + @Test + public void testGetPubTemplateKeyWordsById() throws WxErrorException { + final List result = this.wxService.getSubscribeService().getPubTemplateKeyWordsById("99"); + System.out.println(result); + } + + @Test + public void testAddTemplate() throws WxErrorException { + final String templateId = this.wxService.getSubscribeService().addTemplate("401", Lists.newArrayList(1, 2), "测试数据"); + System.out.println(templateId); + } + + @Test + public void testGetTemplateList() throws WxErrorException { + final List templateList = this.wxService.getSubscribeService().getTemplateList(); + System.out.println(templateList); + } + + @Test + public void testDelTemplate() throws WxErrorException { + this.wxService.getSubscribeService().delTemplate("priTmplId"); + } + + @Test + public void testGetCategory() throws WxErrorException { + final List categoryData = this.wxService.getSubscribeService().getCategory(); + assertThat(categoryData).isNotNull(); + System.out.println(categoryData); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java new file mode 100644 index 0000000000..fe3fa1675c --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java @@ -0,0 +1,74 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import cn.binarywang.wx.miniapp.test.TestConfig; +import com.google.common.collect.ImmutableMap; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * 测试用户相关的接口 + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaUserServiceImplTest { + + @Inject + private WxMaService wxService; + + @Test + public void testGetSessionKey() throws Exception { + assertNotNull(this.wxService.getUserService().getSessionInfo("aaa")); + } + + @Test + public void testGetUserInfo() { + WxMaUserInfo userInfo = this.wxService.getUserService().getUserInfo("tiihtNczf5v6AKRyjwEUhQ==", + "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==", + "r7BXXKkLb8qrSNn05n0qiA=="); + assertNotNull(userInfo); + System.out.println(userInfo.toString()); + } + + @Test + public void testCheckUserInfo() { + assertTrue(this.wxService.getUserService().checkUserInfo("HyVFkGl5F5OQWJZZaNzBBg==", + "{\"nickName\":\"Band\",\"gender\":1,\"language\":\"zh_CN\",\"city\":\"Guangzhou\",\"province\":\"Guangdong\",\"country\":\"CN\",\"avatarUrl\":\"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0\"}", + "75e81ceda165f4ffa64f4068af58c64b8f54b88c")); + } + + /** + * TODO 测试数据有问题,需要替换为正确的数据 + */ + @Test + public void testGetPhoneNoInfo() { + WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNoInfo("tiihtNczf5v6AKRyjwEUhQ==", + "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==", + "r7BXXKkLb8qrSNn05n0qiA=="); + assertNotNull(phoneNoInfo); + System.out.println(phoneNoInfo.toString()); + } + + @Test + public void testGetSessionInfo() { + } + + /** + * TODO 测试数据有问题,需要替换为正确的数据 + */ + @Test + public void testSetUserStorage() throws WxErrorException { + this.wxService.getUserService().setUserStorage(ImmutableMap.of("1","2"), + "r7BXXKkLb8qrSNn05n0qiA",((TestConfig)this.wxService.getWxMaConfig()).getOpenid()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java new file mode 100644 index 0000000000..6486c3237f --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java @@ -0,0 +1,69 @@ +package cn.binarywang.wx.miniapp.bean; + +import org.testng.annotations.*; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Binary Wang + */ +@Test +public class WxMaKefuMessageTest { + + public void testTextBuilder() { + WxMaKefuMessage reply = WxMaKefuMessage.newTextBuilder() + .toUser("OPENID") + .content("sfsfdsdf") + .build(); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + } + + public void testImageBuilder() { + WxMaKefuMessage reply = WxMaKefuMessage.newImageBuilder() + .toUser("OPENID") + . mediaId("MEDIA_ID") + .build(); + assertThat(reply.toJson()) + .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + } + + public void testLinkBuilder() { + WxMaKefuMessage reply = WxMaKefuMessage.newLinkBuilder() + .toUser("OPENID") + .url("url") + .description("description") + .title("title") + .thumbUrl("thumbUrl") + .build(); + assertThat(reply.toJson()) + .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"link\"," + + "\"link\":{\"title\":\"title\",\"description\":\"description\",\"url\":\"url\",\"thumb_url\":\"thumbUrl\"}}"); + } + + public void testMaPageBuilder() { + WxMaKefuMessage reply = WxMaKefuMessage.newMaPageBuilder() + .toUser("OPENID") + .title("title") + .pagePath("pagePath") + .thumbMediaId("thumbMediaId") + .build(); + assertThat(reply.toJson()) + .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"miniprogrampage\"," + + "\"miniprogrampage\":{\"title\":\"title\",\"pagepath\":\"pagePath\",\"thumb_media_id\":\"thumbMediaId\"}}"); + } + + public void testURLEscaped() { + WxMaKefuMessage reply = WxMaKefuMessage.newLinkBuilder() + .toUser("OPENID") + .url("https://mp.weixin.qq.com/s?__biz=MzI0MDA2OTY5NQ==") + .description("description") + .title("title") + .thumbUrl("thumbUrl") + .build(); + assertThat(reply.toJson()) + .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"link\"," + + "\"link\":{\"title\":\"title\",\"description\":\"description\",\"url\":\"https://mp.weixin.qq.com/s?__biz=MzI0MDA2OTY5NQ==\",\"thumb_url\":\"thumbUrl\"}}"); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java new file mode 100644 index 0000000000..26855b36ef --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java @@ -0,0 +1,50 @@ +package cn.binarywang.wx.miniapp.bean; + +import me.chanjar.weixin.common.api.WxConsts; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * @author Binary Wang + */ +@Test +public class WxMaMessageTest { + + public void testFromXml() { + String xml = "\n" + + " \n" + + " \n" + + " 1482048670\n" + + " \n" + + " \n" + + " 1234567890123456\n" + + " \n" + + " \n" + + " <![CDATA[Title]]>\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + WxMaMessage wxMessage = WxMaMessage.fromXml(xml); + assertEquals(wxMessage.getToUser(), "toUser"); + assertEquals(wxMessage.getFromUser(), "fromUser"); + assertEquals(wxMessage.getCreateTime(),new Integer(1482048670)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getTitle(), "Title"); + assertEquals(wxMessage.getPagePath(), "PagePath"); + assertEquals(wxMessage.getThumbUrl(), "ThumbUrl"); + assertEquals(wxMessage.getThumbMediaId(), "ThumbMediaId"); + assertEquals(wxMessage.getAppId(), "AppId"); + assertEquals(wxMessage.getEvent(), "user_enter_tempsession"); + assertEquals(wxMessage.getSessionFrom(), "sessionFrom"); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaRunStepInfoTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaRunStepInfoTest.java new file mode 100644 index 0000000000..bbc87394d2 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaRunStepInfoTest.java @@ -0,0 +1,42 @@ +package cn.binarywang.wx.miniapp.bean; + +import java.util.List; + +import org.testng.annotations.*; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/11/4.
    + * 
    + * + * @author Binary Wang + */ +public class WxMaRunStepInfoTest { + + @Test + public void testFromJson() { + // 数据来源:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/werun/wx.getWeRunData.html + String json = "{\n" + + " \"stepInfoList\": [\n" + + " {\n" + + " \"timestamp\": 1445866601,\n" + + " \"step\": 100\n" + + " },\n" + + " {\n" + + " \"timestamp\": 1445876601,\n" + + " \"step\": 120\n" + + " }\n" + + " ]\n" + + "}"; + + final List stepInfoList = WxMaRunStepInfo.fromJson(json); + assertThat(stepInfoList).isNotEmpty(); + assertThat(stepInfoList.get(0).getStep()).isEqualTo(100); + assertThat(stepInfoList.get(0).getTimestamp()).isEqualTo(1445866601); + assertThat(stepInfoList.get(1).getStep()).isEqualTo(120); + assertThat(stepInfoList.get(1).getTimestamp()).isEqualTo(1445876601); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaRetainInfoTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaRetainInfoTest.java new file mode 100644 index 0000000000..c2fd925e09 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaRetainInfoTest.java @@ -0,0 +1,22 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +/** + * @author Charming + * @since 2018-04-28 + */ +public class WxMaRetainInfoTest { + @Test + public void testFromJson() throws Exception { + String json = "{\"ref_date\":\"20170313\",\"visit_uv_new\":[{\"key\":0,\"value\":5464}],\"visit_uv\":[{\"key\":0,\"value\":55500}]}\n"; + WxMaRetainInfo retainInfo = WxMaRetainInfo.fromJson(json); + assertNotNull(retainInfo); + assertEquals("20170313", retainInfo.getRefDate()); + assertTrue(retainInfo.getVisitUv().containsKey(0)); + assertTrue(retainInfo.getVisitUvNew().containsKey(0)); + System.out.println(retainInfo); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaUserPortraitTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaUserPortraitTest.java new file mode 100644 index 0000000000..87239bb599 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaUserPortraitTest.java @@ -0,0 +1,19 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; + +/** + * @author Charming + * @since 2018-04-28 + */ +public class WxMaUserPortraitTest { + @Test + public void testFromJson() throws Exception { + String json = "{\"ref_date\":\"20170611\",\"visit_uv_new\":{\"province\":[{\"id\":31,\"name\":\"广东省\",\"value\":215}],\"city\":[{\"id\":3102,\"name\":\"广州\",\"value\":78}],\"genders\":[{\"id\":1,\"name\":\"男\",\"value\":2146}],\"platforms\":[{\"id\":1,\"name\":\"iPhone\",\"value\":27642}],\"devices\":[{\"name\":\"OPPO R9\",\"value\":61}],\"ages\":[{\"id\":1,\"name\":\"17岁以下\",\"value\":151}]},\"visit_uv\":{\"province\":[{\"id\":31,\"name\":\"广东省\",\"value\":1341}],\"city\":[{\"id\":3102,\"name\":\"广州\",\"value\":234}],\"genders\":[{\"id\":1,\"name\":\"男\",\"value\":14534}],\"platforms\":[{\"id\":1,\"name\":\"iPhone\",\"value\":21750}],\"devices\":[{\"name\":\"OPPO R9\",\"value\":617}],\"ages\":[{\"id\":1,\"name\":\"17岁以下\",\"value\":3156}]}}\n"; + WxMaUserPortrait portrait = WxMaUserPortrait.fromJson(json); + System.out.println(portrait); + assertNotNull(portrait); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitDistributionTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitDistributionTest.java new file mode 100644 index 0000000000..2b01b3aad7 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/analysis/WxMaVisitDistributionTest.java @@ -0,0 +1,23 @@ +package cn.binarywang.wx.miniapp.bean.analysis; + +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +/** + * @author Charming + * @since 2018-04-28 + */ +public class WxMaVisitDistributionTest { + @Test + public void testFromJson() throws Exception { + String json = "{\"ref_date\":\"20170313\",\"list\":[{\"index\":\"access_source_session_cnt\",\"item_list\":[{\"key\":10,\"value\":5},{\"key\":8,\"value\":687},{\"key\":7,\"value\":10740},{\"key\":6,\"value\":1961},{\"key\":5,\"value\":677},{\"key\":4,\"value\":653},{\"key\":3,\"value\":1120},{\"key\":2,\"value\":10243},{\"key\":1,\"value\":116578}]},{\"index\":\"access_staytime_info\",\"item_list\":[{\"key\":8,\"value\":16329},{\"key\":7,\"value\":19322},{\"key\":6,\"value\":21832},{\"key\":5,\"value\":19539},{\"key\":4,\"value\":29670},{\"key\":3,\"value\":19667},{\"key\":2,\"value\":11794},{\"key\":1,\"value\":4511}]},{\"index\":\"access_depth_info\",\"item_list\":[{\"key\":5,\"value\":217},{\"key\":4,\"value\":3259},{\"key\":3,\"value\":32445},{\"key\":2,\"value\":63542},{\"key\":1,\"value\":43201}]}]}\n"; + WxMaVisitDistribution distribution = WxMaVisitDistribution.fromJson(json); + assertNotNull(distribution); + assertEquals("20170313", distribution.getRefDate()); + assertTrue(distribution.getList().containsKey("access_source_session_cnt")); + assertTrue(distribution.getList().containsKey("access_staytime_info")); + assertTrue(distribution.getList().containsKey("access_depth_info")); + System.out.println(distribution); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequestTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequestTest.java new file mode 100644 index 0000000000..36cf1c4840 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequestTest.java @@ -0,0 +1,25 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * @author Charming + * @since 2018-04-26 19:54 + */ +public class WxMaCodeCommitRequestTest { + @Test + public void testToJson() { + WxMaCodeCommitRequest commitRequest = WxMaCodeCommitRequest.builder() + .templateId(1L) + .userVersion("v0.1.0") + .userDesc("init") + .extConfig(WxMaCodeExtConfig.builder() + .extAppid("app123") + .extEnable(true) + .build()) + .build(); + assertEquals(commitRequest.toJson(), "{\"template_id\":1,\"user_version\":\"v0.1.0\",\"user_desc\":\"init\",\"ext_json\":\"{\\\"extEnable\\\":true,\\\"extAppid\\\":\\\"app123\\\"}\"}"); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java new file mode 100644 index 0000000000..1f962087dc --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java @@ -0,0 +1,30 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import org.testng.annotations.Test; + +import java.util.Arrays; + +/** + * @author Charming + * @since 2018-04-26 19:55 + */ +public class WxMaCodeSubmitAuditRequestTest { + @Test + public void testToJson() { + WxMaCodeSubmitAuditRequest request = WxMaCodeSubmitAuditRequest + .builder() + .itemList(Arrays.asList( + WxMaCategory + .builder() + .address("pages/logs/logs") + .tag("工具 效率") + .firstClass("工具") + .firstId(287L) + .secondClass("效率") + .secondId(616L) + .title("日志") + .build() + )).build(); + System.out.println(request.toJson()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistributionTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistributionTest.java new file mode 100644 index 0000000000..da02972e04 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistributionTest.java @@ -0,0 +1,15 @@ +package cn.binarywang.wx.miniapp.bean.code; + +import org.testng.annotations.Test; + +/** + * @author Charming + * @since 2018-04-26 19:58 + */ +public class WxMaCodeVersionDistributionTest { + @Test + public void testFromJson() { + String json = "{\"errcode\":0,\"errmsg\":\"ok\",\"now_version\":\"1.2.0\",\"uv_info\":{\"items\":[{\"version\":\"0.0.0\",\"percentage\":0},{\"version\":\"1.0.0\",\"percentage\":0},{\"version\":\"1.0.1\",\"percentage\":0},{\"version\":\"1.1.0\",\"percentage\":0},{\"version\":\"1.1.1\",\"percentage\":0},{\"version\":\"1.2.0\",\"percentage\":0},{\"version\":\"1.2.1\",\"percentage\":0},{\"version\":\"1.2.2\",\"percentage\":0},{\"version\":\"1.2.3\",\"percentage\":0},{\"version\":\"1.2.4\",\"percentage\":0},{\"version\":\"1.2.5\",\"percentage\":0},{\"version\":\"1.2.6\",\"percentage\":0},{\"version\":\"1.3.0\",\"percentage\":0},{\"version\":\"1.4.0\",\"percentage\":0},{\"version\":\"1.4.1\",\"percentage\":0},{\"version\":\"1.4.2\",\"percentage\":0},{\"version\":\"1.4.3\",\"percentage\":0},{\"version\":\"1.4.4\",\"percentage\":0},{\"version\":\"1.5.0\",\"percentage\":0},{\"version\":\"1.5.1\",\"percentage\":0},{\"version\":\"1.5.2\",\"percentage\":0},{\"version\":\"1.5.3\",\"percentage\":0},{\"version\":\"1.5.4\",\"percentage\":0},{\"version\":\"1.5.5\",\"percentage\":0},{\"version\":\"1.5.6\",\"percentage\":0},{\"version\":\"1.5.7\",\"percentage\":0},{\"version\":\"1.5.8\",\"percentage\":0},{\"version\":\"1.6.0\",\"percentage\":0.0132},{\"version\":\"1.6.1\",\"percentage\":0.0132},{\"version\":\"1.6.2\",\"percentage\":0.0132},{\"version\":\"1.6.3\",\"percentage\":0.0132},{\"version\":\"1.6.4\",\"percentage\":0.0132},{\"version\":\"1.6.5\",\"percentage\":0.0132},{\"version\":\"1.6.6\",\"percentage\":0.0132},{\"version\":\"1.6.7\",\"percentage\":0.1711},{\"version\":\"1.6.8\",\"percentage\":0.1711},{\"version\":\"1.7.0\",\"percentage\":0.1842},{\"version\":\"1.7.1\",\"percentage\":0.25},{\"version\":\"1.7.2\",\"percentage\":0.5263},{\"version\":\"1.7.3\",\"percentage\":0.5263},{\"version\":\"1.7.4\",\"percentage\":0.6711}]}}\n"; + System.out.println(WxMaCodeVersionDistribution.fromJson(json)); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImplTest.java new file mode 100644 index 0000000000..6fdcee10f4 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImplTest.java @@ -0,0 +1,70 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import lombok.SneakyThrows; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.TransportMode; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author yqx + * @date 2020/5/3 + */ +public class WxMaRedissonConfigImplTest { + + WxMaDefaultConfigImpl wxMaConfig; + + @BeforeMethod + public void setUp() { + Config config = new Config(); + config.useSingleServer().setAddress("redis://127.0.0.1:6379") + .setDatabase(0); + config.setTransportMode(TransportMode.NIO); + RedissonClient redisson = Redisson.create(config); + wxMaConfig = new WxMaRedissonConfigImpl(redisson); + wxMaConfig.setAppid("appid12345678"); + wxMaConfig.updateAccessToken("accessToken", 5); //有效期5秒 + wxMaConfig.updateJsapiTicket("jsapiTicket", 5); + wxMaConfig.updateCardApiTicket("cardApiTicket", 5); + } + + @SneakyThrows + @Test + public void testGetAccessToken() { + String accessToken = wxMaConfig.getAccessToken(); + Assert.assertEquals(accessToken, "accessToken"); + Assert.assertFalse(wxMaConfig.isAccessTokenExpired()); + Thread.sleep(6000);//休眠6s + Assert.assertTrue(wxMaConfig.isAccessTokenExpired()); + } + + @SneakyThrows + @Test + public void testGetJsapiTicket() { + String jsapiTicket = wxMaConfig.getJsapiTicket(); + Assert.assertEquals(jsapiTicket, "jsapiTicket"); + Assert.assertFalse(wxMaConfig.isJsapiTicketExpired()); + Thread.sleep(6000);//休眠6s + Assert.assertTrue(wxMaConfig.isJsapiTicketExpired()); + } + + @SneakyThrows + @Test + public void testGetCardApiTicket() { + String cardApiTicket = wxMaConfig.getCardApiTicket(); + Assert.assertEquals(cardApiTicket, "cardApiTicket"); + Assert.assertFalse(wxMaConfig.isCardApiTicketExpired()); + Thread.sleep(6000);//休眠6s + Assert.assertTrue(wxMaConfig.isCardApiTicketExpired()); + } + + @Test + public void testIsAccessTokenExpired() { + Assert.assertFalse(wxMaConfig.isAccessTokenExpired()); + wxMaConfig.expireAccessToken(); + Assert.assertTrue(wxMaConfig.isAccessTokenExpired()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java new file mode 100644 index 0000000000..7784cf3a1d --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java @@ -0,0 +1,149 @@ +package cn.binarywang.wx.miniapp.demo; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.message.WxMaMessageHandler; +import cn.binarywang.wx.miniapp.message.WxMaMessageRouter; +import cn.binarywang.wx.miniapp.message.WxMaXmlOutMessage; +import cn.binarywang.wx.miniapp.test.TestConfig; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Calendar; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Binary Wang + */ +public class WxMaDemoServer { + + private static final WxMaMessageHandler logHandler = new WxMaMessageHandler() { + @Override + public WxMaXmlOutMessage handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException { + System.out.println("收到消息:" + wxMessage.toString()); + service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson()) + .toUser(wxMessage.getFromUser()).build()); + return null; + } + }; + + private static final WxMaMessageHandler textHandler = new WxMaMessageHandler() { + @Override + public WxMaXmlOutMessage handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) + throws WxErrorException { + service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息") + .toUser(wxMessage.getFromUser()).build()); + return null; + } + + }; + + private static final WxMaMessageHandler picHandler = new WxMaMessageHandler() { + @Override + public WxMaXmlOutMessage handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException { + try { + WxMediaUploadResult uploadResult = service.getMediaService() + .uploadMedia(WxMaConstants.MediaType.IMAGE, "png", + ClassLoader.getSystemResourceAsStream("tmp.png")); + service.getMsgService().sendKefuMsg( + WxMaKefuMessage + .newImageBuilder() + .mediaId(uploadResult.getMediaId()) + .toUser(wxMessage.getFromUser()) + .build()); + } catch (WxErrorException e) { + e.printStackTrace(); + } + return null; + } + }; + + private static final WxMaMessageHandler qrcodeHandler = new WxMaMessageHandler() { + @Override + public WxMaXmlOutMessage handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException { + try { + final File file = service.getQrcodeService().createQrcode("123", 430); + WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia(WxMaConstants.MediaType.IMAGE, file); + service.getMsgService().sendKefuMsg( + WxMaKefuMessage + .newImageBuilder() + .mediaId(uploadResult.getMediaId()) + .toUser(wxMessage.getFromUser()) + .build()); + } catch (WxErrorException e) { + e.printStackTrace(); + } + return null; + } + }; + + private static final WxMaMessageHandler customerServiceMessageHandler = new WxMaMessageHandler() { + @Override + public WxMaXmlOutMessage handle(WxMaMessage message, Map context, WxMaService service, WxSessionManager sessionManager) { + return new WxMaXmlOutMessage() + .setMsgType(WxConsts.XmlMsgType.TRANSFER_CUSTOMER_SERVICE) + .setFromUserName(message.getToUser()) + .setCreateTime(Calendar.getInstance().getTimeInMillis() / 1000) + .setToUserName(message.getFromUser()); + } + }; + + private static WxMaConfig config; + private static WxMaService service; + private static WxMaMessageRouter router; + private static String templateId; + + public static void main(String[] args) throws Exception { + init(); + + Server server = new Server(8080); + + ServletHandler servletHandler = new ServletHandler(); + server.setHandler(servletHandler); + + ServletHolder endpointServletHolder = new ServletHolder(new WxMaPortalServlet(config, service, router)); + servletHandler.addServletWithMapping(endpointServletHolder, "/*"); + + server.start(); + server.join(); + } + + private static void init() { + try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { + TestConfig config = TestConfig.fromXml(is1); + config.setAccessTokenLock(new ReentrantLock()); + templateId = config.getTemplateId(); + + WxMaDemoServer.config = config; + service = new WxMaServiceImpl(); + service.setWxMaConfig(config); + + router = new WxMaMessageRouter(service); + + router.rule().handler(logHandler).next() + .rule().async(false).content("文本").handler(textHandler).end() + .rule().async(false).content("图片").handler(picHandler).end() + .rule().async(false).content("二维码").handler(qrcodeHandler).end() + .rule().async(false).content("转发客服").handler(customerServiceMessageHandler).end(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java new file mode 100644 index 0000000000..c209082d45 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java @@ -0,0 +1,97 @@ +package cn.binarywang.wx.miniapp.demo; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.message.WxMaMessageRouter; +import cn.binarywang.wx.miniapp.message.WxMaXmlOutMessage; +import lombok.AllArgsConstructor; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * @author Binary Wang + */ +@AllArgsConstructor +public class WxMaPortalServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + private WxMaConfig config; + private WxMaService service; + private WxMaMessageRouter messageRouter; + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) + throws IOException { + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + + String signature = request.getParameter("signature"); + String nonce = request.getParameter("nonce"); + String timestamp = request.getParameter("timestamp"); + + if (!this.service.checkSignature(timestamp, nonce, signature)) { + // 消息签名不正确,说明不是公众平台发过来的消息 + response.getWriter().println("非法请求"); + return; + } + + String echoStr = request.getParameter("echostr"); + if (StringUtils.isNotBlank(echoStr)) { + // 说明是一个仅仅用来验证的请求,回显echostr + response.getWriter().println(echoStr); + return; + } + + String encryptType = request.getParameter("encrypt_type"); + final boolean isJson = Objects.equals(this.config.getMsgDataFormat(), WxMaConstants.MsgDataFormat.JSON); + if (StringUtils.isBlank(encryptType)) { + // 明文传输的消息 + WxMaMessage inMessage; + if (isJson) { + inMessage = WxMaMessage.fromJson(IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8)); + } else {//xml + inMessage = WxMaMessage.fromXml(request.getInputStream()); + } + + final WxMaXmlOutMessage outMessage = this.messageRouter.route(inMessage); + if (outMessage != null) { + response.getWriter().write(outMessage.toXml()); + return; + } + + response.getWriter().write("success"); + return; + } + + if ("aes".equals(encryptType)) { + // 是aes加密的消息 + String msgSignature = request.getParameter("msg_signature"); + WxMaMessage inMessage; + if (isJson) { + inMessage = WxMaMessage.fromEncryptedJson(request.getInputStream(), this.config); + } else {//xml + inMessage = WxMaMessage.fromEncryptedXml(request.getInputStream(), this.config, timestamp, nonce, msgSignature); + } + + final WxMaXmlOutMessage outMessage = this.messageRouter.route(inMessage); + if (outMessage != null) { + response.getWriter().write(outMessage.toEncryptedXml(this.config)); + return; + } + response.getWriter().write("success"); + return; + } + + response.getWriter().println("不可识别的加密类型"); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/message/WxMaXmlOutMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/message/WxMaXmlOutMessageTest.java new file mode 100644 index 0000000000..f6f775b6f6 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/message/WxMaXmlOutMessageTest.java @@ -0,0 +1,22 @@ +package cn.binarywang.wx.miniapp.message; + +import me.chanjar.weixin.common.api.WxConsts; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class WxMaXmlOutMessageTest { + + @Test + public void testToXml() { + WxMaXmlOutMessage message = WxMaXmlOutMessage.builder() + .fromUserName("1") + .toUserName("2") + .msgType(WxConsts.XmlMsgType.TRANSFER_CUSTOMER_SERVICE) + .createTime(System.currentTimeMillis() / 1000) + .build(); + + assertThat(message.toXml()).isNotEmpty(); + System.out.println(message.toXml()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java new file mode 100644 index 0000000000..267eb70ca3 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java @@ -0,0 +1,41 @@ +package cn.binarywang.wx.miniapp.test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import com.google.inject.Binder; +import com.google.inject.Module; + +/** + * @author Binary Wang + */ +public class ApiTestModule implements Module { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + private static final String TEST_CONFIG_XML = "test-config.xml"; + + @Override + public void configure(Binder binder) { + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(TEST_CONFIG_XML)) { + if (inputStream == null) { + throw new RuntimeException("测试配置文件【" + TEST_CONFIG_XML + "】未找到,请参照test-config-sample.xml文件生成"); + } + TestConfig config = TestConfig.fromXml(inputStream); + config.setAccessTokenLock(new ReentrantLock()); + + WxMaService wxService = new cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl(); + wxService.setWxMaConfig(config); + + binder.bind(WxMaService.class).toInstance(wxService); + binder.bind(WxMaConfig.class).toInstance(config); + } catch (IOException e) { + this.log.error(e.getMessage(), e); + } + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java new file mode 100644 index 0000000000..a9dd465612 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java @@ -0,0 +1,62 @@ +package cn.binarywang.wx.miniapp.test; + +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.io.InputStream; +import java.util.concurrent.locks.Lock; + +/** + * @author Binary Wang + */ +@XStreamAlias("xml") +public class TestConfig extends WxMaDefaultConfigImpl { + + private String openid; + private String kfAccount; + private String templateId; + + public static TestConfig fromXml(InputStream is) { + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(TestConfig.class); + return (TestConfig) xstream.fromXML(is); + } + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public String getKfAccount() { + return this.kfAccount; + } + + public void setKfAccount(String kfAccount) { + this.kfAccount = kfAccount; + } + + public String getTemplateId() { + return this.templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + @Override + public void setAccessTokenLock(Lock lock) { + super.accessTokenLock = lock; + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConstants.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConstants.java new file mode 100644 index 0000000000..091de1d3ed --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConstants.java @@ -0,0 +1,17 @@ +package cn.binarywang.wx.miniapp.test; + +/** + *
    + * 仅供测试使用的一些常量
    + * Created by Binary Wang on 2017-3-9.
    + * 
    + */ +public class TestConstants { + /////////////////////// + // 文件类型 + /////////////////////// + public static final String FILE_JPG = "jpeg"; + public static final String FILE_MP3 = "mp3"; + public static final String FILE_AMR = "amr"; + public static final String FILE_MP4 = "mp4"; +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtilsTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtilsTest.java new file mode 100644 index 0000000000..76b4e96743 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtilsTest.java @@ -0,0 +1,35 @@ +package cn.binarywang.wx.miniapp.util.crypt; + + +import org.testng.annotations.*; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/12/25.
    + * 
    + * + * @author Binary Wang + */ +public class WxMaCryptUtilsTest { + @Test + public void testDecrypt() { + String sessionKey = "7MG7jbTToVVRWRXVA885rg=="; + String encryptedData = "BY6VOgcWbwGcyrunK0ECWI8rnDsT69DucZ+M78tc1aL9aM/3bEAHFYd4fu7kRjWhD4YfjObw44T9vUqKyHIjbKs6hvtEasZZEIW35x4a91xVgN48ZqZ7MTQqUlP13kDUlkuwYh+/8g8yceu4kNbjowYrhihx+SV7CfjKCveJ7TSepr5Z7aLv1o+rfeelfOwn++WN/YoQsuZ6S3L4fWlWe5DAAUnFUI6cJvxxCohVzbrVXhyH2AqQdSjH2WnMYFeaGFIbcoxMznlk7oEwFn+hBj63dyT/swdYQfEdzuyCBmKXy8d6l1RKVX6Y65coTD8kIlbr+FKsqYrXVUIUBSwehqYuOdhYWZ9Bntl5DWU1oqzAPCnMn2cAIoQpQPKP7IGSxMOvCNAMhVXbE7BvnWuVuGF+AM5tXAa9IVUhcMImGwLQqm4iV5uBd+5OcFObh3A4VJk9iBCBWSkBHa/rV9CVoY0bFv2F9/2Hv82++Ybl274="; + String ivStr = "TarMFjnzHVxy8pdS93wQbw=="; + System.out.println(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); +// System.out.println(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr)); + } + + @Test + public void testDecryptAnotherWay() { + String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew=="; + String ivStr = "r7BXXKkLb8qrSNn05n0qiA=="; + String sessionKey = "tiihtNczf5v6AKRyjwEUhQ=="; + + assertThat(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)) + .isEqualTo(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr)); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapterTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapterTest.java new file mode 100644 index 0000000000..1ae60070cd --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapterTest.java @@ -0,0 +1,111 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; +import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/9/23.
    + * 
    + * + * @author Binary Wang + */ +public class WxMaUniformMessageGsonAdapterTest { + + @Test + public void testSerialize_mp() { + WxMaUniformMessage message = WxMaUniformMessage.builder() + .isMpTemplateMsg(true) + .toUser("OPENID") + .appid("APPID") + .templateId("TEMPLATE_ID") + .url("http://weixin.qq.com/download") + .miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false, false)) + .build(); + message.addData(new WxMaTemplateData("first", "恭喜你购买成功!", "#173177")) + .addData(new WxMaTemplateData("keyword1", "巧克力", "#173177")) + .addData(new WxMaTemplateData("keyword2", "39.8元", "#173177")) + .addData(new WxMaTemplateData("keyword3", "2014年9月22日", "#173177")) + .addData(new WxMaTemplateData("remark", "欢迎再次购买!", "#173177")); + + assertThat(message.toJson()).isEqualTo(GsonParser.parse("{\n" + + " \"touser\":\"OPENID\",\n" + + " \"mp_template_msg\":{\n" + + " \"appid\":\"APPID\",\n" + + " \"template_id\":\"TEMPLATE_ID\",\n" + + " \"url\":\"http://weixin.qq.com/download\",\n" + + " \"miniprogram\":{\n" + + " \"appid\":\"xiaochengxuappid12345\",\n" + + " \"pagepath\":\"index?foo=bar\"\n" + + " },\n" + + " \"data\":{\n" + + " \"first\":{\n" + + " \"value\":\"恭喜你购买成功!\",\n" + + " \"color\":\"#173177\"\n" + + " },\n" + + " \"keyword1\":{\n" + + " \"value\":\"巧克力\",\n" + + " \"color\":\"#173177\"\n" + + " },\n" + + " \"keyword2\":{\n" + + " \"value\":\"39.8元\",\n" + + " \"color\":\"#173177\"\n" + + " },\n" + + " \"keyword3\":{\n" + + " \"value\":\"2014年9月22日\",\n" + + " \"color\":\"#173177\"\n" + + " },\n" + + " \"remark\":{\n" + + " \"value\":\"欢迎再次购买!\",\n" + + " \"color\":\"#173177\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}").toString()); + } + + @Test + public void testSerialize_ma() { + WxMaUniformMessage message = WxMaUniformMessage.builder() + .isMpTemplateMsg(false) + .toUser("OPENID") + .page("page/page/index") + .templateId("TEMPLATE_ID") + .formId("FORMID") + .emphasisKeyword("keyword1.DATA") + .build(); + message.addData(new WxMaTemplateData("keyword1", "339208499")) + .addData(new WxMaTemplateData("keyword2", "2015年01月05日 12:30")) + .addData(new WxMaTemplateData("keyword3", "腾讯微信总部")) + .addData(new WxMaTemplateData("keyword4", "广州市海珠区新港中路397号")); + + assertThat(message.toJson()).isEqualTo(GsonParser.parse("{\n" + + " \"touser\":\"OPENID\",\n" + + " \"weapp_template_msg\":{\n" + + " \"template_id\":\"TEMPLATE_ID\",\n" + + " \"page\":\"page/page/index\",\n" + + " \"form_id\":\"FORMID\",\n" + + " \"data\":{\n" + + " \"keyword1\":{\n" + + " \"value\":\"339208499\"\n" + + " },\n" + + " \"keyword2\":{\n" + + " \"value\":\"2015年01月05日 12:30\"\n" + + " },\n" + + " \"keyword3\":{\n" + + " \"value\":\"腾讯微信总部\"\n" + + " },\n" + + " \"keyword4\":{\n" + + " \"value\":\"广州市海珠区新港中路397号\"\n" + + " }\n" + + " },\n" + + " \"emphasis_keyword\":\"keyword1.DATA\"\n" + + " }\n" + + "}").toString()); + } +} diff --git a/weixin-java-miniapp/src/test/resources/logback-test.xml b/weixin-java-miniapp/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..e4a33acd88 --- /dev/null +++ b/weixin-java-miniapp/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n + + + + + + + + diff --git a/weixin-java-miniapp/src/test/resources/test-config-sample.xml b/weixin-java-miniapp/src/test/resources/test-config-sample.xml new file mode 100644 index 0000000000..7812fc7469 --- /dev/null +++ b/weixin-java-miniapp/src/test/resources/test-config-sample.xml @@ -0,0 +1,12 @@ + + JSON或者XML + appid + secret + Token + EncodingAESKey + 云环境 + 可以不填写 + 可以不填写 + 某个用户的openId + 模版消息的模版ID + diff --git a/weixin-java-miniapp/src/test/resources/tmp.png b/weixin-java-miniapp/src/test/resources/tmp.png new file mode 100644 index 0000000000..7c3a48838d Binary files /dev/null and b/weixin-java-miniapp/src/test/resources/tmp.png differ diff --git a/weixin-java-miniapp/src/test/resources/wx-ma-jssecacerts b/weixin-java-miniapp/src/test/resources/wx-ma-jssecacerts new file mode 100644 index 0000000000..7a7995ea0d Binary files /dev/null and b/weixin-java-miniapp/src/test/resources/wx-ma-jssecacerts differ diff --git a/weixin-java-miniapp/src/test/resources/wx-mp-jssecacerts b/weixin-java-miniapp/src/test/resources/wx-mp-jssecacerts new file mode 100644 index 0000000000..5f7a420d37 Binary files /dev/null and b/weixin-java-miniapp/src/test/resources/wx-mp-jssecacerts differ diff --git a/weixin-java-mp/build.gradle b/weixin-java-mp/build.gradle deleted file mode 100644 index de1534a3a9..0000000000 --- a/weixin-java-mp/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ - -description = 'WeiXin Java Tools - MP' -dependencies { - compile project(':weixin-java-common') - testCompile group: 'junit', name: 'junit', version:'4.11' - testCompile group: 'org.testng', name: 'testng', version:'6.8.7' - testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' - testCompile group: 'com.google.inject', name: 'guice', version:'3.0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' - testCompile group: 'joda-time', name: 'joda-time', version:'2.9.4' -} -test.useTestNG() diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 3f4bf6498f..a89664d469 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -1,15 +1,17 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> 4.0.0 com.github.binarywang - weixin-java-parent - 2.6.0 + wx-java + 3.9.0 + weixin-java-mp - WeiXin Java Tools - MP + WxJava - MP Java SDK 微信公众号Java SDK @@ -18,11 +20,28 @@ weixin-java-common ${project.version} + + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + + org.testng testng test + + org.mockito + mockito-all + test + com.google.inject guice @@ -41,13 +60,30 @@ joda-time joda-time - 2.9.4 test redis.clients jedis + + ch.qos.logback + logback-classic + test + + + org.assertj + assertj-guava + test + + + org.projectlombok + lombok + + + org.redisson + redisson + @@ -64,4 +100,35 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java new file mode 100644 index 0000000000..230910e888 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java @@ -0,0 +1,72 @@ +package me.chanjar.weixin.mp.api; + +import java.io.File; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.enums.AiLangType; + +/** + *
    + * 微信AI开放接口(语音识别,微信翻译).
    + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21516712282KzWVE
    + *  Created by BinaryWang on 2018/6/9.
    + * 
    + * + * @author Binary Wang + */ +public interface WxMpAiOpenService { + + /** + *
    +   * 提交语音.
    +   * http请求方式: POST
    +   * http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?access_token=ACCESS_TOKEN&format=&voice_id=xxxxxx&lang=zh_CN
    +   * 
    + * + * @param lang 语言,zh_CN 或 en_US,默认中文 + * @param voiceFile 语音文件 + * @param voiceId 语音唯一标识 + */ + void uploadVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException; + + /** + *
    +   * 获取语音识别结果.
    +   * 接口调用请求说明
    +   *
    +   * http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext?access_token=ACCESS_TOKEN&voice_id=xxxxxx&lang=zh_CN
    +   * 请注意,添加完文件之后10s内调用这个接口
    +   *
    +   * 
    + * + * @param lang 语言,zh_CN 或 en_US,默认中文 + * @param voiceId 语音唯一标识 + */ + String queryRecognitionResult(String voiceId, AiLangType lang) throws WxErrorException; + + /** + * 识别指定语音文件内容. + * 此方法揉合了前两两个方法:uploadVoice 和 queryRecognitionResult + * + * @param lang 语言,zh_CN 或 en_US,默认中文 + * @param voiceFile 语音文件 + * @param voiceId 语音唯一标识 + */ + String recogniseVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException; + + /** + *
    +   * 微信翻译.
    +   * 接口调用请求说明
    +   *
    +   * http请求方式: POST
    +   * http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx<o=xxx
    +   *
    +   * 
    + * + * @param langFrom 源语言,zh_CN 或 en_US + * @param langTo 目标语言,zh_CN 或 en_US + * @param content 要翻译的文本内容 + */ + String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java index d4a36c65b8..f0bd0b8f04 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java @@ -1,27 +1,37 @@ package me.chanjar.weixin.mp.api; import me.chanjar.weixin.common.bean.WxCardApiSignature; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.mp.bean.result.WxMpCardResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.card.*; + +import java.util.List; /** - * 卡券相关接口 - * @author YuJian(mgcnrx11@hotmail.com) on 01/11/2016 + * 卡券相关接口. + * + * @author YuJian(mgcnrx11 @ hotmail.com) on 01/11/2016 + * @author yuanqixun 2018-08-29 */ public interface WxMpCardService { + /** + * 得到WxMpService. + * + * @return WxMpService + */ + WxMpService getWxMpService(); /** - * 获得卡券api_ticket,不强制刷新卡券api_ticket + * 获得卡券api_ticket,不强制刷新卡券api_ticket. * * @return 卡券api_ticket - * @throws WxErrorException + * @throws WxErrorException 异常 * @see #getCardApiTicket(boolean) */ String getCardApiTicket() throws WxErrorException; /** *
    -   * 获得卡券api_ticket
    +   * 获得卡券api_ticket.
        * 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
        *
        * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94.9F.E6.88.90.E7.AE.97.E6.B3.95
    @@ -29,67 +39,68 @@ public interface WxMpCardService {
        *
        * @param forceRefresh 强制刷新
        * @return 卡券api_ticket
    -   * @throws WxErrorException
    +   * @throws WxErrorException 异常
        */
       String getCardApiTicket(boolean forceRefresh) throws WxErrorException;
     
       /**
        * 
    -   * 创建调用卡券api时所需要的签名
    +   * 创建调用卡券api时所需要的签名.
        *
        * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
        * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
        * .9F.E6.88.90.E7.AE.97.E6.B3.95
        * 
    * - * @param optionalSignParam 参与签名的参数数组。 - * 可以为下列字段:app_id, card_id, card_type, code, openid, location_id + * @param optionalSignParam 参与签名的参数数组。可以为下列字段:app_id, card_id, card_type, code, openid, location_id *
    注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空 * @return 卡券Api签名对象 + * @throws WxErrorException 异常 */ - WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws - WxErrorException; + WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws WxErrorException; /** - * 卡券Code解码 + * 卡券Code解码. * * @param encryptCode 加密Code,通过JSSDK的chooseCard接口获得 * @return 解密后的Code + * @throws WxErrorException 异常 */ String decryptCardCode(String encryptCode) throws WxErrorException; /** - * 卡券Code查询 + * 卡券Code查询. + * 文档地址: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272&anchor=1 * * @param cardId 卡券ID代表一类卡券 * @param code 单张卡券的唯一标准 * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 * @return WxMpCardResult对象 + * @throws WxErrorException 异常 */ - WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) - throws WxErrorException; + WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException; /** * 卡券Code核销。核销失败会抛出异常 * * @param code 单张卡券的唯一标准 - * @return 调用返回的JSON字符串。 - *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @return 调用返回的JSON字符串。可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @throws WxErrorException 异常 */ String consumeCardCode(String code) throws WxErrorException; /** - * 卡券Code核销。核销失败会抛出异常 + * 卡券Code核销。核销失败会抛出异常. * * @param code 单张卡券的唯一标准 * @param cardId 当自定义Code卡券时需要传入card_id - * @return 调用返回的JSON字符串。 - *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @return 调用返回的JSON字符串。可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 + * @throws WxErrorException 异常 */ String consumeCardCode(String code, String cardId) throws WxErrorException; /** - * 卡券Mark接口。 + * 卡券Mark接口. * 开发者在帮助消费者核销卡券之前,必须帮助先将此code(卡券串码)与一个openid绑定(即mark住), * 才能进一步调用核销接口,否则报错。 * @@ -97,18 +108,190 @@ WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) * @param cardId 卡券的ID * @param openId 用券用户的openid * @param isMark 是否要mark(占用)这个code,填写true或者false,表示占用或解除占用 + * @throws WxErrorException 异常 */ - void markCardCode(String code, String cardId, String openId, boolean isMark) throws - WxErrorException; + void markCardCode(String code, String cardId, String openId, boolean isMark) throws WxErrorException; /** - * 查看卡券详情接口 + * 查看卡券详情接口. * 详见 https://mp.weixin.qq.com/wiki/14/8dd77aeaee85f922db5f8aa6386d385e.html#.E6.9F.A5.E7.9C.8B.E5.8D.A1.E5.88.B8.E8.AF.A6.E6.83.85 * * @param cardId 卡券的ID * @return 返回的卡券详情JSON字符串 *
    [注] 由于返回的JSON格式过于复杂,难以定义其对应格式的Bean并且难以维护,因此只返回String格式的JSON串。 *
    可由 com.google.gson.JsonParser#parse 等方法直接取JSON串中的某个字段。 + * @throws WxErrorException 异常 */ String getCardDetail(String cardId) throws WxErrorException; + + /** + * 添加测试白名单. + * + * @param openid 用户的openid + * @return string + * @throws WxErrorException 异常 + */ + String addTestWhiteList(String openid) throws WxErrorException; + + /** + * 创建卡券. + * + * @param cardCreateMessage 请求 + * @return result + * @throws WxErrorException 异常 + */ + WxMpCardCreateResult createCard(WxMpCardCreateRequest cardCreateMessage) throws WxErrorException; + + /** + * 创建卡券二维码. + * + * @param cardId 卡券编号 + * @param outerStr 二维码标识 + * @return WxMpCardQrcodeCreateResult + * @throws WxErrorException 异常 + */ + WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr) throws WxErrorException; + + /** + * 创建卡券二维码. + * + * @param cardId 卡券编号 + * @param outerStr 二维码标识 + * @param expiresIn 指定二维码的有效时间,范围是60 ~ 1800秒。不填默认为365天有效 + * @return WxMpCardQrcodeCreateResult + * @throws WxErrorException 异常 + */ + WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr, int expiresIn) throws WxErrorException; + + /** + * 创建卡券二维码. + * + * @param cardId 卡券编号 + * @param outerStr 用户首次领卡时,会通过 领取事件推送 给商户; 对于会员卡的二维码,用户每次扫码打开会员卡后点击任何url,会将该值拼入url中,方便开发者定位扫码来源 + * @param expiresIn 指定二维码的有效时间,范围是60 ~ 1800秒。不填默认为365天有效 + * @param isUniqueCode 指定下发二维码,生成的二维码随机分配一个code,领取后不可再次扫描。填写true或false。默认false,注意填写该字段时,卡券须通过审核且库存不为0。 + * @param code 卡券Code码,use_custom_code字段为true的卡券必须填写,非自定义code和导入code模式的卡券不必填写。 + * @param openid 指定领取者的openid,只有该用户能领取。bind_openid字段为true的卡券必须填写,非指定openid不必填写。 + * @return WxMpCardQrcodeCreateResult + * @throws WxErrorException 异常 + */ + WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr, int expiresIn, String openid, + String code, boolean isUniqueCode) throws WxErrorException; + + /** + * 创建卡券货架. + * + * @param createRequest 货架创建参数 + * @return WxMpCardLandingPageCreateResult + * @throws WxErrorException 异常 + */ + WxMpCardLandingPageCreateResult createLandingPage(WxMpCardLandingPageCreateRequest createRequest) + throws WxErrorException; + + /** + * 将用户的卡券设置为失效状态. + * 详见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272&anchor=9 + * + * @param cardId 卡券编号 + * @param code 用户会员卡号 + * @param reason 设置为失效的原因 + * @return result + * @throws WxErrorException 异常 + */ + String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException; + + /** + * 删除卡券接口. + * + * @param cardId 卡券id + * @return 删除结果 + * @throws WxErrorException 异常 + */ + WxMpCardDeleteResult deleteCard(String cardId) throws WxErrorException; + + + /** + * 导入自定义code(仅对自定义code商户) + * + * @param cardId 卡券id + * @param codeList 需导入微信卡券后台的自定义code,上限为100个。 + */ + WxMpCardCodeDepositResult cardCodeDeposit(String cardId, List codeList) throws WxErrorException; + + /** + * 查询导入code数目接口 + * + * @param cardId 卡券id + */ + WxMpCardCodeDepositCountResult cardCodeDepositCount(String cardId) throws WxErrorException; + + + /** + * 核查code接口 + * + * @param cardId 卡券id + * @param codeList 已经微信卡券后台的自定义code,上限为100个 + */ + WxMpCardCodeCheckcodeResult cardCodeCheckcode(String cardId, List codeList) throws WxErrorException; + + /** + * 图文消息群发卡券获取内嵌html + * + * @param cardId 卡券id + */ + WxMpCardMpnewsGethtmlResult cardMpnewsGethtml(String cardId) throws WxErrorException; + + + /** + * 修改库存接口 + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#5 + * + * @param cardId 卡券ID + * @param changeValue 库存变更值,负值为减少库存 + */ + void cardModifyStock(String cardId, Integer changeValue) throws WxErrorException; + + + /** + * 更改Code接口 + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#6 + * + * @param cardId 卡券ID + * @param oldCode 需变更的Code码 + * @param newCode 变更后的有效Code码 + */ + void cardCodeUpdate(String cardId, String oldCode, String newCode) throws WxErrorException; + + /** + * 设置买单接口 + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Create_a_Coupon_Voucher_or_Card.html#12 + * + * @param cardId 卡券ID + * @param isOpen 是否开启买单功能,填true/false + */ + void cardPaycellSet(String cardId, Boolean isOpen) throws WxErrorException; + + /** + * 设置自助核销 + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Create_a_Coupon_Voucher_or_Card.html#14 + * + * @param cardId 卡券ID + * @param isOpen 是否开启自助核销功能 + * @param needVerifyCod 用户核销时是否需要输入验证码, 填true/false, 默认为false + * @param needRemarkAmount 用户核销时是否需要备注核销金额, 填true/false, 默认为false + */ + void cardSelfConsumeCellSet(String cardId, Boolean isOpen, + Boolean needVerifyCod, Boolean needRemarkAmount) throws WxErrorException; + + /** + * 获取用户已领取卡券接口 + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#1 + * + * @param openId 需要查询的用户openid + * @param cardId 卡券ID。不填写时默认查询当前appid下的卡券 + * @return + * @throws WxErrorException + */ + WxUserCardListResult getUserCardList(String openId, String cardId) throws WxErrorException; + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java new file mode 100644 index 0000000000..c4ba7ca326 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java @@ -0,0 +1,103 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.comment.WxMpCommentListVo; + +/** + * 图文消息留言管理接口. + * https://developers.weixin.qq.com/doc/offiaccount/Comments_management/Image_Comments_Management_Interface.html + * + * @author Binary Wang + * @date 2019-06-16 + */ +public interface WxMpCommentService { + /** + * 打开已群发文章评论. + * https://api.weixin.qq.com/cgi-bin/comment/open?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @throws WxErrorException 异常 + */ + void open(String msgDataId, Integer index) throws WxErrorException; + + /** + * 关闭已群发文章评论. + * https://api.weixin.qq.com/cgi-bin/comment/close?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @throws WxErrorException 异常 + */ + void close(String msgDataId, Integer index) throws WxErrorException; + + /** + * 查看指定文章的评论数据. + * https://api.weixin.qq.com/cgi-bin/comment/list?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param begin 起始位置 + * @param count 获取数目(>=50会被拒绝) + * @param type type=0 普通评论&精选评论 type=1 普通评论 type=2 精选评论 + * @return 评论列表数据 + * @throws WxErrorException 异常 + */ + WxMpCommentListVo list(String msgDataId, Integer index, int begin, int count, int type) throws WxErrorException; + + /** + * 2.4 将评论标记精选. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/markelect?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void markElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; + + /** + * 2.5 将评论取消精选. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/unmarkelect?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void unmarkElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; + + /** + * 2.6 删除评论. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/delete?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void delete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; + + /** + * 2.7 回复评论. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/reply/add?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @param content 回复内容 + * @throws WxErrorException 异常 + */ + void replyAdd(String msgDataId, Integer index, Long userCommentId, String content) throws WxErrorException; + + /** + * 2.8 删除回复. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/reply/delete?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void replyDelete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java deleted file mode 100644 index f4053899ee..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java +++ /dev/null @@ -1,114 +0,0 @@ -package me.chanjar.weixin.mp.api; - -import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; - -import java.io.File; -import java.util.concurrent.locks.Lock; - -/** - * 微信客户端配置存储 - * - * @author chanjarster - */ -public interface WxMpConfigStorage { - - String getAccessToken(); - - Lock getAccessTokenLock(); - - boolean isAccessTokenExpired(); - - /** - * 强制将access token过期掉 - */ - void expireAccessToken(); - - /** - * 应该是线程安全的 - * - * @param accessToken 要更新的WxAccessToken对象 - */ - void updateAccessToken(WxAccessToken accessToken); - - /** - * 应该是线程安全的 - * - * @param accessToken 新的accessToken值 - * @param expiresInSeconds 过期时间,以秒为单位 - */ - void updateAccessToken(String accessToken, int expiresInSeconds); - - String getJsapiTicket(); - - Lock getJsapiTicketLock(); - - boolean isJsapiTicketExpired(); - - /** - * 强制将jsapi ticket过期掉 - */ - void expireJsapiTicket(); - - /** - * 应该是线程安全的 - * - * @param jsapiTicket 新的jsapi ticket值 - * @param expiresInSeconds 过期时间,以秒为单位 - */ - void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); - - String getCardApiTicket(); - - Lock getCardApiTicketLock(); - - boolean isCardApiTicketExpired(); - - /** - * 强制将卡券api ticket过期掉 - */ - void expireCardApiTicket(); - - /** - * 应该是线程安全的 - * - * @param cardApiTicket 新的cardApi ticket值 - * @param expiresInSeconds 过期时间,以秒为单位 - */ - void updateCardApiTicket(String cardApiTicket, int expiresInSeconds); - - String getAppId(); - - String getSecret(); - - String getToken(); - - String getAesKey(); - - long getExpiresTime(); - - String getOauth2redirectUri(); - - String getHttpProxyHost(); - - int getHttpProxyPort(); - - String getHttpProxyUsername(); - - String getHttpProxyPassword(); - - File getTmpDirFile(); - - /** - * http client builder - * - * @return ApacheHttpClientBuilder - */ - ApacheHttpClientBuilder getApacheHttpClientBuilder(); - - /** - * 是否自动刷新token - */ - boolean autoRefreshToken(); - -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java index 32f219d51a..c1b35bee5f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java @@ -1,19 +1,15 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.mp.bean.datacube.WxDataCubeArticleResult; -import me.chanjar.weixin.mp.bean.datacube.WxDataCubeArticleTotal; -import me.chanjar.weixin.mp.bean.datacube.WxDataCubeInterfaceResult; -import me.chanjar.weixin.mp.bean.datacube.WxDataCubeMsgResult; -import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserCumulate; -import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserSummary; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.datacube.*; import java.util.Date; import java.util.List; /** * 统计分析相关接口 - * Created by Binary Wang on 2016/8/23. + * Created by Binary Wang on 2016/8/23. + * * @author binarywang (https://github.com/binarywang) */ public interface WxMpDataCubeService { @@ -192,6 +188,7 @@ public interface WxMpDataCubeService { List getUpstreamMsgDistMonth(Date beginDate, Date endDate) throws WxErrorException; //*******************接口分析数据接口***********************// + /** *
        * 获取接口分析数据(getinterfacesummary)
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java
    index 2580d23a76..c2ccef5a64 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java
    @@ -1,10 +1,12 @@
     package me.chanjar.weixin.mp.api;
     
    -import me.chanjar.weixin.common.exception.WxErrorException;
    +import me.chanjar.weixin.common.error.WxErrorException;
     import me.chanjar.weixin.mp.bean.device.*;
     
     /**
      * Created by keungtung on 10/12/2016.
    + *
    + * @author keungtung
      */
     public interface WxMpDeviceService {
       /**
    @@ -20,6 +22,7 @@ public interface WxMpDeviceService {
        *   获取一组新的deviceid和设备二维码
        *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-6
        * 
    + * * @param productId 产品id * @return 返回WxDeviceQrCodeResult */ @@ -30,6 +33,7 @@ public interface WxMpDeviceService { * 将device id及其属性信息提交公众平台进行授权 * 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-6 *
    + * * @param wxDeviceAuthorize 授权请求对象 * @return WxDeviceAuthorizeResult */ @@ -41,6 +45,7 @@ public interface WxMpDeviceService { * 第三方后台绑定成功后,通知公众平台 * 详情请见:http://iot.weixin.qq.com/wiki/new/index.html/page=3-4-7 * + * * @param wxDeviceBind 绑定请求对象 * @return WxDeviceBindResult */ @@ -51,6 +56,7 @@ public interface WxMpDeviceService { * 强制绑定用户和设备 * 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-7 * + * * @param wxDeviceBind 强制绑定请求对象 * @return WxDeviceBindResult */ @@ -61,6 +67,7 @@ public interface WxMpDeviceService { * 第三方确认用户和设备的解绑操作 * 详情请见:http://iot.weixin.qq.com/wiki/new/index.html/page=3-4-7 * + * * @param wxDeviceBind 绑定请求对象 * @return WxDeviceBidResult */ @@ -71,6 +78,7 @@ public interface WxMpDeviceService { * 强制解绑用户和设备 * 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-7 * + * * @param wxDeviceBind 强制解绑请求对象 * @return WxDeviceBindResult */ @@ -81,17 +89,19 @@ public interface WxMpDeviceService { * 通过device type和device id 获取设备主人的openid * 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-11 * + * * @param deviceType 设备类型,目前为"公众账号原始ID" - * @param deviceId 设备ID + * @param deviceId 设备ID * @return WxDeviceOpenIdResult */ - WxDeviceOpenIdResult getOpenId(String deviceType,String deviceId) throws WxErrorException; + WxDeviceOpenIdResult getOpenId(String deviceType, String deviceId) throws WxErrorException; /** *
        *   通过openid获取用户在当前devicetype下绑定的deviceid列表`
        *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-12
        * 
    + * * @param openId 要查询的用户的openid * @return WxDeviceBindDeviceResult */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java deleted file mode 100644 index b2bb9b4e89..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java +++ /dev/null @@ -1,90 +0,0 @@ -package me.chanjar.weixin.mp.api; - -import redis.clients.jedis.Jedis; - -/** - * 基于Redis的微信配置provider - * - * @author lly835 - */ -@SuppressWarnings("hiding") -public class WxMpInRedisConfigStorage extends WxMpInMemoryConfigStorage { - - private final static String ACCESS_TOKEN_KEY = "wechat_access_token_"; - - private final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket_"; - - private final static String CARDAPI_TICKET_KEY = "wechat_cardapi_ticket_"; - - protected Jedis jedis; - - @Override - public String getAccessToken() { - return jedis.get(ACCESS_TOKEN_KEY.concat(appId)); - } - - @Override - public boolean isAccessTokenExpired() { - return getAccessToken() == null ? true : false; - } - -@Override - public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { - jedis.set(ACCESS_TOKEN_KEY.concat(appId), accessToken); - jedis.expire(ACCESS_TOKEN_KEY.concat(appId), expiresInSeconds - 200); - } - - @Override - public void expireAccessToken() { - jedis.expire(ACCESS_TOKEN_KEY.concat(appId), 0); - } - - @Override - public String getJsapiTicket() { - return jedis.get(JSAPI_TICKET_KEY.concat(appId)); - } - - @Override - public boolean isJsapiTicketExpired() { - return getJsapiTicket() == null ? true : false; - } - - @Override - public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { - jedis.set(JSAPI_TICKET_KEY.concat(appId), jsapiTicket); - jedis.expire(JSAPI_TICKET_KEY.concat(appId), expiresInSeconds - 200); - } - - @Override - public void expireJsapiTicket() { - jedis.expire(JSAPI_TICKET_KEY.concat(appId), 0); - } - - /** - * 卡券api_ticket - */ - @Override - public String getCardApiTicket() { - return jedis.get(CARDAPI_TICKET_KEY.concat(appId)); - } - - @Override - public boolean isCardApiTicketExpired() { - return getCardApiTicket() == null ? true : false; - } - - @Override - public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { - jedis.set(CARDAPI_TICKET_KEY.concat(appId), cardApiTicket); - jedis.expire(CARDAPI_TICKET_KEY.concat(appId), expiresInSeconds - 200); - } - - @Override - public void expireCardApiTicket() { - jedis.expire(CARDAPI_TICKET_KEY.concat(appId), 0); - } - - public void setJedis(Jedis jedis) { - this.jedis = jedis; - } -} \ No newline at end of file diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java index f57c963019..3404f5fe2e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java @@ -1,29 +1,35 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; -import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; -import me.chanjar.weixin.mp.bean.kefu.result.*; - import java.io.File; import java.util.Date; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; +import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; + /** - * 客服接口 , - * 命名采用kefu拼音的原因是: - * 其英文CustomerService如果再加上Service后缀显得有点啰嗦, - * 如果不加又显得表意不完整 + *
    + * 客服接口.
    + * 注意:命名采用kefu拼音的原因是:其英文CustomerService如果再加上Service后缀显得有点啰嗦,如果不加又显得表意不完整。
    + * 
    * * @author Binary Wang */ public interface WxMpKefuService { - /** *
        * 发送客服消息
        * 详情请见: 发送客服消息
        * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
        * 
    + * + * @throws WxErrorException 异常 */ boolean sendKefuMessage(WxMpKefuMessage message) throws WxErrorException; @@ -35,6 +41,8 @@ public interface WxMpKefuService { * 详情请见:客服管理 * 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN * + * + * @throws WxErrorException 异常 */ WxMpKfList kfList() throws WxErrorException; @@ -44,6 +52,8 @@ public interface WxMpKefuService { * 详情请见:客服管理 * 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token=ACCESS_TOKEN * + * + * @throws WxErrorException 异常 */ WxMpKfOnlineList kfOnlineList() throws WxErrorException; @@ -53,6 +63,8 @@ public interface WxMpKefuService { * 详情请见:客服管理 * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN * + * + * @throws WxErrorException 异常 */ boolean kfAccountAdd(WxMpKfAccountRequest request) throws WxErrorException; @@ -71,6 +83,8 @@ public interface WxMpKefuService { * 详情请见:客服管理 * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/inviteworker?access_token=ACCESS_TOKEN * + * + * @throws WxErrorException 异常 */ boolean kfAccountInviteWorker(WxMpKfAccountRequest request) throws WxErrorException; @@ -78,11 +92,12 @@ public interface WxMpKefuService { *
        * 上传客服头像
        * 详情请见:客服管理
    -   * 接口url格式:http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT
    +   * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT
        * 
    + * + * @throws WxErrorException 异常 */ - boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) - throws WxErrorException; + boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) throws WxErrorException; /** *
    @@ -90,6 +105,8 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile)
        * 详情请见:客服管理
        * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT
        * 
    + * + * @throws WxErrorException 异常 */ boolean kfAccountDel(String kfAccount) throws WxErrorException; @@ -102,6 +119,8 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) * 详情请见:客服会话控制接口 * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN * + * + * @throws WxErrorException 异常 */ boolean kfSessionCreate(String openid, String kfAccount) throws WxErrorException; @@ -112,6 +131,8 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) * 详情请见:客服会话控制接口 * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/close?access_token=ACCESS_TOKEN * + * + * @throws WxErrorException 异常 */ boolean kfSessionClose(String openid, String kfAccount) throws WxErrorException; @@ -122,6 +143,8 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) * 详情请见:客服会话控制接口 * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsession?access_token=ACCESS_TOKEN&openid=OPENID * + * + * @throws WxErrorException 异常 */ WxMpKfSessionGetResult kfSessionGet(String openid) throws WxErrorException; @@ -132,6 +155,8 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) * 详情请见:客服会话控制 * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsessionlist?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT * + * + * @throws WxErrorException 异常 */ WxMpKfSessionList kfSessionList(String kfAccount) throws WxErrorException; @@ -142,6 +167,8 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) * 详情请见:客服会话控制 * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getwaitcase?access_token=ACCESS_TOKEN * + * + * @throws WxErrorException 异常 */ WxMpKfSessionWaitCaseList kfSessionGetWaitCase() throws WxErrorException; @@ -160,7 +187,7 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) * @param msgId 消息id顺序从小到大,从1开始 * @param number 每次获取条数,最多10000条 * @return 聊天记录对象 - * @throws WxErrorException + * @throws WxErrorException 异常 */ WxMpKfMsgList kfMsgList(Date startTime, Date endTime, Long msgId, Integer number) throws WxErrorException; @@ -175,8 +202,27 @@ boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) * @param startTime 起始时间 * @param endTime 结束时间 * @return 聊天记录对象 - * @throws WxErrorException + * @throws WxErrorException 异常 */ WxMpKfMsgList kfMsgList(Date startTime, Date endTime) throws WxErrorException; + /** + *
    +   * 客服输入状态
    +   * 开发者可通过调用“客服输入状态”接口,返回客服当前输入状态给用户。
    +   * 此接口需要客服消息接口权限。
    +   * 如果不满足发送客服消息的触发条件,则无法下发输入状态。
    +   * 下发输入状态,需要客服之前30秒内跟用户有过消息交互。
    +   * 在输入状态中(持续15s),不可重复下发输入态。
    +   * 在输入状态中,如果向用户下发消息,会同时取消输入状态。
    +   *
    +   * 详情请见:客服输入状态
    +   * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param openid 用户id + * @param command "Typing":对用户下发“正在输入"状态 "CancelTyping":取消对用户的”正在输入"状态 + * @throws WxErrorException 异常 + */ + boolean sendKfTypingState(String openid, String command) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMarketingService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMarketingService.java new file mode 100644 index 0000000000..c7daa1f991 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMarketingService.java @@ -0,0 +1,71 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadFilter; +import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult; +import me.chanjar.weixin.mp.bean.marketing.WxMpUserAction; +import me.chanjar.weixin.mp.bean.marketing.WxMpUserActionSet; + +import java.io.IOException; +import java.util.Date; +import java.util.List; + +/** + * 微信营销接口. + * + * @author 007 + */ +public interface WxMpMarketingService { + /** + *
    +   * 创建数据源.
    +   * 接口调用请求说明
    +   * https://wximg.qq.com/wxp/pdftool/get.html?id=rkalQXDBM&pa=39
    +   * 
    + * + * @param type 用户行为源类型 + * @param name 用户行为源名称 必填 + * @param description 用户行为源描述,字段长度最小 1 字节,长度最大 128 字节 + */ + long addUserActionSets(String type, String name, String description) throws WxErrorException; + + /** + *
    +   * 获取数据源信息.
    +   * 
    + * + * @param userActionSetId 数据源唯一ID + */ + List getUserActionSets(Long userActionSetId) throws WxErrorException; + + /** + * 回传数据. + * 接口调用请求说明 + * https://wximg.qq.com/wxp/pdftool/get.html?id=rkalQXDBM&pa=39 + * + * @param actions 用户行为源类型 + */ + void addUserAction(List actions) throws WxErrorException; + + /** + *
    +   * 获取朋友圈销售线索数据接口.
    +   * 接口调用请求说明
    +   *
    +   * http请求方式: POST
    +   * http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx<o=xxx
    +   *
    +   * 
    + * + * @param beginDate 开始日期 + * @param endDate 结束日期 + * @param filtering 过滤条件 + * @param page 页码,获取指定页数据 + * @param pageSize 一页获取的数据条数(1-100) + * @return . + * @throws WxErrorException . + * @throws IOException . + */ + WxMpAdLeadResult getAdLeads(Date beginDate, Date endDate, List filtering, Integer page, Integer pageSize) + throws WxErrorException, IOException; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMassMessageService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMassMessageService.java new file mode 100644 index 0000000000..df5d03e0c9 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMassMessageService.java @@ -0,0 +1,129 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.result.WxMpMassGetResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassSpeedGetResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; + +/** + *
    + * 群发消息服务类.
    + * Created by Binary Wang on 2017-8-16.
    + * 
    + * + * @author Binary Wang + */ +public interface WxMpMassMessageService { + /** + *
    +   * 上传群发用的图文消息,上传后才能群发图文消息.
    +   *
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 
    + * + * @see #massGroupMessageSend(WxMpMassTagMessage) + * @see #massOpenIdsMessageSend(WxMpMassOpenIdsMessage) + */ + WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException; + + /** + *
    +   * 上传群发用的视频,上传后才能群发视频消息.
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 
    + * + * @see #massGroupMessageSend(WxMpMassTagMessage) + * @see #massOpenIdsMessageSend(WxMpMassOpenIdsMessage) + */ + WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException; + + /** + *
    +   * 分组群发消息.
    +   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(WxMpMassNews)} 获得media_id,然后再发送
    +   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(WxMpMassVideo)} 获得media_id,然后再发送
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 
    + */ + WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException; + + /** + *
    +   * 按openId列表群发消息.
    +   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(WxMpMassNews)} 获得media_id,然后再发送
    +   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(WxMpMassVideo)} 获得media_id,然后再发送
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 
    + */ + WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException; + + /** + *
    +   * 群发消息预览接口.
    +   * 开发者可通过该接口发送消息给指定用户,在手机端查看消息的样式和排版。为了满足第三方平台开发者的需求,
    +   * 在保留对openID预览能力的同时,增加了对指定微信号发送预览的能力,但该能力每日调用次数有限制(100次),请勿滥用。
    +   * 接口调用请求说明
    +   *  http请求方式: POST
    +   *  https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN
    +   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 
    + * + * @return wxMpMassSendResult + */ + WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws WxErrorException; + + /** + *
    +   * 删除群发.
    +   * 群发之后,随时可以通过该接口删除群发。
    +   * 请注意:
    +   * 1、只有已经发送成功的消息才能删除
    +   * 2、删除消息是将消息的图文详情页失效,已经收到的用户,还是能在其本地看到消息卡片。
    +   * 3、删除群发消息只能删除图文消息和视频消息,其他类型的消息一经发送,无法删除。
    +   * 4、如果多次群发发送的是一个图文消息,那么删除其中一次群发,就会删除掉这个图文消息也,导致所有群发都失效
    +   * 接口调用请求说明:
    +   *  http请求方式: POST
    +   *  https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token=ACCESS_TOKEN
    +   * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1481187827_i0l21
    +   * 
    + * + * @param msgId 发送出去的消息ID + * @param articleIndex 要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填0会删除全部文章 + */ + void delete(Long msgId, Integer articleIndex) throws WxErrorException; + + + /** + * 获取群发速度 + * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html#9 + */ + WxMpMassSpeedGetResult messageMassSpeedGet() throws WxErrorException; + + + /** + * 设置群发速度 + * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html#9 + * + * @param speed 群发速度的级别,是一个0到4的整数,数字越大表示群发速度越慢。 + * speed realspeed + * 0 80w/分钟 + * 1 60w/分钟 + * 2 45w/分钟 + * 3 30w/分钟 + * 4 10w/分钟 + */ + void messageMassSpeedSet(Integer speed) throws WxErrorException; + + + /** + * 查询群发消息发送状态【订阅号与服务号认证后均可用】 + * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html#%E6%9F%A5%E8%AF%A2%E7%BE%A4%E5%8F%91%E6%B6%88%E6%81%AF%E5%8F%91%E9%80%81%E7%8A%B6%E6%80%81%E3%80%90%E8%AE%A2%E9%98%85%E5%8F%B7%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%8F%B7%E8%AE%A4%E8%AF%81%E5%90%8E%E5%9D%87%E5%8F%AF%E7%94%A8%E3%80%91 + * + * @param msgId 群发消息后返回的消息id + * @return 消息发送后的状态,SEND_SUCCESS表示发送成功,SENDING表示发送中,SEND_FAIL表示发送失败,DELETE表示已删除 + */ + WxMpMassGetResult messageMassGet(Long msgId) throws WxErrorException; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java index 8c867c5fc0..998939ca84 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java @@ -1,14 +1,19 @@ package me.chanjar.weixin.mp.api; +import java.io.File; +import java.io.InputStream; + import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; import me.chanjar.weixin.mp.bean.material.WxMpMaterial; import me.chanjar.weixin.mp.bean.material.WxMpMaterialArticleUpdate; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialCountResult; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialFileBatchGetResult; import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; -import me.chanjar.weixin.mp.bean.material.*; - -import java.io.File; -import java.io.InputStream; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialNewsBatchGetResult; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; /** *
    @@ -17,9 +22,10 @@
      * 即以https://api.weixin.qq.com/cgi-bin/material
      * 和 https://api.weixin.qq.com/cgi-bin/media开头的接口
      * 
    + * + * @author Binary Wang */ public interface WxMpMaterialService { - /** *
        * 新增临时素材
    @@ -37,12 +43,13 @@ public interface WxMpMaterialService {
        *    语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
        *    视频(video):10MB,支持MP4格式
        *    缩略图(thumb):64KB,支持JPG格式
    -   *媒体文件在后台保存时间为3天,即3天后media_id失效。
    +   * 媒体文件在后台保存时间为3天,即3天后media_id失效。
        * 详情请见: 新增临时素材
        * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
        * 
    + * * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param file 文件对象 + * @param file 文件对象 * @throws WxErrorException * @see #mediaUpload(String, String, InputStream) */ @@ -75,11 +82,27 @@ public interface WxMpMaterialService { * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID * * - * @param media_id + * @param mediaId 媒体文件Id + * @return 保存到本地的临时文件 + * @throws WxErrorException + */ + File mediaDownload(String mediaId) throws WxErrorException; + + /** + *
    +   * 获取高清语音素材
    +   * 公众号可以使用本接口获取从JSSDK的uploadVoice接口上传的临时语音素材,格式为speex,16K采样率。
    +   * 该音频比上文的临时素材获取接口(格式为amr,8K采样率)更加清晰,适合用作语音识别等对音质要求较高的业务。
    +   * 详情请见: 
    +   * 获取高清语音素材
    +   * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/get/jssdk?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
    +   * 
    + * + * @param mediaId 媒体文件Id * @return 保存到本地的临时文件 * @throws WxErrorException */ - File mediaDownload(String media_id) throws WxErrorException; + File jssdkMediaDownload(String mediaId) throws WxErrorException; /** *
    @@ -106,12 +129,12 @@ public interface WxMpMaterialService {
        * 接口url格式:https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=ACCESS_TOKEN&type=TYPE
        *
        * 除了3天就会失效的临时素材外,开发者有时需要永久保存一些素材,届时就可以通过本接口新增永久素材。
    -   永久图片素材新增后,将带有URL返回给开发者,开发者可以在腾讯系域名内使用(腾讯系域名外使用,图片将被屏蔽)。
    -   请注意:
    -   1、新增的永久素材也可以在公众平台官网素材管理模块中看到
    -   2、永久素材的数量是有上限的,请谨慎新增。图文消息素材和图片素材的上限为5000,其他类型为1000
    -   3、素材的格式大小等要求与公众平台官网一致。具体是,图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式,语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式
    -   4、调用该接口需https协议
    +   * 永久图片素材新增后,将带有URL返回给开发者,开发者可以在腾讯系域名内使用(腾讯系域名外使用,图片将被屏蔽)。
    +   * 请注意:
    +   * 1、新增的永久素材也可以在公众平台官网素材管理模块中看到
    +   * 2、永久素材的数量是有上限的,请谨慎新增。图文消息素材和图片素材的上限为5000,其他类型为1000
    +   * 3、素材的格式大小等要求与公众平台官网一致。具体是,图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式,语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式
    +   * 4、调用该接口需https协议
        * 
    * * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} @@ -127,12 +150,12 @@ public interface WxMpMaterialService { * 接口url格式:https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=ACCESS_TOKEN * * 除了3天就会失效的临时素材外,开发者有时需要永久保存一些素材,届时就可以通过本接口新增永久素材。 - 永久图片素材新增后,将带有URL返回给开发者,开发者可以在腾讯系域名内使用(腾讯系域名外使用,图片将被屏蔽)。 - 请注意: - 1、新增的永久素材也可以在公众平台官网素材管理模块中看到 - 2、永久素材的数量是有上限的,请谨慎新增。图文消息素材和图片素材的上限为5000,其他类型为1000 - 3、素材的格式大小等要求与公众平台官网一致。具体是,图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式,语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式 - 4、调用该接口需https协议 + * 永久图片素材新增后,将带有URL返回给开发者,开发者可以在腾讯系域名内使用(腾讯系域名外使用,图片将被屏蔽)。 + * 请注意: + * 1、新增的永久素材也可以在公众平台官网素材管理模块中看到 + * 2、永久素材的数量是有上限的,请谨慎新增。图文消息素材和图片素材的上限为5000,其他类型为1000 + * 3、素材的格式大小等要求与公众平台官网一致。具体是,图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式,语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式 + * 4、调用该接口需https协议 * * * @param news 上传的图文消息, 请看{@link WxMpMaterialNews} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java new file mode 100644 index 0000000000..07572bb500 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java @@ -0,0 +1,125 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.card.CardUpdateResult; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardActivateUserFormRequest; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardActivateUserFormResult; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardUpdateRequest; +import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; +import me.chanjar.weixin.mp.bean.card.membercard.*; + +/** + * 会员卡相关接口. + * + * @author YuJian(mgcnrx11 @ gmail.com) + * @author yuanqixun + * @version 2017/7/8 + * @date 2018-08-30 + */ +public interface WxMpMemberCardService { + /** + * 得到WxMpService. + * + * @return WxMpService + */ + WxMpService getWxMpService(); + + /** + * 会员卡创建接口. + * + * @param createJson 会员卡json字符串 + * @return 返回json字符串 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException; + + /** + * 会员卡创建接口 + * + * @param createMessageMessage 会员卡创建对象 + * @return 会员卡信息的结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException; + + /** + * 会员卡激活接口. + * + * @param activatedMessage 激活所需参数 + * @return 会员卡激活后的json字符串 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException; + + /** + * 拉取会员信息接口. + * + * @param cardId 会员卡的CardId,微信分配 + * @param code 领取会员的会员卡Code + * @return 会员信息的结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException; + + /** + * 当会员持卡消费后,支持开发者调用该接口更新会员信息. + * 会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 + * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。 + * 同时传入add_bonus和bonus时 add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 + * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 + * + * @param updateUserMessage 更新会员信息所需字段消息 + * @return 调用返回的JSON字符串。 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) throws WxErrorException; + + /** + * 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要). + * + * @param userFormRequest 会员卡激活字段对象 + * @return 会员卡激活后结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException; + + /** + * 获取会员卡开卡插件参数(跳转型开卡组件需要参数). + * + * @param cardId 会员卡的CardId,微信分配 + * @param outStr 会员卡设置商户的渠道 + * @return 会员卡开卡插件参数结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + ActivatePluginParam getActivatePluginParam(String cardId, String outStr) throws WxErrorException; + + /** + * 获取开卡组件链接接口 + * + * @param cardId 会员卡的CardId,微信分配 + * @param outStr 会员卡设置商户的渠道 + * @return 会员卡开卡插件参数结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + String getActivatePluginUrl(String cardId, String outStr) throws WxErrorException; + + /** + * 更新会员卡信息. + * + * @param memberCardUpdateRequest 会员卡更新对象 + * @return 会员卡更新后结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateRequest) throws WxErrorException; + + /** + * 解析跳转型开卡字段用户提交的资料. + * 开发者在URL上截取ticket后须先进行urldecode + * + * @param activateTicket 用户提交的资料 + * @return 开卡字段的会员信息对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + WxMpMemberCardActivateTempInfoResult getActivateTempInfo(String activateTicket) throws WxErrorException; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java index f1c9fd47d9..e7cef4ebb3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java @@ -1,17 +1,16 @@ package me.chanjar.weixin.mp.api; import me.chanjar.weixin.common.bean.menu.WxMenu; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; import me.chanjar.weixin.mp.bean.menu.WxMpMenu; /** - * 菜单相关操作接口 + * 菜单相关操作接口. * * @author Binary Wang */ public interface WxMpMenuService { - /** *
        * 自定义菜单创建接口
    @@ -19,6 +18,7 @@ public interface WxMpMenuService {
        * 如果要创建个性化菜单,请设置matchrule属性
        * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN
        * 
    + * * @return 如果是个性化菜单,则返回menuid,否则返回null */ String menuCreate(WxMenu menu) throws WxErrorException; @@ -30,6 +30,7 @@ public interface WxMpMenuService { * 如果要创建个性化菜单,请设置matchrule属性 * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN * + * * @return 如果是个性化菜单,则返回menuid,否则返回null */ String menuCreate(String json) throws WxErrorException; @@ -74,16 +75,16 @@ public interface WxMpMenuService { *
        * 获取自定义菜单配置接口
        * 本接口将会提供公众号当前使用的自定义菜单的配置,如果公众号是通过API调用设置的菜单,则返回菜单的开发配置,而如果公众号是在公众平台官网通过网站功能发布菜单,则本接口返回运营者设置的菜单配置。
    -     请注意:
    -     1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自定义菜单配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。
    -     2、本接口与自定义菜单查询接口的不同之处在于,本接口无论公众号的接口是如何设置的,都能查询到接口,而自定义菜单查询接口则仅能查询到使用API设置的菜单配置。
    -     3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。
    -     4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。
    -     5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。
    +   * 请注意:
    +   * 1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自定义菜单配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。
    +   * 2、本接口与自定义菜单查询接口的不同之处在于,本接口无论公众号的接口是如何设置的,都能查询到接口,而自定义菜单查询接口则仅能查询到使用API设置的菜单配置。
    +   * 3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。
    +   * 4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。
    +   * 5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。
        *  接口调用请求说明:
    -        http请求方式: GET(请使用https协议)
    -        https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN
    -   *
    + * http请求方式: GET(请使用https协议) + * https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN + * */ WxMpGetSelfMenuInfoResult getSelfMenuInfo() throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMerchantInvoiceService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMerchantInvoiceService.java new file mode 100644 index 0000000000..294fba85bc --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMerchantInvoiceService.java @@ -0,0 +1,85 @@ +package me.chanjar.weixin.mp.api; + + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.invoice.merchant.*; + +/** + * 商户电子发票相关的接口 + *

    + * 重要!!!, 根据不同开票平台, 以下错误码可能开票成功(开票,冲红), 内部暂时未处理: + * 73105: 开票平台开票中,请使用相同的发票请求流水号重试开票 + * 73107: 发票请求流水正在被处理,请通过查询接口获取结果 + * 73100: 开票平台错误 + *

    + * 流程文档: https://developers.weixin.qq.com/doc/offiaccount/WeChat_Invoice/E_Invoice/Vendor_and_Invoicing_Platform_Mode_Instruction.html + * 接口文档: https://developers.weixin.qq.com/doc/offiaccount/WeChat_Invoice/E_Invoice/Vendor_API_List.html + */ +public interface WxMpMerchantInvoiceService { + + /** + * 获取开票授权页链接 + */ + InvoiceAuthPageResult getAuthPageUrl(InvoiceAuthPageRequest params) throws WxErrorException; + + /** + * 获得用户授权数据 + */ + InvoiceAuthDataResult getAuthData(InvoiceAuthDataRequest params) throws WxErrorException; + + /** + * 拒绝开票 + *

    + * 场景: 用户授权填写数据无效 + * 结果: 用户会收到一条开票失败提示 + */ + void rejectInvoice(InvoiceRejectRequest params) throws WxErrorException; + + /** + * 开具电子发票 + */ + void makeOutInvoice(MakeOutInvoiceRequest params) throws WxErrorException; + + /** + * 发票冲红 + */ + void clearOutInvoice(ClearOutInvoiceRequest params) throws WxErrorException; + + /** + * 查询发票信息 + * + * @param fpqqlsh 发票请求流水号 + * @param nsrsbh 纳税人识别号 + */ + InvoiceResult queryInvoiceInfo(String fpqqlsh, String nsrsbh) throws WxErrorException; + + /** + * 设置商户联系方式, 获取授权链接前需要设置商户联系信息 + */ + void setMerchantContactInfo(MerchantContactInfo contact) throws WxErrorException; + + /** + * 获取商户联系方式 + */ + MerchantContactInfo getMerchantContactInfo() throws WxErrorException; + + /** + * 配置授权页面字段 + */ + void setAuthPageSetting(InvoiceAuthPageSetting authPageSetting) throws WxErrorException; + + /** + * 获取授权页面配置 + */ + InvoiceAuthPageSetting getAuthPageSetting() throws WxErrorException; + + /** + * 设置商户开票平台信息 + */ + void setMerchantInvoicePlatform(MerchantInvoicePlatformInfo merchantInvoicePlatformInfo) throws WxErrorException; + + /** + * 获取商户开票平台信息 + */ + MerchantInvoicePlatformInfo getMerchantInvoicePlatform(MerchantInvoicePlatformInfo merchantInvoicePlatformInfo) throws WxErrorException; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java index 5a336bc8d9..0c6912c38d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; @@ -8,18 +8,21 @@ import java.util.Map; /** - * 处理微信推送消息的处理器接口 + * 处理微信推送消息的处理器接口. * * @author Daniel Qian */ public interface WxMpMessageHandler { /** - * @param wxMessage + * 处理微信推送消息. + * + * @param wxMessage 微信推送消息 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 - * @param wxMpService - * @param sessionManager + * @param wxMpService 服务类 + * @param sessionManager session管理器 * @return xml格式的消息,如果在异步规则里处理的话,可以返回null + * @throws WxErrorException 异常 */ WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java index 15223895b9..f377b036d1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageMatcher.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageMatcher.java index 812992c9b1..fd522c7730 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageMatcher.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageMatcher.java @@ -9,6 +9,7 @@ public interface WxMpMessageMatcher { /** * 消息是否匹配某种模式 + * * @param message */ boolean match(WxMpXmlMessage message); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java index 24dc4451a1..98ef7716f9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java @@ -1,12 +1,15 @@ package me.chanjar.weixin.mp.api; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,15 +49,12 @@ * router.route(message); * * - * @author Daniel Qian * + * @author Daniel Qian */ public class WxMpMessageRouter { - - protected final Logger log = LoggerFactory.getLogger(WxMpMessageRouter.class); - private static final int DEFAULT_THREAD_POOL_SIZE = 100; - + protected final Logger log = LoggerFactory.getLogger(WxMpMessageRouter.class); private final List rules = new ArrayList<>(); private final WxMpService wxMpService; @@ -75,12 +75,30 @@ public WxMpMessageRouter(WxMpService wxMpService) { this.exceptionHandler = new LogExceptionHandler(); } + /** + * 使用自定义的 {@link ExecutorService}. + */ + public WxMpMessageRouter(WxMpService wxMpService, ExecutorService executorService) { + this.wxMpService = wxMpService; + this.executorService = executorService; + this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); + this.sessionManager = new StandardSessionManager(); + this.exceptionHandler = new LogExceptionHandler(); + } + + /** + * 如果使用默认的 {@link ExecutorService},则系统退出前,应该调用该方法. + */ + public void shutDownExecutorService() { + this.executorService.shutdown(); + } + + /** *

        * 设置自定义的 {@link ExecutorService}
        * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
        * 
    - * @param executorService */ public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; @@ -91,7 +109,6 @@ public void setExecutorService(ExecutorService executorService) { * 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} * - * @param messageDuplicateChecker */ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { this.messageDuplicateChecker = messageDuplicateChecker; @@ -102,7 +119,6 @@ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicat * 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager} * - * @param sessionManager */ public void setSessionManager(WxSessionManager sessionManager) { this.sessionManager = sessionManager; @@ -113,7 +129,6 @@ public void setSessionManager(WxSessionManager sessionManager) { * 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} * - * @param exceptionHandler */ public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; @@ -124,18 +139,35 @@ List getRules() { } /** - * 开始一个新的Route规则 + * 开始一个新的Route规则. */ public WxMpMessageRouterRule rule() { return new WxMpMessageRouterRule(this); } /** - * 处理微信消息 - * @param wxMessage + * 处理微信消息. */ - public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { - if (isDuplicateMessage(wxMessage)) { + public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map context) { + return route(wxMessage, context, null); + } + + /** + * 处理不同appid微信消息 + */ + public WxMpXmlOutMessage route(final String appid, final WxMpXmlMessage wxMessage, final Map context) { + return route(wxMessage, context, this.wxMpService.switchoverTo(appid)); + } + + /** + * 处理微信消息. + */ + public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map context, WxMpService wxMpService) { + if (wxMpService == null) { + wxMpService = this.wxMpService; + } + final WxMpService mpService = wxMpService; + if (isMsgDuplicated(wxMessage)) { // 如果是重复消息,那么就不做处理 return null; } @@ -145,7 +177,7 @@ public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { for (final WxMpMessageRouterRule rule : this.rules) { if (rule.test(wxMessage)) { matchRules.add(rule); - if(!rule.isReEnter()) { + if (!rule.isReEnter()) { break; } } @@ -159,17 +191,17 @@ public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { final List> futures = new ArrayList<>(); for (final WxMpMessageRouterRule rule : matchRules) { // 返回最后一个非异步的rule的执行结果 - if(rule.isAsync()) { + if (rule.isAsync()) { futures.add( - this.executorService.submit(new Runnable() { - @Override - public void run() { - rule.service(wxMessage, WxMpMessageRouter.this.wxMpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); - } - }) + this.executorService.submit(new Runnable() { + @Override + public void run() { + rule.service(wxMessage, context, mpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); + } + }) ); } else { - res = rule.service(wxMessage, this.wxMpService, this.sessionManager, this.exceptionHandler); + res = rule.service(wxMessage, context, mpService, this.sessionManager, this.exceptionHandler); // 在同步操作结束,session访问结束 this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); sessionEndAccess(wxMessage); @@ -188,6 +220,7 @@ public void run() { sessionEndAccess(wxMessage); } catch (InterruptedException e) { WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); + Thread.currentThread().interrupt(); } catch (ExecutionException e) { WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); } @@ -198,17 +231,29 @@ public void run() { return res; } - protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) { + public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { + return this.route(wxMessage, new HashMap(2)); + } + + public WxMpXmlOutMessage route(String appid, final WxMpXmlMessage wxMessage) { + return this.route(appid, wxMessage, new HashMap(2)); + } - StringBuffer messageId = new StringBuffer(); + private boolean isMsgDuplicated(WxMpXmlMessage wxMessage) { + StringBuilder messageId = new StringBuilder(); if (wxMessage.getMsgId() == null) { messageId.append(wxMessage.getCreateTime()) .append("-").append(wxMessage.getFromUser()) - .append("-").append(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) - .append("-").append(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) - ; + .append("-").append(StringUtils.trimToEmpty(wxMessage.getEventKey())) + .append("-").append(StringUtils.trimToEmpty(wxMessage.getEvent())); } else { - messageId.append(wxMessage.getMsgId()); + messageId.append(wxMessage.getMsgId()) + .append("-").append(wxMessage.getCreateTime()) + .append("-").append(wxMessage.getFromUser()); + } + + if (StringUtils.isNotEmpty(wxMessage.getUserCardCode())) { + messageId.append("-").append(wxMessage.getUserCardCode()); } return this.messageDuplicateChecker.isDuplicate(messageId.toString()); @@ -216,15 +261,12 @@ protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) { } /** - * 对session的访问结束 - * @param wxMessage + * 对session的访问结束. */ - protected void sessionEndAccess(WxMpXmlMessage wxMessage) { - - InternalSession session = ((InternalSessionManager)this.sessionManager).findSession(wxMessage.getFromUser()); + private void sessionEndAccess(WxMpXmlMessage wxMessage) { + InternalSession session = ((InternalSessionManager) this.sessionManager).findSession(wxMessage.getFromUser()); if (session != null) { session.endAccess(); } - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java index 6859b7d77d..ad11e81b41 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java @@ -1,10 +1,11 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.common.api.WxErrorExceptionHandler; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.HashMap; @@ -26,6 +27,8 @@ public class WxMpMessageRouterRule { private String eventKey; + private String eventKeyRegex; + private String content; private String rContent; @@ -74,6 +77,14 @@ public WxMpMessageRouterRule eventKey(String eventKey) { return this; } + /** + * 如果eventKey匹配该正则表达式 + */ + public WxMpMessageRouterRule eventKeyRegex(String regex) { + this.eventKeyRegex = regex; + return this; + } + /** * 如果content等于某值 */ @@ -168,22 +179,22 @@ public WxMpMessageRouter next() { */ protected boolean test(WxMpXmlMessage wxMessage) { return - (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUser())) - && - (this.msgType == null || this.msgType.toLowerCase().equals((wxMessage.getMsgType()==null?null:wxMessage.getMsgType().toLowerCase()))) - && - (this.event == null || this.event.toLowerCase().equals((wxMessage.getEvent()==null?null:wxMessage.getEvent().toLowerCase()))) - && - (this.eventKey == null || this.eventKey.toLowerCase().equals((wxMessage.getEventKey()==null?null:wxMessage.getEventKey().toLowerCase()))) - && - (this.content == null || this.content - .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) - && - (this.rContent == null || Pattern - .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) - && - (this.matcher == null || this.matcher.match(wxMessage)) - ; + (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUser())) + && + (this.msgType == null || this.msgType.equalsIgnoreCase(wxMessage.getMsgType())) + && + (this.event == null || this.event.equalsIgnoreCase(wxMessage.getEvent())) + && + (this.eventKey == null || this.eventKey.equalsIgnoreCase(wxMessage.getEventKey())) + && + (this.eventKeyRegex == null || Pattern.matches(this.eventKeyRegex, StringUtils.trimToEmpty(wxMessage.getEventKey()))) + && + (this.content == null || this.content.equals(StringUtils.trimToNull(wxMessage.getContent()))) + && + (this.rContent == null || Pattern.matches(this.rContent, StringUtils.trimToEmpty(wxMessage.getContent()))) + && + (this.matcher == null || this.matcher.match(wxMessage)) + ; } /** @@ -193,13 +204,16 @@ protected boolean test(WxMpXmlMessage wxMessage) { * @return true 代表继续执行别的router,false 代表停止执行别的router */ protected WxMpXmlOutMessage service(WxMpXmlMessage wxMessage, - WxMpService wxMpService, - WxSessionManager sessionManager, - WxErrorExceptionHandler exceptionHandler) { + Map context, + WxMpService wxMpService, + WxSessionManager sessionManager, + WxErrorExceptionHandler exceptionHandler) { - try { + if (context == null) { + context = new HashMap<>(); + } - Map context = new HashMap<>(); + try { // 如果拦截器不通过 for (WxMpMessageInterceptor interceptor : this.interceptors) { if (!interceptor.intercept(wxMessage, context, wxMpService, sessionManager)) { @@ -211,7 +225,7 @@ protected WxMpXmlOutMessage service(WxMpXmlMessage wxMessage, WxMpXmlOutMessage res = null; for (WxMpMessageHandler handler : this.handlers) { // 返回最后handler的结果 - if(handler == null){ + if (handler == null) { continue; } res = handler.handle(wxMessage, context, wxMpService, sessionManager); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java index ba8665e946..6622159d22 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; import java.io.File; @@ -10,10 +10,10 @@ * 二维码相关操作接口 * 文档地址:https://mp.weixin.qq.com/wiki?action=doc&id=mp1443433542&t=0.9274944716856435 * + * * @author Binary Wang */ public interface WxMpQrcodeService { - /** *
        * 换取临时二维码ticket
    @@ -25,6 +25,18 @@ public interface WxMpQrcodeService {
        */
       WxMpQrCodeTicket qrCodeCreateTmpTicket(int sceneId, Integer expireSeconds) throws WxErrorException;
     
    +
    +  /**
    +   * 
    +   * 换取临时二维码ticket
    +   * 详情请见: 生成带参数的二维码
    +   * 
    + * + * @param sceneStr 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64 + * @param expireSeconds 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。 + */ + WxMpQrCodeTicket qrCodeCreateTmpTicket(String sceneStr, Integer expireSeconds) throws WxErrorException; + /** *
        * 换取永久二维码ticket
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
    index cf1401a571..aa7a872f39 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
    @@ -1,348 +1,676 @@
     package me.chanjar.weixin.mp.api;
     
    +import me.chanjar.weixin.common.api.WxImgProcService;
    +import me.chanjar.weixin.common.api.WxOcrService;
     import me.chanjar.weixin.common.bean.WxJsapiSignature;
    -import me.chanjar.weixin.common.exception.WxErrorException;
    +import me.chanjar.weixin.common.bean.WxNetCheckResult;
    +import me.chanjar.weixin.common.enums.TicketType;
    +import me.chanjar.weixin.common.error.WxErrorException;
    +import me.chanjar.weixin.common.service.WxService;
    +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
     import me.chanjar.weixin.common.util.http.RequestExecutor;
    -import me.chanjar.weixin.mp.bean.*;
    -import me.chanjar.weixin.mp.bean.result.*;
    -import org.apache.http.HttpHost;
    +import me.chanjar.weixin.common.util.http.RequestHttp;
    +import me.chanjar.weixin.mp.bean.WxMpSemanticQuery;
    +import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo;
    +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
    +import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult;
    +import me.chanjar.weixin.mp.bean.result.WxMpUser;
    +import me.chanjar.weixin.mp.config.WxMpConfigStorage;
    +import me.chanjar.weixin.mp.enums.WxMpApiUrl;
    +
    +import java.util.Map;
     
     /**
    - * 微信API的Service
    + * 微信公众号API的Service.
    + *
    + * @author chanjarster
      */
    -public interface WxMpService {
    -
    +public interface WxMpService extends WxService {
       /**
        * 
    -   * 验证消息的确来自微信服务器
    +   * 验证消息的确来自微信服务器.
        * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
        * 
    + * + * @param timestamp 时间戳 + * @param nonce 随机串 + * @param signature 签名 + * @return 是否验证通过 */ boolean checkSignature(String timestamp, String nonce, String signature); /** - * 获取access_token, 不强制刷新access_token + * 获取access_token, 不强制刷新access_token. * + * @return token + * @throws WxErrorException . * @see #getAccessToken(boolean) */ String getAccessToken() throws WxErrorException; /** *
    -   * 获取access_token,本方法线程安全
    +   * 获取access_token,本方法线程安全.
        * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
        *
    -   * 另:本service的所有方法都会在access_token过期是调用此方法
    +   * 另:本service的所有方法都会在access_token过期时调用此方法
        *
        * 程序员在非必要情况下尽量不要主动调用此方法
        *
        * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN
        * 
    * - * @param forceRefresh 强制刷新 + * @param forceRefresh 是否强制刷新 + * @return token + * @throws WxErrorException . */ String getAccessToken(boolean forceRefresh) throws WxErrorException; /** - * 获得jsapi_ticket,不强制刷新jsapi_ticket + * 获得ticket,不强制刷新ticket. + * + * @param type ticket 类型 + * @return ticket + * @throws WxErrorException . + * @see #getTicket(TicketType, boolean) + */ + String getTicket(TicketType type) throws WxErrorException; + + /** + *
    +   * 获得ticket.
    +   * 获得时会检查 Token是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    +   * 
    + * + * @param type ticket类型 + * @param forceRefresh 强制刷新 + * @return ticket + * @throws WxErrorException . + */ + String getTicket(TicketType type, boolean forceRefresh) throws WxErrorException; + + /** + * 获得jsapi_ticket,不强制刷新jsapi_ticket. * + * @return jsapi ticket + * @throws WxErrorException . * @see #getJsapiTicket(boolean) */ String getJsapiTicket() throws WxErrorException; /** *
    -   * 获得jsapi_ticket
    +   * 获得jsapi_ticket.
        * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
        *
        * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
        * 
    * * @param forceRefresh 强制刷新 + * @return jsapi ticket + * @throws WxErrorException . */ String getJsapiTicket(boolean forceRefresh) throws WxErrorException; /** *
    -   * 创建调用jsapi时所需要的签名
    +   * 创建调用jsapi时所需要的签名.
        *
        * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
        * 
    + * + * @param url 地址 + * @return 生成的签名对象 + * @throws WxErrorException . */ WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; /** *
    -   * 上传群发用的图文消息,上传后才能群发图文消息
    -   *
    -   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 长链接转短链接接口.
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=长链接转短链接接口
        * 
    * - * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) - * @see #massOpenIdsMessageSend(me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage) + * @param longUrl 长url + * @return 生成的短地址 + * @throws WxErrorException . */ - WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException; + String shortUrl(String longUrl) throws WxErrorException; /** *
    -   * 上传群发用的视频,上传后才能群发视频消息
    -   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 语义查询接口.
    +   * 详情请见:http://mp.weixin.qq.com/wiki/index.php?title=语义理解
        * 
    * - * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) - * @see #massOpenIdsMessageSend(me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage) - */ - WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException; - - /** - *
    -   * 分组群发消息
    -   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送
    -   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送
    -   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    -   * 
    + * @param semanticQuery 查询条件 + * @return 查询结果 + * @throws WxErrorException . */ - WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException; + WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; /** *
    -   * 按openId列表群发消息
    -   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送
    -   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送
    -   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 构造第三方使用网站应用授权登录的url.
    +   * 详情请见: 网站应用微信登录开发指南
    +   * URL格式为:https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
        * 
    + * + * @param redirectUri 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode + * @param scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 + * @param state 非必填,用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 + * @return url string */ - WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException; + String buildQrConnectUrl(String redirectUri, String scope, String state); /** *
    -   * 群发消息预览接口
    -   * 开发者可通过该接口发送消息给指定用户,在手机端查看消息的样式和排版。为了满足第三方平台开发者的需求,在保留对openID预览能力的同时,增加了对指定微信号发送预览的能力,但该能力每日调用次数有限制(100次),请勿滥用。
    -   * 接口调用请求说明
    -   *  http请求方式: POST
    -   *  https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN
    -   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
    +   * 获取微信服务器IP地址
    +   * http://mp.weixin.qq.com/wiki/0/2ad4b6bfd29f30f71d39616c2a0fcedc.html
        * 
    * - * @return wxMpMassSendResult + * @return 微信服务器ip地址数组 + * @throws WxErrorException . */ - WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception; + String[] getCallbackIP() throws WxErrorException; /** *
    -   * 长链接转短链接接口
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=长链接转短链接接口
    +   *  网络检测
    +   *  https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21541575776DtsuT
    +   *  为了帮助开发者排查回调连接失败的问题,提供这个网络检测的API。它可以对开发者URL做域名解析,然后对所有IP进行一次ping操作,得到丢包率和耗时。
        * 
    * + * @param action 执行的检测动作 + * @param operator 指定平台从某个运营商进行检测 + * @return 检测结果 + * @throws WxErrorException . */ - String shortUrl(String long_url) throws WxErrorException; + WxNetCheckResult netCheck(String action, String operator) throws WxErrorException; /** *
    -   * 语义查询接口
    -   * 详情请见:http://mp.weixin.qq.com/wiki/index.php?title=语义理解
    +   * 获取公众号的自动回复规则.
    +   * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Getting_Rules_for_Auto_Replies.html
    +   * 开发者可以通过该接口,获取公众号当前使用的自动回复规则,包括关注后自动回复、消息自动回复(60分钟内触发一次)、关键词自动回复。
    +   * 请注意:
    +   * 1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自动回复配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。
    +   * 2、本接口仅能获取公众号在公众平台官网的自动回复功能中设置的自动回复规则,若公众号自行开发实现自动回复,或通过第三方平台开发者来实现,则无法获取。
    +   * 3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。
    +   * 4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。
    +   * 5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。
    +   * 接口调用请求说明
    +   * http请求方式: GET(请使用https协议)
    +   * https://api.weixin.qq.com/cgi-bin/get_current_autoreply_info?access_token=ACCESS_TOKEN
        * 
    + * + * @return 公众号的自动回复规则 + * @throws WxErrorException . */ - WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; + WxMpCurrentAutoReplyInfo getCurrentAutoReplyInfo() throws WxErrorException; /** *
    -   * 构造第三方使用网站应用授权登录的url
    -   * 详情请见: 网站应用微信登录开发指南
    -   * URL格式为:https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
    +   *  公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零:
    +   *  HTTP调用:https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN
    +   *  接口文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433744592
    +   *
        * 
    * - * @param redirectURI 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode - * @param scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 - * @param state 非必填,用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 - * @return url + * @param appid 公众号的APPID + * @throws WxErrorException the wx error exception */ - String buildQrConnectUrl(String redirectURI, String scope, String state); + void clearQuota(String appid) throws WxErrorException; /** *
    -   * 构造oauth2授权的url连接
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息
    +   * Service没有实现某个API的时候,可以用这个,
    +   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
    +   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
        * 
    * - * @param redirectURI 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode - * @return url + * @param the type parameter + * @param the type parameter + * @param executor 执行器 + * @param url 接口地址 + * @param data 参数数据 + * @return 结果 + * @throws WxErrorException 异常 */ - String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state); + T execute(RequestExecutor executor, String url, E data) throws WxErrorException; /** - *
    -   * 用code换取oauth2的access token
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息
    -   * 
    + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. + * + * @param url 请求接口地址 + * @param queryParam 参数 + * @return 接口响应字符串 + * @throws WxErrorException 异常 */ - WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException; + String get(WxMpApiUrl url, String queryParam) throws WxErrorException; /** - *
    -   * 刷新oauth2的access token
    -   * 
    + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. + * + * @param url 请求接口地址 + * @param postData 请求参数json值 + * @return 接口响应字符串 + * @throws WxErrorException 异常 */ - WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException; + String post(WxMpApiUrl url, String postData) throws WxErrorException; /** *
    -   * 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以
    +   * Service没有实现某个API的时候,可以用这个,
    +   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
    +   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
        * 
    * - * @param lang zh_CN, zh_TW, en + * @param the type parameter + * @param the type parameter + * @param executor 执行器 + * @param url 接口地址 + * @param data 参数数据 + * @return 结果 + * @throws WxErrorException 异常 */ - WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException; + T execute(RequestExecutor executor, WxMpApiUrl url, E data) throws WxErrorException; /** - *
    -   * 验证oauth2的access token是否有效
    -   * 
    + * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试. * + * @param retrySleepMillis 默认:1000ms */ - boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); + void setRetrySleepMillis(int retrySleepMillis); /** *
    -   * 获取微信服务器IP地址
    -   * http://mp.weixin.qq.com/wiki/0/2ad4b6bfd29f30f71d39616c2a0fcedc.html
    +   * 设置当微信系统响应系统繁忙时,最大重试次数.
    +   * 默认:5次
        * 
    + * + * @param maxRetryTimes 最大重试次数 */ - String[] getCallbackIP() throws WxErrorException; + void setMaxRetryTimes(int maxRetryTimes); /** - * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 + * 获取WxMpConfigStorage 对象. + * + * @return WxMpConfigStorage */ - String get(String url, String queryParam) throws WxErrorException; + WxMpConfigStorage getWxMpConfigStorage(); /** - * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 + * 设置 {@link WxMpConfigStorage} 的实现. 兼容老版本 + * + * @param wxConfigProvider . */ - String post(String url, String postData) throws WxErrorException; + void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); /** - *
    -   * Service没有实现某个API的时候,可以用这个,
    -   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
    -   * 可以参考,{@link me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor}的实现方法
    -   * 
    + * Map里 加入新的 {@link WxMpConfigStorage},适用于动态添加新的微信公众号配置. + * + * @param mpId 公众号id + * @param configStorage 新的微信配置 */ - T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; + void addConfigStorage(String mpId, WxMpConfigStorage configStorage); /** - * 获取代理对象 + * 从 Map中 移除 {@link String mpId} 所对应的 {@link WxMpConfigStorage},适用于动态移除微信公众号配置. + * + * @param mpId 对应公众号的标识 */ - HttpHost getHttpProxy(); + void removeConfigStorage(String mpId); /** - * 注入 {@link WxMpConfigStorage} 的实现 + * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String mpId} 值 + * 随机采用一个{@link String mpId}进行Http初始化操作 + * + * @param configStorages WxMpConfigStorage map */ - void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); + void setMultiConfigStorages(Map configStorages); /** - *
    -   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
    -   * 默认:1000ms
    -   * 
    + * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String label} 值 + * + * @param configStorages WxMpConfigStorage map + * @param defaultMpId 设置一个{@link WxMpConfigStorage} 所对应的{@link String mpId}进行Http初始化 */ - void setRetrySleepMillis(int retrySleepMillis); + void setMultiConfigStorages(Map configStorages, String defaultMpId); /** - *
    -   * 设置当微信系统响应系统繁忙时,最大重试次数
    -   * 默认:5次
    -   * 
    + * 进行相应的公众号切换. + * + * @param mpId 公众号标识 + * @return 切换是否成功 */ - void setMaxRetryTimes(int maxRetryTimes); + boolean switchover(String mpId); /** - * 获取WxMpConfigStorage 对象 + * 进行相应的公众号切换. * - * @return WxMpConfigStorage + * @param mpId 公众号标识 + * @return 切换成功,则返回当前对象,方便链式调用,否则抛出异常 */ - WxMpConfigStorage getWxMpConfigStorage(); + WxMpService switchoverTo(String mpId); /** - * 返回客服接口方法实现类,以方便调用其各个接口 + * 返回客服接口方法实现类,以方便调用其各个接口. * * @return WxMpKefuService */ WxMpKefuService getKefuService(); /** - * 返回素材相关接口方法的实现类对象,以方便调用其各个接口 + * 返回素材相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpMaterialService */ WxMpMaterialService getMaterialService(); /** - * 返回菜单相关接口方法的实现类对象,以方便调用其各个接口 + * 返回菜单相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpMenuService */ WxMpMenuService getMenuService(); /** - * 返回用户相关接口方法的实现类对象,以方便调用其各个接口 + * 返回用户相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpUserService */ WxMpUserService getUserService(); /** - * 返回用户标签相关接口方法的实现类对象,以方便调用其各个接口 + * 返回用户标签相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpUserTagService */ WxMpUserTagService getUserTagService(); /** - * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口 + * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpQrcodeService */ WxMpQrcodeService getQrcodeService(); /** - * 返回卡券相关接口方法的实现类对象,以方便调用其各个接口 + * 返回卡券相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpCardService */ WxMpCardService getCardService(); /** - * 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口 + * 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpDataCubeService */ WxMpDataCubeService getDataCubeService(); /** - * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各个接口 + * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpUserBlacklistService */ WxMpUserBlacklistService getBlackListService(); /** - * 返回门店管理相关接口方法的实现类对象,以方便调用其各个接口 + * 返回门店管理相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpStoreService */ WxMpStoreService getStoreService(); /** - * 返回模板消息相关接口方法的实现类对象,以方便调用其各个接口 + * 返回模板消息相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpTemplateMsgService */ WxMpTemplateMsgService getTemplateMsgService(); /** - * 返回硬件平台相关接口方法的实现类对象,以方便调用其各个接口 + * 返回一次性订阅消息相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpSubscribeMsgService + */ + WxMpSubscribeMsgService getSubscribeMsgService(); + + /** + * 返回硬件平台相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpDeviceService */ WxMpDeviceService getDeviceService(); + + /** + * 返回摇一摇周边相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpShakeService + */ + WxMpShakeService getShakeService(); + + /** + * 返回会员卡相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpMemberCardService + */ + WxMpMemberCardService getMemberCardService(); + + /** + * 返回营销相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpMarketingService + */ + WxMpMarketingService getMarketingService(); + + /** + * 初始化http请求对象. + */ + void initHttp(); + + /** + * 获取RequestHttp对象. + * + * @return RequestHttp对象 + */ + RequestHttp getRequestHttp(); + + /** + * 返回群发消息相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpMassMessageService + */ + WxMpMassMessageService getMassMessageService(); + + /** + * 返回AI开放接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpAiOpenService + */ + WxMpAiOpenService getAiOpenService(); + + /** + * 返回WIFI接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpWifiService + */ + WxMpWifiService getWifiService(); + + /** + * 返回WIFI接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpWifiService + */ + WxOcrService getOcrService(); + + /** + * 返回图像处理接口的实现类对象,以方便调用其各个接口. + * + * @return WxImgProcService + */ + WxImgProcService getImgProcService(); + + /** + * . + * + * @param kefuService . + */ + void setKefuService(WxMpKefuService kefuService); + + /** + * . + * + * @param materialService . + */ + void setMaterialService(WxMpMaterialService materialService); + + /** + * . + * + * @param menuService . + */ + void setMenuService(WxMpMenuService menuService); + + /** + * . + * + * @param userService . + */ + void setUserService(WxMpUserService userService); + + /** + * . + * + * @param userTagService . + */ + void setUserTagService(WxMpUserTagService userTagService); + + /** + * . + * + * @param qrcodeService . + */ + void setQrcodeService(WxMpQrcodeService qrcodeService); + + /** + * . + * + * @param cardService . + */ + void setCardService(WxMpCardService cardService); + + /** + * . + * + * @param storeService . + */ + void setStoreService(WxMpStoreService storeService); + + /** + * . + * + * @param dataCubeService . + */ + void setDataCubeService(WxMpDataCubeService dataCubeService); + + /** + * . + * + * @param blackListService . + */ + void setBlackListService(WxMpUserBlacklistService blackListService); + + /** + * . + * + * @param templateMsgService . + */ + void setTemplateMsgService(WxMpTemplateMsgService templateMsgService); + + /** + * . + * + * @param deviceService . + */ + void setDeviceService(WxMpDeviceService deviceService); + + /** + * . + * + * @param shakeService . + */ + void setShakeService(WxMpShakeService shakeService); + + /** + * . + * + * @param memberCardService . + */ + void setMemberCardService(WxMpMemberCardService memberCardService); + + /** + * . + * + * @param massMessageService . + */ + void setMassMessageService(WxMpMassMessageService massMessageService); + + /** + * . + * + * @param aiOpenService . + */ + void setAiOpenService(WxMpAiOpenService aiOpenService); + + /** + * . + * + * @param marketingService . + */ + void setMarketingService(WxMpMarketingService marketingService); + + /** + * . + * + * @param ocrService . + */ + void setOcrService(WxOcrService ocrService); + + /** + * . + * + * @param imgProcService . + */ + void setImgProcService(WxImgProcService imgProcService); + + /** + * 返回评论数据管理接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpWifiService + */ + WxMpCommentService getCommentService(); + + /** + * . + * + * @param commentService . + */ + void setCommentService(WxMpCommentService commentService); + + /** + * Gets oauth2 service. + * + * @return the oauth2 service + */ + WxOAuth2Service getOAuth2Service(); + + /** + * Sets oauth2Service. + * + * @param oAuth2Service the o auth 2 service + */ + void setOAuth2Service(WxOAuth2Service oAuth2Service); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpShakeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpShakeService.java new file mode 100644 index 0000000000..8c45dadea0 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpShakeService.java @@ -0,0 +1,60 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.WxMpShakeInfoResult; +import me.chanjar.weixin.mp.bean.WxMpShakeQuery; +import me.chanjar.weixin.mp.bean.shake.*; + +/** + * 摇一摇周边的相关接口. + * + * @author rememberber + */ +public interface WxMpShakeService { + /** + *
    +   * 获取设备及用户信息
    + * 获取设备信息,包括UUID、major、minor,以及距离、openID等信息。 + * 详情请见: https://mp.weixin.qq.com/wiki?action=doc&id=mp1443447963 + * http请求方式: POST(请使用https协议) + * 接口地址:https://api.weixin.qq.com/shakearound/user/getshakeinfo?access_token=ACCESS_TOKE + *
    + * + * @param wxMpShakeQuery 查询参数 + */ + WxMpShakeInfoResult getShakeInfo(WxMpShakeQuery wxMpShakeQuery) throws WxErrorException; + + /** + *
    +   * 页面管理
    + * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1459246752 + *
    + * @param shakeAroundPageAddQuery + * @return + * @throws WxErrorException + */ + WxMpShakeAroundPageAddResult pageAdd(WxMpShakeAroundPageAddQuery shakeAroundPageAddQuery) throws WxErrorException; + + /** + *
    +   * 配置设备与页面的关联关系
    + * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1459301931 + *
    + * @param shakeAroundDeviceBindPageQuery + * @return + * @throws WxErrorException + */ + WxError deviceBindPageQuery(WxMpShakeAroundDeviceBindPageQuery shakeAroundDeviceBindPageQuery) throws WxErrorException; + + /** + *
    +   * 查询设备与页面的关联关系
    + * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443447914 + *
    + * @param shakeAroundRelationSearchQuery + * @return + * @throws WxErrorException + */ + WxMpShakeAroundRelationSearchResult relationSearch(WxMpShakeAroundRelationSearchQuery shakeAroundRelationSearchQuery) throws WxErrorException; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpStoreService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpStoreService.java index c214c46d9e..82eaa5eeb5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpStoreService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpStoreService.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.store.WxMpStoreBaseInfo; import me.chanjar.weixin.mp.bean.store.WxMpStoreInfo; import me.chanjar.weixin.mp.bean.store.WxMpStoreListResult; @@ -8,11 +8,12 @@ import java.util.List; /** - * 门店管理的相关接口代码 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016-09-23. + * 门店管理的相关接口代码. + * Created by Binary Wang on 2016-09-23. + * + * @author Binary Wang */ -public interface WxMpStoreService { +public interface WxMpStoreService { /** *
        * 创建门店
    @@ -21,9 +22,8 @@ public interface WxMpStoreService {
        * 创建门店接口调用成功后会返回errcode 0、errmsg ok,但不会实时返回poi_id。
        * 成功创建后,会生成poi_id,但该id不一定为最终id。门店信息会经过审核,审核通过后方可获取最终poi_id,该id为门店的唯一id,强烈建议自行存储审核通过后的最终poi_id,并为后续调用使用。
        * 详情请见: 微信门店接口
    -   * 接口格式: http://api.weixin.qq.com/cgi-bin/poi/addpoi?access_token=TOKEN
    +   * 接口格式: https://api.weixin.qq.com/cgi-bin/poi/addpoi?access_token=TOKEN
        * 
    - * */ void add(WxMpStoreBaseInfo request) throws WxErrorException; @@ -35,10 +35,10 @@ public interface WxMpStoreService { * 最终结果会在5 个工作日内,最终确认是否采纳,并前端生效(但该扩展字段的采纳过程不影响门店的可用性,即available_state仍为审核通过状态) * 注:扩展字段为公共编辑信息(大家都可修改),修改将会审核,并决定是否对修改建议进行采纳,但不会影响该门店的生效可用状态。 * 详情请见: 微信门店接口 - * 接口格式:http://api.weixin.qq.com/cgi-bin/poi/getpoi?access_token=TOKEN + * 接口格式:https://api.weixin.qq.com/cgi-bin/poi/getpoi?access_token=TOKEN *
    - * @param poiId 门店Id - * @throws WxErrorException + * + * @param poiId 门店Id */ WxMpStoreBaseInfo get(String poiId) throws WxErrorException; @@ -47,10 +47,10 @@ public interface WxMpStoreService { * 删除门店 * 商户可以通过该接口,删除已经成功创建的门店。请商户慎重调用该接口。 * 详情请见: 微信门店接口 - * 接口格式:http://api.weixin.qq.com/cgi-bin/poi/delpoi?access_token=TOKEN + * 接口格式:https://api.weixin.qq.com/cgi-bin/poi/delpoi?access_token=TOKEN *
    - * @param poiId 门店Id - * @throws WxErrorException + * + * @param poiId 门店Id */ void delete(String poiId) throws WxErrorException; @@ -59,11 +59,11 @@ public interface WxMpStoreService { * 查询门店列表(指定查询起始位置和个数) * 商户可以通过该接口,批量查询自己名下的门店list,并获取已审核通过的poi_id(所有状态均会返回poi_id,但该poi_id不一定为最终id)、商户自身sid 用于对应、商户名、分店名、地址字段。 * 详情请见: 微信门店接口 - * 接口格式:http://api.weixin.qq.com/cgi-bin/poi/getpoilist?access_token=TOKEN + * 接口格式:https://api.weixin.qq.com/cgi-bin/poi/getpoilist?access_token=TOKEN * + * * @param begin 开始位置,0 即为从第一条开始查询 * @param limit 返回数据条数,最大允许50,默认为20 - * @throws WxErrorException */ WxMpStoreListResult list(int begin, int limit) throws WxErrorException; @@ -72,9 +72,8 @@ public interface WxMpStoreService { * 查询门店列表(所有) * 商户可以通过该接口,批量查询自己名下的门店list,并获取已审核通过的poi_id(所有状态均会返回poi_id,但该poi_id不一定为最终id)、商户自身sid 用于对应、商户名、分店名、地址字段。 * 详情请见: 微信门店接口 - * 接口格式:http://api.weixin.qq.com/cgi-bin/poi/getpoilist?access_token=TOKEN + * 接口格式:https://api.weixin.qq.com/cgi-bin/poi/getpoilist?access_token=TOKEN * - * @throws WxErrorException */ List listAll() throws WxErrorException; @@ -83,9 +82,8 @@ public interface WxMpStoreService { * 修改门店服务信息 * 商户可以通过该接口,修改门店的服务信息,包括:sid、图片列表、营业时间、推荐、特色服务、简介、人均价格、电话8个字段(名称、坐标、地址等不可修改)修改后需要人工审核。 * 详情请见: 微信门店接口 - * 接口格式:http://api.weixin.qq.com/cgi-bin/poi/updatepoi?access_token=TOKEN + * 接口格式:https://api.weixin.qq.com/cgi-bin/poi/updatepoi?access_token=TOKEN * - * @throws WxErrorException */ void update(WxMpStoreBaseInfo info) throws WxErrorException; @@ -94,9 +92,8 @@ public interface WxMpStoreService { * 门店类目表 * 类目名称接口是为商户提供自己门店类型信息的接口。门店类目定位的越规范,能够精准的吸引更多用户,提高曝光率。 * 详情请见: 微信门店接口 - * 接口格式:http://api.weixin.qq.com/cgi-bin/poi/getwxcategory?access_token=TOKEN + * 接口格式:https://api.weixin.qq.com/cgi-bin/poi/getwxcategory?access_token=TOKEN * - * @throws WxErrorException */ List listCategories() throws WxErrorException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpSubscribeMsgService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpSubscribeMsgService.java new file mode 100644 index 0000000000..549018e63b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpSubscribeMsgService.java @@ -0,0 +1,39 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage; + +/** + *
    + * 一次性订阅消息接口
    + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB
    + * 
    + * + * @author Mklaus + * @date 2018-01-22 上午11:07 + */ +public interface WxMpSubscribeMsgService { + /** + *
    +   * 构造用户订阅一条模板消息授权的url连接
    +   * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB
    +   * 
    + * + * @param redirectURI 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode + * @param scene 重定向后会带上scene参数,开发者可以填0-10000的整形值,用来标识订阅场景值 + * @param reserved 用于保持请求和回调的状态,授权请后原样带回给第三方 (最多128字节,要求做urlencode) + * @return url + */ + String subscribeMsgAuthorizationUrl(String redirectURI, int scene, String reserved); + + /** + *
    +   * 发送一次性订阅消息
    +   * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB
    +   * 
    + * + * @return 消息Id + */ + boolean sendSubscribeMessage(WxMpSubscribeMessage message) throws WxErrorException; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java index 9b66f7223f..24c6eded72 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.template.WxMpTemplate; import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; @@ -13,11 +13,10 @@ * http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN * Created by Binary Wang on 2016-10-14. * @author miller.lin - * @author binarywang(Binary Wang) + * @author Binary Wang * */ public interface WxMpTemplateMsgService { - /** *
        * 设置所属行业
    @@ -25,7 +24,9 @@ public interface WxMpTemplateMsgService {
        * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN
        * 
    * + * @param wxMpIndustry 行业信息 * @return 是否成功 + * @throws WxErrorException . */ boolean setIndustry(WxMpTemplateIndustry wxMpIndustry) throws WxErrorException; @@ -36,6 +37,7 @@ public interface WxMpTemplateMsgService { * * * @return wxMpIndustry + * @throws WxErrorException . */ WxMpTemplateIndustry getIndustry() throws WxErrorException; @@ -45,7 +47,9 @@ public interface WxMpTemplateMsgService { * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN * * + * @param templateMessage 模板消息 * @return 消息Id + * @throws WxErrorException . */ String sendTemplateMsg(WxMpTemplateMessage templateMessage) throws WxErrorException; @@ -56,8 +60,10 @@ public interface WxMpTemplateMsgService { * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN * 接口地址格式:https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN * + * * @param shortTemplateId 模板库中模板的编号,有“TM**”和“OPENTMTM**”等形式 * @return templateId 模板Id + * @throws WxErrorException . */ String addTemplate(String shortTemplateId) throws WxErrorException; @@ -70,6 +76,7 @@ public interface WxMpTemplateMsgService { * * * @return templateId 模板Id + * @throws WxErrorException . */ List getAllPrivateTemplate() throws WxErrorException; @@ -82,6 +89,8 @@ public interface WxMpTemplateMsgService { * * * @param templateId 模板Id + * @return . + * @throws WxErrorException . */ boolean delPrivateTemplate(String templateId) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserBlacklistService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserBlacklistService.java index d93384f005..e7e2fd84fc 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserBlacklistService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserBlacklistService.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.result.WxMpUserBlacklistGetResult; import java.util.List; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java index 964991281a..00eea89e74 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java @@ -1,19 +1,19 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.WxMpUserQuery; +import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid; import me.chanjar.weixin.mp.bean.result.WxMpUser; import me.chanjar.weixin.mp.bean.result.WxMpUserList; import java.util.List; /** - * 用户管理相关操作接口 + * 用户管理相关操作接口. * * @author Binary Wang */ public interface WxMpUserService { - /** *
        * 设置用户备注名
    @@ -48,7 +48,7 @@ public interface WxMpUserService {
        * 
    * * @param openid 用户openid - * @param lang 语言,zh_CN 简体(默认),zh_TW 繁体,en 英语 + * @param lang 语言,zh_CN 简体(默认),zh_TW 繁体,en 英语 */ WxMpUser userInfo(String openid, String lang) throws WxErrorException; @@ -61,9 +61,9 @@ public interface WxMpUserService { * 接口地址:https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN * * - * @param openids 用户openid列表 + * @param openidList 用户openid列表 */ - List userInfoList(List openids) throws WxErrorException; + List userInfoList(List openidList) throws WxErrorException; /** *
    @@ -81,7 +81,9 @@ public interface WxMpUserService {
       /**
        * 
        * 获取用户列表
    -   * 公众号可通过本接口来获取帐号的关注者列表,关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。一次拉取调用最多拉取10000个关注者的OpenID,可以通过多次拉取的方式来满足需求。
    +   * 公众号可通过本接口来获取帐号的关注者列表,
    +   * 关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。
    +   * 一次拉取调用最多拉取10000个关注者的OpenID,可以通过多次拉取的方式来满足需求。
        * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840&token=&lang=zh_CN
        * http请求方式: GET(请使用https协议)
        * 接口地址:https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
    @@ -90,4 +92,18 @@ public interface WxMpUserService {
        * @param nextOpenid 可选,第一个拉取的OPENID,null为从头开始拉取
        */
       WxMpUserList userList(String nextOpenid) throws WxErrorException;
    +
    +  /**
    +   * 
    +   * 微信公众号主体变更迁移用户 openid
    +   * 详情请见: http://kf.qq.com/faq/170221aUnmmU170221eUZJNf.html
    +   * http://kf.qq.com/faq/1901177NrqMr190117nqYJze.html
    +   * http请求方式: POST
    +   * 接口地址:https://api.weixin.qq.com/cgi-bin/changeopenid?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param fromAppid 原公众号的 appid + * @param openidList 需要转换的openid,这些必须是旧账号目前关注的才行,否则会出错;一次最多100个 + */ + List changeOpenid(String fromAppid, List openidList) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java index 4c0ab7c8ba..c1549aff41 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java @@ -1,19 +1,18 @@ package me.chanjar.weixin.mp.api; -import java.util.List; - -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.tag.WxTagListUser; import me.chanjar.weixin.mp.bean.tag.WxUserTag; +import java.util.List; + /** * 用户标签管理相关接口 * Created by Binary Wang on 2016/9/2. - * @author binarywang(Binary Wang) * + * @author Binary Wang */ public interface WxMpUserTagService { - /** *
        * 创建标签
    @@ -32,7 +31,6 @@ public interface WxMpUserTagService {
        * 详情请见:用户标签管理
        * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/get?access_token=ACCESS_TOKEN
        * 
    - * */ List tagGet() throws WxErrorException; @@ -42,7 +40,6 @@ public interface WxMpUserTagService { * 详情请见:用户标签管理 * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/update?access_token=ACCESS_TOKEN *
    - * */ Boolean tagUpdate(Long tagId, String name) throws WxErrorException; @@ -52,7 +49,6 @@ public interface WxMpUserTagService { * 详情请见:用户标签管理 * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/delete?access_token=ACCESS_TOKEN *
    - * */ Boolean tagDelete(Long tagId) throws WxErrorException; @@ -62,10 +58,9 @@ public interface WxMpUserTagService { * 详情请见:用户标签管理 * 接口url格式: https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN * - * */ WxTagListUser tagListUser(Long tagId, String nextOpenid) - throws WxErrorException; + throws WxErrorException; /** *
    @@ -73,7 +68,6 @@ WxTagListUser tagListUser(Long tagId, String nextOpenid)
        * 详情请见:用户标签管理
        * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN
        * 
    - * */ boolean batchTagging(Long tagId, String[] openids) throws WxErrorException; @@ -81,9 +75,8 @@ WxTagListUser tagListUser(Long tagId, String nextOpenid) *
        * 批量为用户取消标签
        * 详情请见:用户标签管理
    -   * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN
    +   * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token=ACCESS_TOKEN
        * 
    - * */ boolean batchUntagging(Long tagId, String[] openids) throws WxErrorException; @@ -94,7 +87,8 @@ WxTagListUser tagListUser(Long tagId, String nextOpenid) * 详情请见:用户标签管理 * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=ACCESS_TOKEN * - * @return 标签Id的列表 + * + * @return 标签Id的列表 */ List userTagList(String openid) throws WxErrorException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpWifiService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpWifiService.java new file mode 100644 index 0000000000..b0876c7686 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpWifiService.java @@ -0,0 +1,66 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.wifi.WxMpWifiShopDataResult; +import me.chanjar.weixin.mp.bean.wifi.WxMpWifiShopListResult; + +/** + *
    + *  微信连接WI-FI接口.
    + *  Created by BinaryWang on 2018/6/10.
    + * 
    + * + * @author Binary Wang + */ +public interface WxMpWifiService { + /** + *
    +   * 获取Wi-Fi门店列表.
    +   * 通过此接口获取WiFi的门店列表,该列表包括公众平台的门店信息、以及添加设备后的WiFi相关信息。创建门店方法请参考“微信门店接口”。
    +   * 注:微信连Wi-Fi下的所有接口中的shop_id,必需先通过此接口获取。
    +   *
    +   * http请求方式: POST
    +   * 请求URL:https://api.weixin.qq.com/bizwifi/shop/list?access_token=ACCESS_TOKEN
    +   * 
    + * + * @param pageIndex 分页下标,默认从1开始 + * @param pageSize 每页的个数,默认10个,最大20个 + * @return 结果 + * @throws WxErrorException 异常 + */ + WxMpWifiShopListResult listShop(int pageIndex, int pageSize) throws WxErrorException; + + /** + *
    +   * 查询门店Wi-Fi信息
    +   * 通过此接口查询某一门店的详细Wi-Fi信息,包括门店内的设备类型、ssid、密码、设备数量、商家主页URL、顶部常驻入口文案。
    +   *
    +   * http请求方式: POST
    +   * 请求URL:https://api.weixin.qq.com/bizwifi/shop/get?access_token=ACCESS_TOKEN
    +   * POST数据格式:JSON
    +   * 
    + * + * @param shopId 门店ID + * @return 结果 + * @throws WxErrorException 异常 + */ + WxMpWifiShopDataResult getShopWifiInfo(int shopId) throws WxErrorException; + + /** + *
    +   * 修改门店网络信息.
    +   * 通过此接口修改门店的网络信息,包括网络名称(ssid)或密码。需注意:
    +   * 只有门店下已添加Wi-Fi网络信息,才能调用此接口修改网络信息;添加方式请参考“添加密码型设备”和"添加portal型设备”接口文档。
    +   * 网络信息修改后,密码型设备需同步修改所有设备的ssid或密码;portal型设备需修改所有设备的ssid,并按照《硬件鉴权协议接口》修改“第二步:改造移动端portal页面”中的ssid参数,否则将无法正常连网。
    +   * 文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1457435413
    +   * 
    + * + * @param shopId 门店ID + * @param oldSsid 旧的ssid + * @param ssid 无线网络设备的ssid。32个字符以内;ssid支持中文,但可能因设备兼容性问题导致显示乱码,或无法连接等问题,相关风险自行承担! 当门店下是portal型设备时,ssid必填;当门店下是密码型设备时,ssid选填,且ssid和密码必须有一个以大写字母“WX”开头 + * @param password 无线网络设备的密码。8-24个字符;不能包含中文字符; 当门店下是密码型设备时,才可填写password,且ssid和密码必须有一个以大写字母“WX”开头 + * @return 是否更新成功 + * @throws WxErrorException . + */ + boolean updateShopWifiInfo(int shopId, String oldSsid, String ssid, String password) throws WxErrorException; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxOAuth2Service.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxOAuth2Service.java new file mode 100644 index 0000000000..1fbb1f1a92 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxOAuth2Service.java @@ -0,0 +1,72 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.mp.bean.result.WxMpUser; + +/** + * oauth2 相关接口. + * + * @author Binary Wang + * @date 2020-08-08 + */ +public interface WxOAuth2Service { + /** + *
    +   * 构造oauth2授权的url连接.
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息
    +   * 
    + * + * @param redirectURI 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode + * @param scope scope + * @param state state + * @return url + */ + String buildAuthorizationUrl(String redirectURI, String scope, String state); + + /** + *
    +   * 用code换取oauth2的access token.
    +   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息
    +   * 
    + * + * @param code code + * @return token对象 + * @throws WxErrorException . + */ + WxMpOAuth2AccessToken getAccessToken(String code) throws WxErrorException; + + /** + *
    +   * 刷新oauth2的access token.
    +   * 
    + * + * @param refreshToken 刷新token + * @return 新的token对象 + * @throws WxErrorException . + */ + WxMpOAuth2AccessToken refreshAccessToken(String refreshToken) throws WxErrorException; + + /** + *
    +   * 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以.
    +   * 
    + * + * @param oAuth2AccessToken token对象 + * @param lang zh_CN, zh_TW, en + * @return 用户对象 + * @throws WxErrorException . + */ + WxMpUser getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException; + + /** + *
    +   * 验证oauth2的access token是否有效.
    +   * 
    + * + * @param oAuth2AccessToken token对象 + * @return 是否有效 + */ + boolean validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java new file mode 100644 index 0000000000..f836cebaa6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java @@ -0,0 +1,480 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.api.WxImgProcService; +import me.chanjar.weixin.common.api.WxOcrService; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.bean.WxNetCheckResult; +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.StandardSessionManager; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.common.util.DataUtils; +import me.chanjar.weixin.common.util.RandomUtils; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.http.*; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.api.*; +import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; +import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; +import me.chanjar.weixin.mp.bean.result.WxMpUser; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.enums.WxMpApiUrl; +import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.locks.Lock; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.*; + +/** + * 基础实现类. + * + * @author someone + */ +@Slf4j +public abstract class BaseWxMpServiceImpl implements WxMpService, RequestHttp { + protected WxSessionManager sessionManager = new StandardSessionManager(); + @Getter + @Setter + private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); + @Getter + @Setter + private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); + @Getter + @Setter + private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); + @Getter + @Setter + private WxMpUserService userService = new WxMpUserServiceImpl(this); + @Getter + @Setter + private WxMpUserTagService userTagService = new WxMpUserTagServiceImpl(this); + @Getter + @Setter + private WxMpQrcodeService qrcodeService = new WxMpQrcodeServiceImpl(this); + @Getter + @Setter + private WxMpCardService cardService = new WxMpCardServiceImpl(this); + @Getter + @Setter + private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); + @Getter + @Setter + private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); + @Getter + @Setter + private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); + @Getter + @Setter + private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this); + @Getter + @Setter + private final WxMpSubscribeMsgService subscribeMsgService = new WxMpSubscribeMsgServiceImpl(this); + @Getter + @Setter + private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this); + @Getter + @Setter + private WxMpShakeService shakeService = new WxMpShakeServiceImpl(this); + @Getter + @Setter + private WxMpMemberCardService memberCardService = new WxMpMemberCardServiceImpl(this); + @Getter + @Setter + private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this); + @Getter + @Setter + private WxMpAiOpenService aiOpenService = new WxMpAiOpenServiceImpl(this); + @Getter + @Setter + private final WxMpWifiService wifiService = new WxMpWifiServiceImpl(this); + @Getter + @Setter + private WxMpMarketingService marketingService = new WxMpMarketingServiceImpl(this); + @Getter + @Setter + private WxMpCommentService commentService = new WxMpCommentServiceImpl(this); + @Getter + @Setter + private WxOcrService ocrService = new WxMpOcrServiceImpl(this); + @Getter + @Setter + private WxImgProcService imgProcService = new WxMpImgProcServiceImpl(this); + @Getter + @Setter + private WxMpMerchantInvoiceService merchantInvoiceService = new WxMpMerchantInvoiceServiceImpl(this, this.cardService); + + @Getter + @Setter + private WxOAuth2Service oAuth2Service = new WxOAuth2ServiceImpl(this); + + private Map configStorageMap; + + private int retrySleepMillis = 1000; + private int maxRetryTimes = 5; + + @Override + public boolean checkSignature(String timestamp, String nonce, String signature) { + try { + return SHA1.gen(this.getWxMpConfigStorage().getToken(), timestamp, nonce) + .equals(signature); + } catch (Exception e) { + log.error("Checking signature failed, and the reason is :" + e.getMessage()); + return false; + } + } + + @Override + public String getTicket(TicketType type) throws WxErrorException { + return this.getTicket(type, false); + } + + @Override + public String getTicket(TicketType type, boolean forceRefresh) throws WxErrorException { + Lock lock = this.getWxMpConfigStorage().getTicketLock(type); + lock.lock(); + try { + if (forceRefresh) { + this.getWxMpConfigStorage().expireTicket(type); + } + + if (this.getWxMpConfigStorage().isTicketExpired(type)) { + String responseContent = execute(SimpleGetRequestExecutor.create(this), + GET_TICKET_URL.getUrl(this.getWxMpConfigStorage()) + type.getCode(), null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); + int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); + this.getWxMpConfigStorage().updateTicket(type, jsapiTicket, expiresInSeconds); + } + } finally { + lock.unlock(); + } + + return this.getWxMpConfigStorage().getTicket(type); + } + + @Override + public String getJsapiTicket() throws WxErrorException { + return this.getJsapiTicket(false); + } + + @Override + public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { + return this.getTicket(TicketType.JSAPI, forceRefresh); + } + + @Override + public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { + long timestamp = System.currentTimeMillis() / 1000; + String randomStr = RandomUtils.getRandomStr(); + String jsapiTicket = getJsapiTicket(false); + String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, + "noncestr=" + randomStr, "timestamp=" + timestamp, "url=" + url); + WxJsapiSignature jsapiSignature = new WxJsapiSignature(); + jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId()); + jsapiSignature.setTimestamp(timestamp); + jsapiSignature.setNonceStr(randomStr); + jsapiSignature.setUrl(url); + jsapiSignature.setSignature(signature); + return jsapiSignature; + } + + @Override + public String getAccessToken() throws WxErrorException { + return getAccessToken(false); + } + + @Override + public String shortUrl(String longUrl) throws WxErrorException { + if (longUrl.contains("&access_token=")) { + throw new WxErrorException(WxError.builder().errorCode(-1) + .errorMsg("要转换的网址中存在非法字符{&access_token=},会导致微信接口报错,属于微信bug,请调整地址,否则不建议使用此方法!") + .build()); + } + + JsonObject o = new JsonObject(); + o.addProperty("action", "long2short"); + o.addProperty("long_url", longUrl); + String responseContent = this.post(SHORTURL_API_URL, o.toString()); + return GsonParser.parse(responseContent).get("short_url").getAsString(); + } + + @Override + public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { + String responseContent = this.post(SEMANTIC_SEMPROXY_SEARCH_URL, semanticQuery.toJson()); + return WxMpSemanticQueryResult.fromJson(responseContent); + } + + @Override + public String buildQrConnectUrl(String redirectUri, String scope, String state) { + return String.format(QRCONNECT_URL.getUrl(this.getWxMpConfigStorage()), this.getWxMpConfigStorage().getAppId(), + URIUtil.encodeURIComponent(redirectUri), scope, StringUtils.trimToEmpty(state)); + } + + @Override + public String[] getCallbackIP() throws WxErrorException { + String responseContent = this.get(GET_CALLBACK_IP_URL, null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); + JsonArray ipList = tmpJsonObject.get("ip_list").getAsJsonArray(); + String[] ipArray = new String[ipList.size()]; + for (int i = 0; i < ipList.size(); i++) { + ipArray[i] = ipList.get(i).getAsString(); + } + return ipArray; + } + + @Override + public WxNetCheckResult netCheck(String action, String operator) throws WxErrorException { + JsonObject o = new JsonObject(); + o.addProperty("action", action); + o.addProperty("check_operator", operator); + String responseContent = this.post(NETCHECK_URL, o.toString()); + return WxNetCheckResult.fromJson(responseContent); + } + + @Override + public WxMpCurrentAutoReplyInfo getCurrentAutoReplyInfo() throws WxErrorException { + return WxMpCurrentAutoReplyInfo.fromJson(this.get(GET_CURRENT_AUTOREPLY_INFO_URL, null)); + } + + @Override + public void clearQuota(String appid) throws WxErrorException { + JsonObject o = new JsonObject(); + o.addProperty("appid", appid); + this.post(CLEAR_QUOTA_URL, o.toString()); + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(SimpleGetRequestExecutor.create(this), url, queryParam); + } + + @Override + public String get(WxMpApiUrl url, String queryParam) throws WxErrorException { + return this.get(url.getUrl(this.getWxMpConfigStorage()), queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(SimplePostRequestExecutor.create(this), url, postData); + } + + @Override + public String post(WxMpApiUrl url, String postData) throws WxErrorException { + return this.post(url.getUrl(this.getWxMpConfigStorage()), postData); + } + + @Override + public String post(String url, Object obj) throws WxErrorException { + return this.execute(SimplePostRequestExecutor.create(this), url, WxGsonBuilder.create().toJson(obj)); + } + + @Override + public T execute(RequestExecutor executor, WxMpApiUrl url, E data) throws WxErrorException { + return this.execute(executor, url.getUrl(this.getWxMpConfigStorage()), data); + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求. + */ + @Override + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + int retryTimes = 0; + do { + try { + return this.executeInternal(executor, uri, data); + } catch (WxErrorException e) { + if (retryTimes + 1 > this.maxRetryTimes) { + log.warn("重试达到最大次数【{}】", maxRetryTimes); + //最后一次重试失败后,直接抛出异常,不再等待 + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + WxError error = e.getError(); + // -1 系统繁忙, 1000ms后重试 + if (error.getErrorCode() == -1) { + int sleepMillis = this.retrySleepMillis * (1 << retryTimes); + try { + log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + Thread.sleep(sleepMillis); + } catch (InterruptedException e1) { + throw new RuntimeException(e1); + } + } else { + throw e; + } + } + } while (retryTimes++ < this.maxRetryTimes); + + log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + E dataForLog = DataUtils.handleDataWithSecret(data); + + if (uri.contains("access_token=")) { + throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); + } + + String accessToken = getAccessToken(false); + String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken; + + try { + T result = executor.execute(uriWithAccessToken, data, WxType.MP); + log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); + return result; + } catch (WxErrorException e) { + WxError error = e.getError(); + if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) { + // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token + Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); + lock.lock(); + try { + if (StringUtils.equals(this.getWxMpConfigStorage().getAccessToken(), accessToken)) { + this.getWxMpConfigStorage().expireAccessToken(); + } + } catch (Exception ex) { + this.getWxMpConfigStorage().expireAccessToken(); + } finally { + lock.unlock(); + } + if (this.getWxMpConfigStorage().autoRefreshToken()) { + log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg()); + return this.execute(executor, uri, data); + } + } + + if (error.getErrorCode() != 0) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); + throw new WxErrorException(error, e); + } + return null; + } catch (IOException e) { + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); + throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e); + } + } + + @Override + public WxMpConfigStorage getWxMpConfigStorage() { + if (this.configStorageMap.size() == 1) { + // 只有一个公众号,直接返回其配置即可 + return this.configStorageMap.values().iterator().next(); + } + + return this.configStorageMap.get(WxMpConfigStorageHolder.get()); + } + + protected String extractAccessToken(String resultContent) throws WxErrorException { + WxMpConfigStorage config = this.getWxMpConfigStorage(); + WxError error = WxError.fromJson(resultContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + config.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + return config.getAccessToken(); + } + + @Override + public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { + final String defaultMpId = wxConfigProvider.getAppId(); + this.setMultiConfigStorages(ImmutableMap.of(defaultMpId, wxConfigProvider), defaultMpId); + } + + @Override + public void setMultiConfigStorages(Map configStorages) { + this.setMultiConfigStorages(configStorages, configStorages.keySet().iterator().next()); + } + + @Override + public void setMultiConfigStorages(Map configStorages, String defaultMpId) { + this.configStorageMap = Maps.newHashMap(configStorages); + WxMpConfigStorageHolder.set(defaultMpId); + this.initHttp(); + } + + @Override + public void addConfigStorage(String mpId, WxMpConfigStorage configStorages) { + synchronized (this) { + if (this.configStorageMap == null) { + this.setWxMpConfigStorage(configStorages); + } else { + this.configStorageMap.put(mpId, configStorages); + } + } + } + + @Override + public void removeConfigStorage(String mpId) { + synchronized (this) { + if (this.configStorageMap.size() == 1) { + this.configStorageMap.remove(mpId); + log.warn("已删除最后一个公众号配置:{},须立即使用setWxMpConfigStorage或setMultiConfigStorages添加配置", mpId); + return; + } + if (WxMpConfigStorageHolder.get().equals(mpId)) { + this.configStorageMap.remove(mpId); + final String defaultMpId = this.configStorageMap.keySet().iterator().next(); + WxMpConfigStorageHolder.set(defaultMpId); + log.warn("已删除默认公众号配置,公众号【{}】被设为默认配置", defaultMpId); + return; + } + this.configStorageMap.remove(mpId); + } + } + + @Override + public WxMpService switchoverTo(String mpId) { + if (this.configStorageMap.containsKey(mpId)) { + WxMpConfigStorageHolder.set(mpId); + return this; + } + + throw new RuntimeException(String.format("无法找到对应【%s】的公众号配置信息,请核实!", mpId)); + } + + @Override + public boolean switchover(String mpId) { + if (this.configStorageMap.containsKey(mpId)) { + WxMpConfigStorageHolder.set(mpId); + return true; + } + + log.error("无法找到对应【{}】的公众号配置信息,请核实!", mpId); + return false; + } + + @Override + public void setRetrySleepMillis(int retrySleepMillis) { + this.retrySleepMillis = retrySleepMillis; + } + + @Override + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + @Override + public RequestHttp getRequestHttp() { + return this; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java new file mode 100644 index 0000000000..9c9bbe84c4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.mp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.api.WxMpAiOpenService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.enums.AiLangType; +import me.chanjar.weixin.mp.util.requestexecuter.voice.VoiceUploadRequestExecutor; + +import java.io.File; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.AiOpen.*; + +/** + *
    + *  Created by BinaryWang on 2018/6/9.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxMpAiOpenServiceImpl implements WxMpAiOpenService { + private final WxMpService wxMpService; + + @Override + public void uploadVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException { + if (lang == null) { + lang = AiLangType.zh_CN; + } + + this.wxMpService.execute(VoiceUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), + String.format(VOICE_UPLOAD_URL.getUrl(this.wxMpService.getWxMpConfigStorage()), "mp3", voiceId, lang.getCode()), + voiceFile); + } + + @Override + public String recogniseVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException { + this.uploadVoice(voiceId, lang, voiceFile); + return this.queryRecognitionResult(voiceId, lang); + } + + @Override + public String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException { + String response = this.wxMpService.post(String.format(TRANSLATE_URL.getUrl(this.wxMpService.getWxMpConfigStorage()), + langFrom.getCode(), langTo.getCode()), content); + + WxError error = WxError.fromJson(response, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return GsonParser.parse(response).get("to_content").getAsString(); + } + + @Override + public String queryRecognitionResult(String voiceId, AiLangType lang) throws WxErrorException { + if (lang == null) { + lang = AiLangType.zh_CN; + } + + final String response = this.wxMpService.get(VOICE_QUERY_RESULT_URL, + String.format("voice_id=%s&lang=%s", voiceId, lang.getCode())); + WxError error = WxError.fromJson(response, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return GsonParser.parse(response).get("result").getAsString(); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java index 2e6bf88e50..36f49acd35 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java @@ -1,116 +1,92 @@ package me.chanjar.weixin.mp.api.impl; -import java.util.Arrays; -import java.util.concurrent.locks.Lock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; +import com.google.gson.*; import com.google.gson.reflect.TypeToken; - +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.bean.WxCardApiSignature; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.RandomUtils; -import me.chanjar.weixin.common.util.crypto.SHA1; import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.api.WxMpCardService; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.bean.result.WxMpCardResult; +import me.chanjar.weixin.mp.bean.card.*; +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.mp.enums.WxMpApiUrl; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.locks.Lock; /** * Created by Binary Wang on 2016/7/27. + * + * @author BinaryWang */ +@Slf4j +@RequiredArgsConstructor public class WxMpCardServiceImpl implements WxMpCardService { + private static final Gson GSON = WxMpGsonBuilder.create(); + private final WxMpService wxMpService; - private final Logger log = LoggerFactory.getLogger(WxMpCardServiceImpl.class); - - private WxMpService wxMpService; - - public WxMpCardServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; + @Override + public WxMpService getWxMpService() { + return this.wxMpService; } - /** - * 获得卡券api_ticket,不强制刷新卡券api_ticket - * - * @return 卡券api_ticket - * @see #getCardApiTicket(boolean) - */ @Override public String getCardApiTicket() throws WxErrorException { return getCardApiTicket(false); } - /** - *
    -   * 获得卡券api_ticket
    -   * 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    -   *
    -   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
    -   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
    -   * .9F.E6.88.90.E7.AE.97.E6.B3.95
    -   * 
    - * - * @param forceRefresh 强制刷新 - * @return 卡券api_ticket - */ @Override public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { - Lock lock = wxMpService.getWxMpConfigStorage().getCardApiTicketLock(); + final TicketType type = TicketType.WX_CARD; + Lock lock = getWxMpService().getWxMpConfigStorage().getTicketLock(type); + lock.lock(); try { - lock.lock(); if (forceRefresh) { - this.wxMpService.getWxMpConfigStorage().expireCardApiTicket(); + this.getWxMpService().getWxMpConfigStorage().expireTicket(type); } - if (this.wxMpService.getWxMpConfigStorage().isCardApiTicketExpired()) { - String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; - String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + if (this.getWxMpService().getWxMpConfigStorage().isTicketExpired(type)) { + String responseContent = this.wxMpService.execute(SimpleGetRequestExecutor + .create(this.getWxMpService().getRequestHttp()), WxMpApiUrl.Card.CARD_GET_TICKET, null); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.wxMpService.getWxMpConfigStorage().updateCardApiTicket(cardApiTicket, expiresInSeconds); + this.getWxMpService().getWxMpConfigStorage().updateTicket(type, cardApiTicket, expiresInSeconds); } } finally { lock.unlock(); } - return this.wxMpService.getWxMpConfigStorage().getCardApiTicket(); - } - - /** - *
    -   * 创建调用卡券api时所需要的签名
    -   *
    -   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
    -   * .954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94
    -   * .9F.E6.88.90.E7.AE.97.E6.B3.95
    -   * 
    - * - * @param optionalSignParam 参与签名的参数数组。 - * 可以为下列字段:app_id, card_id, card_type, code, openid, location_id - *
    注意:当做wx.chooseCard调用时,必须传入app_id参与签名,否则会造成签名失败导致拉取卡券列表为空 - * @return 卡券Api签名对象 - */ - @Override - public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws - WxErrorException { + return this.getWxMpService().getWxMpConfigStorage().getTicket(type); + } + + @Override + public WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws WxErrorException { long timestamp = System.currentTimeMillis() / 1000; String nonceStr = RandomUtils.getRandomStr(); String cardApiTicket = getCardApiTicket(false); - String[] signParam = Arrays.copyOf(optionalSignParam, optionalSignParam.length + 3); - signParam[optionalSignParam.length] = String.valueOf(timestamp); - signParam[optionalSignParam.length + 1] = nonceStr; - signParam[optionalSignParam.length + 2] = cardApiTicket; - String signature = SHA1.gen(signParam); + String[] signParams = Arrays.copyOf(optionalSignParam, optionalSignParam.length + 3); + signParams[optionalSignParam.length] = String.valueOf(timestamp); + signParams[optionalSignParam.length + 1] = nonceStr; + signParams[optionalSignParam.length + 2] = cardApiTicket; + StringBuilder sb = new StringBuilder(); + Arrays.sort(signParams); + for (String a : signParams) { + sb.append(a); + } + String signature = DigestUtils.sha1Hex(sb.toString()); + WxCardApiSignature cardApiSignature = new WxCardApiSignature(); cardApiSignature.setTimestamp(timestamp); cardApiSignature.setNonceStr(nonceStr); @@ -118,69 +94,36 @@ public WxCardApiSignature createCardApiSignature(String... optionalSignParam) th return cardApiSignature; } - /** - * 卡券Code解码 - * - * @param encryptCode 加密Code,通过JSSDK的chooseCard接口获得 - * @return 解密后的Code - */ @Override public String decryptCardCode(String encryptCode) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/code/decrypt"; JsonObject param = new JsonObject(); param.addProperty("encrypt_code", encryptCode); - String responseContent = this.wxMpService.post(url, param.toString()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + String responseContent = this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_DECRYPT, param.toString()); + JsonObject tmpJsonObject = GsonParser.parse(responseContent); JsonPrimitive jsonPrimitive = tmpJsonObject.getAsJsonPrimitive("code"); return jsonPrimitive.getAsString(); } - /** - * 卡券Code查询 - * - * @param cardId 卡券ID代表一类卡券 - * @param code 单张卡券的唯一标准 - * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 - * @return WxMpCardResult对象 - */ @Override public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/code/get"; JsonObject param = new JsonObject(); param.addProperty("card_id", cardId); param.addProperty("code", code); param.addProperty("check_consume", checkConsume); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_GET, param.toString()); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, - new TypeToken() { - }.getType()); + return WxMpGsonBuilder.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); } - /** - * 卡券Code核销。核销失败会抛出异常 - * - * @param code 单张卡券的唯一标准 - * @return 调用返回的JSON字符串。 - *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 - */ @Override public String consumeCardCode(String code) throws WxErrorException { return consumeCardCode(code, null); } - /** - * 卡券Code核销。核销失败会抛出异常 - * - * @param code 单张卡券的唯一标准 - * @param cardId 当自定义Code卡券时需要传入card_id - * @return 调用返回的JSON字符串。 - *
    可用 com.google.gson.JsonParser#parse 等方法直接取JSON串中的errcode等信息。 - */ @Override public String consumeCardCode(String code, String cardId) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/code/consume"; JsonObject param = new JsonObject(); param.addProperty("code", code); @@ -188,55 +131,239 @@ public String consumeCardCode(String code, String cardId) throws WxErrorExceptio param.addProperty("card_id", cardId); } - return this.wxMpService.post(url, param.toString()); + return this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_CONSUME, param.toString()); } - /** - * 卡券Mark接口。 - * 开发者在帮助消费者核销卡券之前,必须帮助先将此code(卡券串码)与一个openid绑定(即mark住), - * 才能进一步调用核销接口,否则报错。 - * - * @param code 卡券的code码 - * @param cardId 卡券的ID - * @param openId 用券用户的openid - * @param isMark 是否要mark(占用)这个code,填写true或者false,表示占用或解除占用 - */ @Override - public void markCardCode(String code, String cardId, String openId, boolean isMark) throws - WxErrorException { - String url = "https://api.weixin.qq.com/card/code/mark"; + public void markCardCode(String code, String cardId, String openId, boolean isMark) throws WxErrorException { JsonObject param = new JsonObject(); param.addProperty("code", code); param.addProperty("card_id", cardId); param.addProperty("openid", openId); param.addProperty("is_mark", isMark); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.getWxMpService().post(WxMpApiUrl.Card.CARD_CODE_MARK, param.toString()); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - WxMpCardResult cardResult = WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, - new TypeToken() { }.getType()); - if (!cardResult.getErrorCode().equals("0")) { - this.log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg()); + WxMpCardResult cardResult = WxMpGsonBuilder.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); + if (!"0".equals(cardResult.getErrorCode())) { + log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg()); } } @Override public String getCardDetail(String cardId) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/get"; JsonObject param = new JsonObject(); param.addProperty("card_id", cardId); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(WxMpApiUrl.Card.CARD_GET, param.toString()); // 判断返回值 - JsonObject json = (new JsonParser()).parse(responseContent).getAsJsonObject(); + JsonObject json = GsonParser.parse(responseContent); String errcode = json.get("errcode").getAsString(); if (!"0".equals(errcode)) { String errmsg = json.get("errmsg").getAsString(); - WxError error = new WxError(); - error.setErrorCode(Integer.valueOf(errcode)); - error.setErrorMsg(errmsg); - throw new WxErrorException(error); + throw new WxErrorException(WxError.builder() + .errorCode(Integer.valueOf(errcode)).errorMsg(errmsg) + .build()); } return responseContent; } + + @Override + public String addTestWhiteList(String openid) throws WxErrorException { + JsonArray array = new JsonArray(); + array.add(openid); + JsonObject jsonObject = new JsonObject(); + jsonObject.add("openid", array); + return this.wxMpService.post(WxMpApiUrl.Card.CARD_TEST_WHITELIST, GSON.toJson(jsonObject)); + } + + @Override + public WxMpCardCreateResult createCard(WxMpCardCreateRequest cardCreateMessage) throws WxErrorException { + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_CREATE, GSON.toJson(cardCreateMessage)); + return WxMpCardCreateResult.fromJson(response); + } + + @Override + public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr) throws WxErrorException { + return this.createQrcodeCard(cardId, outerStr, 0); + } + + @Override + public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr, int expiresIn) throws WxErrorException { + return this.createQrcodeCard(cardId, outerStr, expiresIn, null, null, false); + } + + @Override + public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr, int expiresIn, String openid, + String code, boolean isUniqueCode) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("action_name", "QR_CARD"); + if (expiresIn > 0) { + jsonObject.addProperty("expire_seconds", expiresIn); + } + + JsonObject actionInfoJson = new JsonObject(); + JsonObject cardJson = new JsonObject(); + if (openid != null) { + cardJson.addProperty("openid", openid); + } + + if (code != null) { + cardJson.addProperty("code", code); + } + + cardJson.addProperty("is_unique_code", isUniqueCode); + cardJson.addProperty("card_id", cardId); + cardJson.addProperty("outer_str", outerStr); + actionInfoJson.add("card", cardJson); + jsonObject.add("action_info", actionInfoJson); + + return WxMpCardQrcodeCreateResult.fromJson(this.wxMpService.post(WxMpApiUrl.Card.CARD_QRCODE_CREATE, GSON.toJson(jsonObject))); + } + + @Override + public WxMpCardLandingPageCreateResult createLandingPage(WxMpCardLandingPageCreateRequest request) throws WxErrorException { + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_LANDING_PAGE_CREATE, GSON.toJson(request)); + return WxMpCardLandingPageCreateResult.fromJson(response); + } + + @Override + public String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException { + if (StringUtils.isAnyBlank(cardId, code, reason)) { + throw new WxErrorException(WxError.builder().errorCode(41012).errorMsg("参数不完整").build()); + } + JsonObject jsonRequest = new JsonObject(); + jsonRequest.addProperty("card_id", cardId); + jsonRequest.addProperty("code", code); + jsonRequest.addProperty("reason", reason); + return this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_UNAVAILABLE, GSON.toJson(jsonRequest)); + } + + @Override + public WxMpCardDeleteResult deleteCard(String cardId) throws WxErrorException { + checkCardId(cardId); + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_DELETE, param.toString()); + return WxMpCardDeleteResult.fromJson(response); + } + + + @Override + public WxMpCardCodeDepositResult cardCodeDeposit(String cardId, List codeList) throws WxErrorException { + checkCardId(cardId); + if (codeList.size() == 0 || codeList.size() > 100) { + throw new WxErrorException(WxError.builder().errorCode(40109).errorMsg("code数量为0或者code数量超过100个").build()); + } + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + param.add("code", + WxGsonBuilder.create().toJsonTree(codeList, new TypeToken>() { + }.getType()).getAsJsonArray()); + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_DEPOSIT, param.toString()); + return WxMpCardCodeDepositResult.fromJson(response); + } + + + @Override + public WxMpCardCodeDepositCountResult cardCodeDepositCount(String cardId) throws WxErrorException { + checkCardId(cardId); + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_DEPOSIT_COUNT, param.toString()); + return WxMpCardCodeDepositCountResult.fromJson(response); + } + + + @Override + public WxMpCardCodeCheckcodeResult cardCodeCheckcode(String cardId, List codeList) throws WxErrorException { + checkCardId(cardId); + if (codeList.size() == 0 || codeList.size() > 100) { + throw new WxErrorException(WxError.builder().errorCode(40109).errorMsg("code数量为0或者code数量超过100个").build()); + } + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + param.add("code", + WxGsonBuilder.create().toJsonTree(codeList, new TypeToken>() { + }.getType()).getAsJsonArray()); + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_CHECKCODE, param.toString()); + return WxMpCardCodeCheckcodeResult.fromJson(response); + } + + + @Override + public WxMpCardMpnewsGethtmlResult cardMpnewsGethtml(String cardId) throws WxErrorException { + checkCardId(cardId); + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_MPNEWS_GETHTML, param.toString()); + return WxMpCardMpnewsGethtmlResult.fromJson(response); + } + + + @Override + public void cardModifyStock(String cardId, Integer changeValue) throws WxErrorException { + checkCardId(cardId); + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + if (changeValue > 0) { + param.addProperty("increase_stock_value", changeValue); + } else { + param.addProperty("reduce_stock_value", Math.abs(changeValue)); + } + this.wxMpService.post(WxMpApiUrl.Card.CARD_MODIFY_STOCK, param.toString()); + } + + + @Override + public void cardCodeUpdate(String cardId, String oldCode, String newCode) throws WxErrorException { + checkCardId(cardId); + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + param.addProperty("code", oldCode); + param.addProperty("new_code", newCode); + this.wxMpService.post(WxMpApiUrl.Card.CARD_CODE_UPDATE, param.toString()); + } + + + @Override + public void cardPaycellSet(String cardId, Boolean isOpen) throws WxErrorException { + checkCardId(cardId); + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + param.addProperty("is_open", isOpen); + this.wxMpService.post(WxMpApiUrl.Card.CARD_PAYCELL_SET, param.toString()); + } + + + @Override + public void cardSelfConsumeCellSet(String cardId, Boolean isOpen, + Boolean needVerifyCod, Boolean needRemarkAmount) throws WxErrorException { + checkCardId(cardId); + JsonObject param = new JsonObject(); + param.addProperty("card_id", cardId); + param.addProperty("is_open", isOpen); + param.addProperty("need_verify_cod", needVerifyCod); + param.addProperty("need_remark_amount", needRemarkAmount); + this.wxMpService.post(WxMpApiUrl.Card.CARD_SELF_CONSUME_CELL_SET, param.toString()); + } + + + @Override + public WxUserCardListResult getUserCardList(String openId, String cardId) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("openid", openId); + param.addProperty("card_id", cardId); + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_USER_CARD_LIST, param.toString()); + return WxUserCardListResult.fromJson(response); + } + + + private void checkCardId(String cardId) throws WxErrorException { + if (StringUtils.isEmpty(cardId)) { + throw new WxErrorException(WxError.builder().errorCode(41012).errorMsg("cardId不能为空").build()); + } + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImpl.java new file mode 100644 index 0000000000..8f287a80f1 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImpl.java @@ -0,0 +1,99 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpCommentService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.comment.WxMpCommentListVo; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Comment.*; + +/** + * @author Binary Wang + * @date 2019-06-16 + */ +@RequiredArgsConstructor +public class WxMpCommentServiceImpl implements WxMpCommentService { + private final WxMpService wxMpService; + + @Override + public void open(String msgDataId, Integer index) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("msg_data_id", msgDataId); + if (index != null) { + json.addProperty("index", index); + } + + this.wxMpService.post(OPEN, json.toString()); + } + + @Override + public void close(String msgDataId, Integer index) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("msg_data_id", msgDataId); + if (index != null) { + json.addProperty("index", index); + } + + this.wxMpService.post(CLOSE, json.toString()); + } + + @Override + public WxMpCommentListVo list(String msgDataId, Integer index, int begin, int count, int type) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("msg_data_id", msgDataId); + json.addProperty("begin", begin); + json.addProperty("count", count); + json.addProperty("type", type); + + if (index != null) { + json.addProperty("index", index); + } + + return WxMpCommentListVo.fromJson(this.wxMpService.post(LIST, json.toString())); + } + + @Override + public void markElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + this.wxMpService.post(MARK_ELECT, json.toString()); + } + + @Override + public void unmarkElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + this.wxMpService.post(UNMARK_ELECT, json.toString()); + } + + @Override + public void delete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + + this.wxMpService.post(DELETE, json.toString()); + } + + @Override + public void replyAdd(String msgDataId, Integer index, Long userCommentId, String content) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + json.addProperty("content", content); + + this.wxMpService.post(REPLY_ADD, json.toString()); + } + + @Override + public void replyDelete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + this.wxMpService.post(REPLY_DELETE, json.toString()); + } + + private JsonObject buildJson(String msgDataId, Integer index, Long userCommentId) { + JsonObject json = new JsonObject(); + json.addProperty("msg_data_id", msgDataId); + json.addProperty("user_comment_id", userCommentId); + if (index != null) { + json.addProperty("index", index); + } + return json; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java index 2dbee076a1..a3523c0d77 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java @@ -1,207 +1,135 @@ package me.chanjar.weixin.mp.api.impl; import com.google.gson.JsonObject; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpDataCubeService; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.datacube.*; +import me.chanjar.weixin.mp.enums.WxMpApiUrl; import org.apache.commons.lang3.time.FastDateFormat; import java.text.Format; import java.util.Date; import java.util.List; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.DataCube.*; + /** - * Created by Binary Wang on 2016/8/23. + * Created by Binary Wang on 2016/8/23. + * * @author binarywang (https://github.com/binarywang) */ +@RequiredArgsConstructor public class WxMpDataCubeServiceImpl implements WxMpDataCubeService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/datacube"; - private final Format dateFormat = FastDateFormat.getInstance("yyyy-MM-dd"); - private WxMpService wxMpService; - - public WxMpDataCubeServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public List getUserSummary(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusersummary"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(GET_USER_SUMMARY, buildParams(beginDate, endDate)); return WxDataCubeUserSummary.fromJson(responseContent); } @Override public List getUserCumulate(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusercumulate"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(GET_USER_CUMULATE, buildParams(beginDate, endDate)); return WxDataCubeUserCumulate.fromJson(responseContent); } @Override public List getArticleSummary(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getarticlesummary"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_ARTICLE_SUMMARY, beginDate, endDate); } @Override public List getArticleTotal(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getarticletotal"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(GET_ARTICLE_TOTAL, buildParams(beginDate, endDate)); return WxDataCubeArticleTotal.fromJson(responseContent); } @Override public List getUserRead(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getuserread"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_USER_READ, beginDate, endDate); } @Override public List getUserReadHour(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getuserreadhour"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_USER_READ_HOUR, beginDate, endDate); } @Override public List getUserShare(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusershare"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_USER_SHARE, beginDate, endDate); } @Override public List getUserShareHour(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusersharehour"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + return this.getArticleResults(GET_USER_SHARE_HOUR, beginDate, endDate); + } + + private List getArticleResults(WxMpApiUrl url, Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(url, buildParams(beginDate, endDate)); return WxDataCubeArticleResult.fromJson(responseContent); } @Override - public List getUpstreamMsg(Date beginDate, Date endDate) - throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsg"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsg(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG, beginDate, endDate); } @Override - public List getUpstreamMsgHour(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsghour"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgHour(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_HOUR, beginDate, endDate); } @Override - public List getUpstreamMsgWeek(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgweek"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgWeek(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_WEEK, beginDate, endDate); } @Override - public List getUpstreamMsgMonth(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgmonth"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgMonth(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_MONTH, beginDate, endDate); } @Override - public List getUpstreamMsgDist(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgdist"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgDist(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_DIST, beginDate, endDate); } @Override - public List getUpstreamMsgDistWeek(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgdistweek"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgDistWeek(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_DIST_WEEK, beginDate, endDate); } @Override - public List getUpstreamMsgDistMonth(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgdistmonth"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + public List getUpstreamMsgDistMonth(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_DIST_MONTH, beginDate, endDate); + } + + private List getUpstreamMsg(WxMpApiUrl url, Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(url, buildParams(beginDate, endDate)); return WxDataCubeMsgResult.fromJson(responseContent); } @Override - public List getInterfaceSummary(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getinterfacesummary"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + public List getInterfaceSummary(Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(GET_INTERFACE_SUMMARY, buildParams(beginDate, endDate)); return WxDataCubeInterfaceResult.fromJson(responseContent); } - @Override - public List getInterfaceSummaryHour(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getinterfacesummaryhour"; + private String buildParams(Date beginDate, Date endDate) { JsonObject param = new JsonObject(); param.addProperty("begin_date", this.dateFormat.format(beginDate)); param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + return param.toString(); + } + + @Override + public List getInterfaceSummaryHour(Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(GET_INTERFACE_SUMMARY_HOUR, buildParams(beginDate, endDate)); return WxDataCubeInterfaceResult.fromJson(responseContent); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java index 5c94236511..0f7b807dea 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java @@ -1,86 +1,75 @@ package me.chanjar.weixin.mp.api.impl; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpDeviceService; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.device.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Device.*; /** * Created by keungtung on 10/12/2016. + * + * @author keungtung */ +@Slf4j +@RequiredArgsConstructor public class WxMpDeviceServiceImpl implements WxMpDeviceService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/device"; - private static Logger log = LoggerFactory.getLogger(WxMpMenuServiceImpl.class); - - private WxMpService wxMpService; - - WxMpDeviceServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public TransMsgResp transMsg(WxDeviceMsg msg) throws WxErrorException { - String url = API_URL_PREFIX + "/transmsg"; - String response = this.wxMpService.post(url, msg.toJson()); + String response = this.wxMpService.post(DEVICE_TRANSMSG, msg.toJson()); return TransMsgResp.fromJson(response); } @Override public WxDeviceQrCodeResult getQrCode(String productId) throws WxErrorException { - String url = API_URL_PREFIX + "/getqrcode"; - String response = this.wxMpService.get(url, "product_id=" + productId); + String response = this.wxMpService.get(DEVICE_GETQRCODE, "product_id=" + productId); return WxDeviceQrCodeResult.fromJson(response); } @Override public WxDeviceAuthorizeResult authorize(WxDeviceAuthorize wxDeviceAuthorize) throws WxErrorException { - String url = API_URL_PREFIX + "/authorize_device"; - String response = this.wxMpService.post(url, wxDeviceAuthorize.toJson()); + String response = this.wxMpService.post(DEVICE_AUTHORIZE_DEVICE, wxDeviceAuthorize.toJson()); return WxDeviceAuthorizeResult.fromJson(response); } @Override public WxDeviceBindResult bind(WxDeviceBind wxDeviceBind) throws WxErrorException { - String url = API_URL_PREFIX + "/bind"; - String response = this.wxMpService.post(url, wxDeviceBind.toJson()); + String response = this.wxMpService.post(DEVICE_BIND, wxDeviceBind.toJson()); return WxDeviceBindResult.fromJson(response); } @Override public WxDeviceBindResult compelBind(WxDeviceBind wxDeviceBind) throws WxErrorException { - String url = API_URL_PREFIX + "/compel_bind"; - String response = this.wxMpService.post(url, wxDeviceBind.toJson()); + String response = this.wxMpService.post(DEVICE_COMPEL_BIND, wxDeviceBind.toJson()); return WxDeviceBindResult.fromJson(response); } @Override public WxDeviceBindResult unbind(WxDeviceBind wxDeviceBind) throws WxErrorException { - String url = API_URL_PREFIX + "/unbind?"; - String response = this.wxMpService.post(url, wxDeviceBind.toJson()); + String response = this.wxMpService.post(DEVICE_UNBIND, wxDeviceBind.toJson()); return WxDeviceBindResult.fromJson(response); } @Override public WxDeviceBindResult compelUnbind(WxDeviceBind wxDeviceBind) throws WxErrorException { - String url = API_URL_PREFIX + "/compel_unbind?"; - String response = this.wxMpService.post(url, wxDeviceBind.toJson()); + String response = this.wxMpService.post(DEVICE_COMPEL_UNBIND, wxDeviceBind.toJson()); return WxDeviceBindResult.fromJson(response); } @Override public WxDeviceOpenIdResult getOpenId(String deviceType, String deviceId) throws WxErrorException { - String url = API_URL_PREFIX + "/get_openid"; - String response = this.wxMpService.get(url, "device_type=" + deviceType + "&device_id=" + deviceId); + String response = this.wxMpService.get(DEVICE_GET_OPENID, "device_type=" + deviceType + "&device_id=" + deviceId); return WxDeviceOpenIdResult.fromJson(response); } @Override public WxDeviceBindDeviceResult getBindDevice(String openId) throws WxErrorException { - String url = API_URL_PREFIX+"/get_bind_device"; - String response = this.wxMpService.get(url,"openid="+openId); + String response = this.wxMpService.get(DEVICE_GET_BIND_DEVICE, "openid=" + openId); return WxDeviceBindDeviceResult.fromJson(response); } } - diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpImgProcServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpImgProcServiceImpl.java new file mode 100644 index 0000000000..24c699657d --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpImgProcServiceImpl.java @@ -0,0 +1,109 @@ +package me.chanjar.weixin.mp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.api.WxImgProcService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcAiCropResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcQrCodeResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcSuperResolutionResult; +import me.chanjar.weixin.common.requestexecuter.ocr.OcrDiscernRequestExecutor; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.ImgProc.AI_CROP; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.ImgProc.FILE_AI_CROP; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.ImgProc.FILE_QRCODE; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.ImgProc.FILE_SUPER_RESOLUTION; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.ImgProc.QRCODE; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.ImgProc.SUPER_RESOLUTION; + +/** + * 图像处理接口实现. + * @author Theo Nie + */ +@RequiredArgsConstructor +public class WxMpImgProcServiceImpl implements WxImgProcService { + private final WxMpService wxMpService; + + @Override + public WxImgProcQrCodeResult qrCode(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + //ignore + } + + String result = this.wxMpService.get(String.format(QRCODE.getUrl(this.wxMpService.getWxMpConfigStorage()), imgUrl), + null); + return WxImgProcQrCodeResult.fromJson(result); + } + + @Override + public WxImgProcQrCodeResult qrCode(File imgFile) throws WxErrorException { + String result = this.wxMpService.execute(OcrDiscernRequestExecutor.create(this.wxMpService.getRequestHttp()), + FILE_QRCODE.getUrl(this.wxMpService.getWxMpConfigStorage()), imgFile); + return WxImgProcQrCodeResult.fromJson(result); + } + + @Override + public WxImgProcSuperResolutionResult superResolution(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + //ignore + } + + final String result = this.wxMpService.get(String.format(SUPER_RESOLUTION.getUrl(this.wxMpService.getWxMpConfigStorage()), imgUrl), null); + return WxImgProcSuperResolutionResult.fromJson(result); + } + + @Override + public WxImgProcSuperResolutionResult superResolution(File imgFile) throws WxErrorException { + String result = this.wxMpService.execute(OcrDiscernRequestExecutor.create(this.wxMpService.getRequestHttp()), + FILE_SUPER_RESOLUTION.getUrl(this.wxMpService.getWxMpConfigStorage()), imgFile); + return WxImgProcSuperResolutionResult.fromJson(result); + } + + @Override + public WxImgProcAiCropResult aiCrop(String imgUrl) throws WxErrorException { + return this.aiCrop(imgUrl, ""); + } + + @Override + public WxImgProcAiCropResult aiCrop(String imgUrl, String ratios) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + //ignore + } + + if (StringUtils.isEmpty(ratios)) { + ratios = ""; + } + + final String result = this.wxMpService.get(String.format(AI_CROP.getUrl(this.wxMpService.getWxMpConfigStorage()), + imgUrl, ratios), null); + return WxImgProcAiCropResult.fromJson(result); + } + + @Override + public WxImgProcAiCropResult aiCrop(File imgFile) throws WxErrorException { + return this.aiCrop(imgFile, ""); + } + + @Override + public WxImgProcAiCropResult aiCrop(File imgFile, String ratios) throws WxErrorException { + if (StringUtils.isEmpty(ratios)) { + ratios = ""; + } + + String result = this.wxMpService.execute(OcrDiscernRequestExecutor.create(this.wxMpService.getRequestHttp()), + String.format(FILE_AI_CROP.getUrl(this.wxMpService.getWxMpConfigStorage()), ratios), imgFile); + return WxImgProcAiCropResult.fromJson(result); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImpl.java index 09a68adf89..a131e3a9f3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImpl.java @@ -1,167 +1,134 @@ package me.chanjar.weixin.mp.api.impl; -import java.io.File; -import java.util.Date; - -import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.google.gson.JsonObject; - -import me.chanjar.weixin.common.bean.result.WxError; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.mp.api.WxMpKefuService; import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfSessionRequest; -import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; -import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; -import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; -import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; -import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; -import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; +import me.chanjar.weixin.mp.bean.kefu.result.*; + +import java.io.File; +import java.util.Date; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Kefu.*; /** - * * @author Binary Wang - * */ +@Slf4j +@RequiredArgsConstructor public class WxMpKefuServiceImpl implements WxMpKefuService { - protected final Logger log = LoggerFactory - .getLogger(WxMpKefuServiceImpl.class); - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/customservice"; - private static final String API_URL_PREFIX_WITH_CGI_BIN = "https://api.weixin.qq.com/cgi-bin/customservice"; - private WxMpService wxMpService; - - public WxMpKefuServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override - public boolean sendKefuMessage(WxMpKefuMessage message) - throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; - String responseContent = this.wxMpService.post(url, message.toJson()); + public boolean sendKefuMessage(WxMpKefuMessage message) throws WxErrorException { + String responseContent = this.wxMpService.post(MESSAGE_CUSTOM_SEND, message.toJson()); return responseContent != null; } @Override public WxMpKfList kfList() throws WxErrorException { - String url = API_URL_PREFIX_WITH_CGI_BIN + "/getkflist"; - String responseContent = this.wxMpService.get(url, null); + String responseContent = this.wxMpService.get(GET_KF_LIST, null); return WxMpKfList.fromJson(responseContent); } @Override public WxMpKfOnlineList kfOnlineList() throws WxErrorException { - String url = API_URL_PREFIX_WITH_CGI_BIN + "/getonlinekflist"; - String responseContent = this.wxMpService.get(url, null); + String responseContent = this.wxMpService.get(GET_ONLINE_KF_LIST, null); return WxMpKfOnlineList.fromJson(responseContent); } @Override - public boolean kfAccountAdd(WxMpKfAccountRequest request) - throws WxErrorException { - String url = API_URL_PREFIX + "/kfaccount/add"; - String responseContent = this.wxMpService.post(url, request.toJson()); + public boolean kfAccountAdd(WxMpKfAccountRequest request) throws WxErrorException { + String responseContent = this.wxMpService.post(KFACCOUNT_ADD, request.toJson()); return responseContent != null; } @Override - public boolean kfAccountUpdate(WxMpKfAccountRequest request) - throws WxErrorException { - String url = API_URL_PREFIX + "/kfaccount/update"; - String responseContent = this.wxMpService.post(url, request.toJson()); + public boolean kfAccountUpdate(WxMpKfAccountRequest request) throws WxErrorException { + String responseContent = this.wxMpService.post(KFACCOUNT_UPDATE, request.toJson()); return responseContent != null; } @Override public boolean kfAccountInviteWorker(WxMpKfAccountRequest request) throws WxErrorException { - String url = API_URL_PREFIX + "/kfaccount/inviteworker"; - String responseContent = this.wxMpService.post(url, request.toJson()); + String responseContent = this.wxMpService.post(KFACCOUNT_INVITE_WORKER, request.toJson()); return responseContent != null; } @Override - public boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) - throws WxErrorException { - String url = API_URL_PREFIX + "/kfaccount/uploadheadimg?kf_account=" + kfAccount; + public boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) throws WxErrorException { WxMediaUploadResult responseContent = this.wxMpService - .execute(new MediaUploadRequestExecutor(), url, imgFile); + .execute(MediaUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), + String.format(KFACCOUNT_UPLOAD_HEAD_IMG.getUrl(this.wxMpService.getWxMpConfigStorage()), kfAccount), imgFile); return responseContent != null; } @Override public boolean kfAccountDel(String kfAccount) throws WxErrorException { - String url = API_URL_PREFIX + "/kfaccount/del?kf_account=" + kfAccount; - String responseContent = this.wxMpService.get(url, null); + String responseContent = this.wxMpService.get(String.format(KFACCOUNT_DEL.getUrl(this.wxMpService.getWxMpConfigStorage()), + kfAccount), null); return responseContent != null; } @Override - public boolean kfSessionCreate(String openid, String kfAccount) - throws WxErrorException { + public boolean kfSessionCreate(String openid, String kfAccount) throws WxErrorException { WxMpKfSessionRequest request = new WxMpKfSessionRequest(kfAccount, openid); - String url = API_URL_PREFIX + "/kfsession/create"; - String responseContent = this.wxMpService.post(url, request.toJson()); + String responseContent = this.wxMpService.post(KFSESSION_CREATE, request.toJson()); return responseContent != null; } @Override - public boolean kfSessionClose(String openid, String kfAccount) - throws WxErrorException { + public boolean kfSessionClose(String openid, String kfAccount) throws WxErrorException { WxMpKfSessionRequest request = new WxMpKfSessionRequest(kfAccount, openid); - String url = API_URL_PREFIX + "/kfsession/close"; - String responseContent = this.wxMpService.post(url, request.toJson()); + String responseContent = this.wxMpService.post(KFSESSION_CLOSE, request.toJson()); return responseContent != null; } @Override - public WxMpKfSessionGetResult kfSessionGet(String openid) - throws WxErrorException { - String url = API_URL_PREFIX + "/kfsession/getsession?openid=" + openid; - String responseContent = this.wxMpService.get(url, null); + public WxMpKfSessionGetResult kfSessionGet(String openid) throws WxErrorException { + String responseContent = this.wxMpService.get(String.format(KFSESSION_GET_SESSION + .getUrl(this.wxMpService.getWxMpConfigStorage()), openid), null); return WxMpKfSessionGetResult.fromJson(responseContent); } @Override - public WxMpKfSessionList kfSessionList(String kfAccount) - throws WxErrorException { - String url = API_URL_PREFIX + "/kfsession/getsessionlist?kf_account=" + kfAccount; - String responseContent = this.wxMpService.get(url, null); + public WxMpKfSessionList kfSessionList(String kfAccount) throws WxErrorException { + String responseContent = this.wxMpService.get(String.format(KFSESSION_GET_SESSION_LIST + .getUrl(this.wxMpService.getWxMpConfigStorage()), kfAccount), null); return WxMpKfSessionList.fromJson(responseContent); } @Override - public WxMpKfSessionWaitCaseList kfSessionGetWaitCase() - throws WxErrorException { - String url = API_URL_PREFIX + "/kfsession/getwaitcase"; - String responseContent = this.wxMpService.get(url, null); + public WxMpKfSessionWaitCaseList kfSessionGetWaitCase() throws WxErrorException { + String responseContent = this.wxMpService.get(KFSESSION_GET_WAIT_CASE, null); return WxMpKfSessionWaitCaseList.fromJson(responseContent); } @Override public WxMpKfMsgList kfMsgList(Date startTime, Date endTime, Long msgId, Integer number) throws WxErrorException { - if(number > 10000){ - throw new WxErrorException(WxError.newBuilder().setErrorMsg("非法参数请求,每次最多查询10000条记录!").build()); + if (number > 10000) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("非法参数请求,每次最多查询10000条记录!").build()); } - if(startTime.after(endTime)){ - throw new WxErrorException(WxError.newBuilder().setErrorMsg("起始时间不能晚于结束时间!").build()); + if (startTime.after(endTime)) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("起始时间不能晚于结束时间!").build()); } - String url = API_URL_PREFIX + "/msgrecord/getmsglist"; - JsonObject param = new JsonObject(); - param.addProperty("starttime", startTime.getTime() / 1000); //starttime 起始时间,unix时间戳 - param.addProperty("endtime", endTime.getTime() / 1000); //endtime 结束时间,unix时间戳,每次查询时段不能超过24小时 - param.addProperty("msgid", msgId); //msgid 消息id顺序从小到大,从1开始 - param.addProperty("number", number); //number 每次获取条数,最多10000条 + param.addProperty("starttime", startTime.getTime() / 1000); + param.addProperty("endtime", endTime.getTime() / 1000); + param.addProperty("msgid", msgId); + param.addProperty("number", number); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(MSG_RECORD_LIST, param.toString()); return WxMpKfMsgList.fromJson(responseContent); } @@ -169,20 +136,29 @@ public WxMpKfMsgList kfMsgList(Date startTime, Date endTime, Long msgId, Integer @Override public WxMpKfMsgList kfMsgList(Date startTime, Date endTime) throws WxErrorException { int number = 10000; - WxMpKfMsgList result = this.kfMsgList(startTime,endTime, 1L, number); + WxMpKfMsgList result = this.kfMsgList(startTime, endTime, 1L, number); - if(result != null && result.getNumber() == number){ + if (result != null && result.getNumber() == number) { Long msgId = result.getMsgId(); - WxMpKfMsgList followingResult = this.kfMsgList(startTime,endTime, msgId, number); - while(followingResult != null && followingResult.getRecords().size() > 0){ + WxMpKfMsgList followingResult = this.kfMsgList(startTime, endTime, msgId, number); + while (followingResult != null && followingResult.getRecords().size() > 0) { result.getRecords().addAll(followingResult.getRecords()); result.setNumber(result.getNumber() + followingResult.getNumber()); result.setMsgId(followingResult.getMsgId()); - followingResult = this.kfMsgList(startTime,endTime, followingResult.getMsgId(), number); + followingResult = this.kfMsgList(startTime, endTime, followingResult.getMsgId(), number); } } return result; } + @Override + public boolean sendKfTypingState(String openid, String command) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("touser", openid); + params.addProperty("command", command); + String responseContent = this.wxMpService.post(CUSTOM_TYPING, params.toString()); + return responseContent != null; + } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImpl.java new file mode 100644 index 0000000000..c3dff12659 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImpl.java @@ -0,0 +1,82 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.api.WxMpMarketingService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadFilter; +import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult; +import me.chanjar.weixin.mp.bean.marketing.WxMpUserAction; +import me.chanjar.weixin.mp.bean.marketing.WxMpUserActionSet; +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Marketing.*; + +/** + * @author 007 + */ +@Slf4j +@RequiredArgsConstructor +public class WxMpMarketingServiceImpl implements WxMpMarketingService { + private final WxMpService wxMpService; + + @Override + public long addUserActionSets(String type, String name, String description) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("type", type); + json.addProperty("name", name); + json.addProperty("description", description); + String responseContent = wxMpService.post(USER_ACTION_SETS_ADD, json.toString()); + JsonObject tmpJson = GsonParser.parse(responseContent); + return tmpJson.get("data").getAsJsonObject().get("user_action_set_id").getAsLong(); + } + + @Override + public List getUserActionSets(Long userActionSetId) throws WxErrorException { + String responseContent = wxMpService.get(USER_ACTION_SETS_GET, "version=v1.0&user_action_set_id=" + userActionSetId); + return WxMpUserActionSet.fromJson(responseContent); + } + + @Override + public void addUserAction(List actions) throws WxErrorException { + wxMpService.post(USER_ACTIONS_ADD, WxMpUserAction.listToJson(actions)); + } + + @Override + public WxMpAdLeadResult getAdLeads(Date beginDate, Date endDate, List filtering, Integer page, Integer pageSize) + throws WxErrorException, IOException { + Date today = new Date(); + if (beginDate == null) { + beginDate = today; + } + if (endDate == null) { + endDate = today; + } + String params = "version=v1.0"; + JsonObject dateRange = new JsonObject(); + dateRange.addProperty("begin_date", DateFormatUtils.format(beginDate, "yyyy-MM-dd")); + dateRange.addProperty("end_date", DateFormatUtils.format(endDate, "yyyy-MM-dd")); + params += "&date_range=" + URLEncoder.encode(dateRange.toString(), StandardCharsets.UTF_8.name()); + params += "&page=" + page; + params += "&page_size=" + pageSize; + if (filtering != null) { + JsonArray filterJson = new JsonArray(); + for (WxMpAdLeadFilter filter : filtering) { + filterJson.add(filter.toJsonObject()); + } + params += "&filtering=" + URLEncoder.encode(filterJson.toString(), StandardCharsets.UTF_8.name()); + } + String responseContent = wxMpService.get(WECHAT_AD_LEADS_GET, params); + return WxMpAdLeadResult.fromJson(responseContent); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImpl.java new file mode 100644 index 0000000000..462604cb02 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImpl.java @@ -0,0 +1,93 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpMassMessageService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.result.WxMpMassGetResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassSpeedGetResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.*; + +/** + *
    + * 群发消息服务类
    + * Created by Binary Wang on 2017-8-16.
    + * 
    + * + * @author Binary Wang + */ +@Slf4j +@RequiredArgsConstructor +public class WxMpMassMessageServiceImpl implements WxMpMassMessageService { + private final WxMpService wxMpService; + + @Override + public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { + String responseContent = this.wxMpService.post(MassMessage.MEDIA_UPLOAD_NEWS_URL, news.toJson()); + return WxMpMassUploadResult.fromJson(responseContent); + } + + @Override + public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { + String responseContent = this.wxMpService.post(MassMessage.MEDIA_UPLOAD_VIDEO_URL, video.toJson()); + return WxMpMassUploadResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException { + String responseContent = this.wxMpService.post(MassMessage.MESSAGE_MASS_SENDALL_URL, message.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { + String responseContent = this.wxMpService.post(MassMessage.MESSAGE_MASS_SEND_URL, message.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws WxErrorException { + String responseContent = this.wxMpService.post(MassMessage.MESSAGE_MASS_PREVIEW_URL, wxMpMassPreviewMessage.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public void delete(Long msgId, Integer articleIndex) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("msg_id", msgId); + jsonObject.addProperty("article_idx", articleIndex); + this.wxMpService.post(MassMessage.MESSAGE_MASS_DELETE_URL, jsonObject.toString()); + } + + + @Override + public WxMpMassSpeedGetResult messageMassSpeedGet() throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + String response = this.wxMpService.post(MassMessage.MESSAGE_MASS_SPEED_GET_URL, jsonObject.toString()); + return WxMpMassSpeedGetResult.fromJson(response); + } + + + @Override + public void messageMassSpeedSet(Integer speed) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("speed", speed); + this.wxMpService.post(MassMessage.MESSAGE_MASS_SPEED_SET_URL, jsonObject.toString()); + } + + + @Override + public WxMpMassGetResult messageMassGet(Long msgId) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("msg_id", msgId); + String response = this.wxMpService.post(MassMessage.MESSAGE_MASS_GET_URL, jsonObject.toString()); + return WxMpMassGetResult.fromJson(response); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java index af9fa54c35..45e1c5c4b1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java @@ -1,21 +1,21 @@ package me.chanjar.weixin.mp.api.impl; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.api.WxMpMaterialService; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.bean.material.WxMpMaterial; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialArticleUpdate; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; import me.chanjar.weixin.mp.bean.material.*; -import me.chanjar.weixin.mp.util.http.*; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import me.chanjar.weixin.mp.util.requestexecuter.material.*; +import me.chanjar.weixin.mp.util.requestexecuter.media.MediaImgUploadRequestExecutor; import java.io.File; import java.io.IOException; @@ -24,53 +24,63 @@ import java.util.Map; import java.util.UUID; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Material.*; + /** * Created by Binary Wang on 2016/7/21. + * + * @author Binary Wang */ +@RequiredArgsConstructor public class WxMpMaterialServiceImpl implements WxMpMaterialService { - private static final String MEDIA_API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/media"; - private static final String MATERIAL_API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/material"; - private WxMpService wxMpService; - - public WxMpMaterialServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException { + File tmpFile = null; try { - return this.mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + tmpFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType); + return this.mediaUpload(mediaType, tmpFile); } catch (IOException e) { - e.printStackTrace(); - throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build()); + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg(e.getMessage()).build(), e); + } finally { + if (tmpFile != null) { + tmpFile.delete(); + } } } @Override public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { - String url = MEDIA_API_URL_PREFIX + "/upload?type=" + mediaType; - return this.wxMpService.execute(new MediaUploadRequestExecutor(), url, file); + String url = String.format(MEDIA_UPLOAD_URL.getUrl(this.wxMpService.getWxMpConfigStorage()), mediaType); + return this.wxMpService.execute(MediaUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), url, file); + } + + @Override + public File mediaDownload(String mediaId) throws WxErrorException { + return this.wxMpService.execute( + BaseMediaDownloadRequestExecutor.create(this.wxMpService.getRequestHttp(), this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), + MEDIA_GET_URL, + "media_id=" + mediaId); } @Override - public File mediaDownload(String media_id) throws WxErrorException { - String url = MEDIA_API_URL_PREFIX + "/get"; + public File jssdkMediaDownload(String mediaId) throws WxErrorException { return this.wxMpService.execute( - new MediaDownloadRequestExecutor(this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), - url, - "media_id=" + media_id); + BaseMediaDownloadRequestExecutor.create(this.wxMpService.getRequestHttp(), this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), + JSSDK_MEDIA_GET_URL, + "media_id=" + mediaId); } @Override public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException { - String url = MEDIA_API_URL_PREFIX + "/uploadimg"; - return this.wxMpService.execute(new MediaImgUploadRequestExecutor(), url, file); + return this.wxMpService.execute(MediaImgUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), IMG_UPLOAD_URL, file); } @Override public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/add_material?type=" + mediaType; - return this.wxMpService.execute(new MaterialUploadRequestExecutor(), url, material); + String url = String.format(MATERIAL_ADD_URL.getUrl(this.wxMpService.getWxMpConfigStorage()), mediaType); + return this.wxMpService.execute(MaterialUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), url, material); } @Override @@ -78,34 +88,33 @@ public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws if (news == null || news.isEmpty()) { throw new IllegalArgumentException("news is empty!"); } - String url = MATERIAL_API_URL_PREFIX + "/add_news"; - String responseContent = this.wxMpService.post(url, news.toJson()); + String responseContent = this.wxMpService.post(NEWS_ADD_URL, news.toJson()); return WxMpMaterialUploadResult.fromJson(responseContent); } @Override - public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_material"; - return this.wxMpService.execute(new MaterialVoiceAndImageDownloadRequestExecutor(this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), url, media_id); + public InputStream materialImageOrVoiceDownload(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialVoiceAndImageDownloadRequestExecutor + .create(this.wxMpService.getRequestHttp(), this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), + MATERIAL_GET_URL, mediaId); } @Override - public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_material"; - return this.wxMpService.execute(new MaterialVideoInfoRequestExecutor(), url, media_id); + public WxMpMaterialVideoInfoResult materialVideoInfo(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialVideoInfoRequestExecutor.create(this.wxMpService.getRequestHttp()), + MATERIAL_GET_URL, mediaId); } @Override - public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_material"; - return this.wxMpService.execute(new MaterialNewsInfoRequestExecutor(), url, media_id); + public WxMpMaterialNews materialNewsInfo(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialNewsInfoRequestExecutor.create(this.wxMpService.getRequestHttp()), + MATERIAL_GET_URL, mediaId); } @Override public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/update_news"; - String responseText = this.wxMpService.post(url, wxMpMaterialArticleUpdate.toJson()); - WxError wxError = WxError.fromJson(responseText); + String responseText = this.wxMpService.post(NEWS_UPDATE_URL, wxMpMaterialArticleUpdate.toJson()); + WxError wxError = WxError.fromJson(responseText, WxType.MP); if (wxError.getErrorCode() == 0) { return true; } else { @@ -114,16 +123,15 @@ public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleU } @Override - public boolean materialDelete(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/del_material"; - return this.wxMpService.execute(new MaterialDeleteRequestExecutor(), url, media_id); + public boolean materialDelete(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialDeleteRequestExecutor.create(this.wxMpService.getRequestHttp()), + MATERIAL_DEL_URL, mediaId); } @Override public WxMpMaterialCountResult materialCount() throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_materialcount"; - String responseText = this.wxMpService.get(url, null); - WxError wxError = WxError.fromJson(responseText); + String responseText = this.wxMpService.get(MATERIAL_GET_COUNT_URL, null); + WxError wxError = WxError.fromJson(responseText, WxType.MP); if (wxError.getErrorCode() == 0) { return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialCountResult.class); } else { @@ -133,13 +141,12 @@ public WxMpMaterialCountResult materialCount() throws WxErrorException { @Override public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/batchget_material"; - Map params = new HashMap<>(); - params.put("type", WxConsts.MATERIAL_NEWS); + Map params = new HashMap<>(4); + params.put("type", WxConsts.MaterialType.NEWS); params.put("offset", offset); params.put("count", count); - String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params)); - WxError wxError = WxError.fromJson(responseText); + String responseText = this.wxMpService.post(MATERIAL_BATCHGET_URL, WxGsonBuilder.create().toJson(params)); + WxError wxError = WxError.fromJson(responseText, WxType.MP); if (wxError.getErrorCode() == 0) { return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialNewsBatchGetResult.class); } else { @@ -149,13 +156,12 @@ public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count @Override public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/batchget_material"; - Map params = new HashMap<>(); + Map params = new HashMap<>(4); params.put("type", type); params.put("offset", offset); params.put("count", count); - String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params)); - WxError wxError = WxError.fromJson(responseText); + String responseText = this.wxMpService.post(MATERIAL_BATCHGET_URL, WxGsonBuilder.create().toJson(params)); + WxError wxError = WxError.fromJson(responseText, WxType.MP); if (wxError.getErrorCode() == 0) { return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialFileBatchGetResult.class); } else { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java new file mode 100644 index 0000000000..4f4471b2bb --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java @@ -0,0 +1,335 @@ +package me.chanjar.weixin.mp.api.impl; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.mp.bean.card.membercard.*; +import me.chanjar.weixin.mp.enums.WxMpApiUrl; +import org.apache.commons.lang3.StringUtils; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.api.WxMpMemberCardService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.card.AdvancedInfo; +import me.chanjar.weixin.mp.bean.card.BaseInfo; +import me.chanjar.weixin.mp.bean.card.CardUpdateResult; +import me.chanjar.weixin.mp.bean.card.DateInfo; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCard; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardActivateUserFormRequest; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardActivateUserFormResult; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardCreateRequest; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardUpdateRequest; +import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; +import me.chanjar.weixin.mp.bean.card.enums.BusinessServiceType; +import me.chanjar.weixin.mp.bean.card.enums.CardColor; +import me.chanjar.weixin.mp.bean.card.enums.DateInfoType; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 会员卡相关接口的实现类 + * + * @author YuJian(mgcnrx11 @ gmail.com) + * @version 2017/7/8 + */ +@Slf4j +@RequiredArgsConstructor +public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { + private final WxMpService wxMpService; + + private static final Gson GSON = WxMpGsonBuilder.create(); + + @Override + public WxMpService getWxMpService() { + return this.wxMpService; + } + + @Override + public WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException { + WxMpMemberCardCreateMessage createMessage = WxGsonBuilder.create() + .fromJson(createJson, WxMpMemberCardCreateMessage.class); + return createMemberCard(createMessage); + } + + @Override + public WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) + throws WxErrorException { + //校验请求对象合法性 + WxMpCardCreateResult validResult = validCheck(createMessageMessage); + if (!validResult.isSuccess()) { + return validResult; + } + + String response = this.wxMpService.post(WxMpApiUrl.MemberCard.MEMBER_CARD_CREATE, GSON.toJson(createMessageMessage)); + return WxMpCardCreateResult.fromJson(response); + } + + private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessageMessage) { + if (createMessageMessage == null) { + return WxMpCardCreateResult.failure("对象不能为空"); + } + MemberCardCreateRequest cardCreateRequest = createMessageMessage.getCardCreateRequest(); + if (cardCreateRequest == null) { + return WxMpCardCreateResult.failure("会员卡对象不能为空"); + } + String cardType = cardCreateRequest.getCardType(); + if (!StringUtils.equals(cardType, "MEMBER_CARD")) { + return WxMpCardCreateResult.failure("卡券类型必须等于MEMBER_CARD"); + } + MemberCard memberCard = cardCreateRequest.getMemberCard(); + + if (StringUtils.isEmpty(memberCard.getPrerogative())) { + return WxMpCardCreateResult.failure("会员卡特权说明不能为空:prerogative"); + } + //卡片激活规则 + if (!memberCard.isAutoActivate() && !memberCard.isWxActivate() + && StringUtils.isEmpty(memberCard.getActivateUrl())) { + return WxMpCardCreateResult.failure("会员卡激活方式为接口激活,activate_url不能为空"); + } + + BaseInfo baseInfo = memberCard.getBaseInfo(); + if (baseInfo == null) { + return WxMpCardCreateResult.failure("会员卡基本信息对象base_info不能为空"); + } + + if (StringUtils.isBlank(baseInfo.getLogoUrl())) { + return WxMpCardCreateResult.failure("会员卡基本信息的商户logo:logo_url不能为空"); + } + + if (StringUtils.isBlank(baseInfo.getCodeType())) { + return WxMpCardCreateResult.failure("会员卡基本信息的条码类型:code_type不能为空"); + } + + if (StringUtils.isBlank(baseInfo.getBrandName())) { + return WxMpCardCreateResult.failure("会员卡基本信息的商户名字:brand_name不能为空"); + } + + if (StringUtils.length(baseInfo.getBrandName()) > 12) { + return WxMpCardCreateResult.failure("会员卡基本信息的商户名字:brand_name长度不能大于12个汉字"); + } + + if (StringUtils.isBlank(baseInfo.getTitle())) { + return WxMpCardCreateResult.failure("会员卡基本信息的卡券名称:title不能为空"); + } + + if (StringUtils.length(baseInfo.getTitle()) > 9) { + return WxMpCardCreateResult.failure("会员卡基本信息的卡券名称:title长度不能大于9个汉字"); + } + + if (StringUtils.isBlank(baseInfo.getColor())) { + return WxMpCardCreateResult.failure("会员卡基本信息的卡颜色:color不能为空"); + } + + CardColor cardColor = null; + try { + cardColor = CardColor.valueOf(baseInfo.getColor()); + } catch (IllegalArgumentException ex) { + + } + if (cardColor == null) { + return WxMpCardCreateResult.failure("会员卡基本信息的卡颜色:" + baseInfo.getColor() + "不支持"); + } + + if (StringUtils.isBlank(baseInfo.getNotice())) { + return WxMpCardCreateResult.failure("会员卡基本信息的使用提醒:notice不能为空"); + } + + if (StringUtils.isBlank(baseInfo.getDescription())) { + return WxMpCardCreateResult.failure("会员卡基本信息的使用说明:description不能为空"); + } + + if (baseInfo.getSku() == null) { + return WxMpCardCreateResult.failure("会员卡基本信息的商品信息:sku不能为空"); + } + + DateInfo dateInfo = baseInfo.getDateInfo(); + if (dateInfo == null) { + return WxMpCardCreateResult.failure("会员卡基本信息的使用日期:date_info不能为空"); + } + + DateInfoType dateInfoType = null; + try { + dateInfoType = DateInfoType.valueOf(dateInfo.getType()); + } catch (IllegalArgumentException ex) { + + } + + if (dateInfoType == null) { + return WxMpCardCreateResult.failure("会员卡基本信息的使用日期类型:" + dateInfo.getType() + "不合法"); + } + + //固定时长 + if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TERM + && (dateInfo.getFixedTerm() == null || dateInfo.getFixedBeginTerm() == null)) { + return WxMpCardCreateResult.failure(String.format("会员卡基本信息的使用日期为:%s,fixedTerm和fixedBeginTerm不能为空", + dateInfoType.getDescription())); + } + + //固定期限 + if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE + && (dateInfo.getBeginTimestamp() == null || dateInfo.getEndTimestamp() == null)) { + return WxMpCardCreateResult.failure(String.format("会员卡基本信息的使用日期为:%s,beginTimestamp 和 endTimestamp 不能为空", + dateInfoType.getDescription())); + } + if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE + && (dateInfo.getBeginTimestamp() * 1000 < System.currentTimeMillis() + || dateInfo.getEndTimestamp() * 1000 < System.currentTimeMillis() + || dateInfo.getBeginTimestamp() > dateInfo.getEndTimestamp())) { + return WxMpCardCreateResult.failure(String.format("会员卡基本信息的使用日期为:%s,beginTimestamp和endTimestamp的值不合法,请检查", + dateInfoType.getDescription())); + } + + if (!baseInfo.isUseAllLocations() && baseInfo.getLocationIdList().isEmpty()) { + return WxMpCardCreateResult.failure("会员卡基本信息的门店使用范围选择指定门店,门店列表:locationIdList不能为空"); + } + + //校验高级信息 + AdvancedInfo advancedInfo = memberCard.getAdvancedInfo(); + if (advancedInfo != null) { + if (advancedInfo.getBusinessServiceList() != null) { + for (String bs : advancedInfo.getBusinessServiceList()) { + try { + BusinessServiceType.valueOf(bs); + } catch (IllegalArgumentException ex) { + return WxMpCardCreateResult.failure("会员卡高级信息的商户服务:" + bs + " 不合法"); + } + } + } + } + + return WxMpCardCreateResult.success(); + } + + @Override + public String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException { + return this.wxMpService.post(WxMpApiUrl.MemberCard.MEMBER_CARD_ACTIVATE, GSON.toJson(activatedMessage)); + } + + @Override + public WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("card_id", cardId); + jsonObject.addProperty("code", code); + + String responseContent = this.getWxMpService().post(WxMpApiUrl.MemberCard.MEMBER_CARD_USER_INFO_GET, jsonObject.toString()); + log.debug("{}", responseContent); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxMpGsonBuilder.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); + } + + @Override + public WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) + throws WxErrorException { + + String responseContent = this.getWxMpService().post(WxMpApiUrl.MemberCard.MEMBER_CARD_UPDATE_USER, GSON.toJson(updateUserMessage)); + + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxMpGsonBuilder.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); + } + + @Override + public MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException { + String responseContent = this.getWxMpService().post(WxMpApiUrl.MemberCard.MEMBER_CARD_ACTIVATE_USER_FORM, GSON.toJson(userFormRequest)); + return MemberCardActivateUserFormResult.fromJson(responseContent); + } + + @Override + public ActivatePluginParam getActivatePluginParam(String cardId, String outStr) throws WxErrorException { + String url = this.getActivatePluginUrl(cardId, outStr); + try { + String decodedUrl = URLDecoder.decode(url, "UTF-8"); + Map resultMap = parseRequestUrl(decodedUrl); + ActivatePluginParam activatePluginParam = new ActivatePluginParam(); + activatePluginParam.setEncryptCardId(resultMap.get("encrypt_card_id")); + activatePluginParam.setOuterStr(resultMap.get("outer_str")); + activatePluginParam.setBiz(resultMap.get("biz") + "=="); + return activatePluginParam; + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return null; + } + + + @Override + public String getActivatePluginUrl(String cardId, String outStr) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("card_id", cardId); + params.addProperty("outer_str", outStr); + String response = this.wxMpService.post(WxMpApiUrl.MemberCard.MEMBER_CARD_ACTIVATE_URL, GSON.toJson(params)); + ActivatePluginParamResult result = GSON.fromJson(response, ActivatePluginParamResult.class); + return result.getUrl(); + } + + @Override + public CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateRequest) throws WxErrorException { + String response = this.wxMpService.post(WxMpApiUrl.MemberCard.MEMBER_CARD_UPDATE, GSON.toJson(memberCardUpdateRequest)); + return GSON.fromJson(response, CardUpdateResult.class); + } + + @Override + public WxMpMemberCardActivateTempInfoResult getActivateTempInfo(String activateTicket) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("activate_ticket", activateTicket); + String response = this.wxMpService.post(WxMpApiUrl.MemberCard.MEMBER_CARD_ACTIVATE_TEMP_INFO, GSON.toJson(params)); + return GSON.fromJson(response, WxMpMemberCardActivateTempInfoResult.class); + } + + private static String truncateUrlPage(String strURL) { + String strAllParam = null; + String[] arrSplit; + arrSplit = strURL.split("[?]"); + if (strURL.length() > 1) { + if (arrSplit.length > 1) { + if (arrSplit[1] != null) { + strAllParam = arrSplit[1]; + } + } + } + + return strAllParam; + } + + private static Map parseRequestUrl(String url) { + Map mapRequest = new HashMap<>(16); + + String[] arrSplit; + + String strUrlParam = truncateUrlPage(url); + if (strUrlParam == null) { + return mapRequest; + } + arrSplit = strUrlParam.split("[&]"); + for (String strSplit : arrSplit) { + String[] arrSplitEqual; + arrSplitEqual = strSplit.split("[=]"); + + //解析出键值 + if (arrSplitEqual.length > 1) { + //正确解析 + mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]); + + } else { + if (!"".equals(arrSplitEqual[0])) { + //只有参数没有值,不加入 + mapRequest.put(arrSplitEqual[0], ""); + } + } + } + return mapRequest; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java index d22f4c82e2..5631a44f7e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java @@ -1,35 +1,35 @@ package me.chanjar.weixin.mp.api.impl; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.bean.menu.WxMenu; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.api.WxMpMenuService; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; import me.chanjar.weixin.mp.bean.menu.WxMpMenu; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import me.chanjar.weixin.mp.enums.WxMpApiUrl; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Menu.*; /** * Created by Binary Wang on 2016/7/21. + * + * @author Binary Wang */ +@Slf4j +@RequiredArgsConstructor public class WxMpMenuServiceImpl implements WxMpMenuService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/menu"; - private static Logger log = LoggerFactory.getLogger(WxMpMenuServiceImpl.class); - - private WxMpService wxMpService; - - public WxMpMenuServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public String menuCreate(WxMenu menu) throws WxErrorException { String menuJson = menu.toJson(); - String url = API_URL_PREFIX + "/create"; + WxMpApiUrl.Menu url = MENU_CREATE; if (menu.getMatchRule() != null) { - url = API_URL_PREFIX + "/addconditional"; + url = MENU_ADDCONDITIONAL; } log.debug("开始创建菜单:{}", menuJson); @@ -38,7 +38,7 @@ public String menuCreate(WxMenu menu) throws WxErrorException { log.debug("创建菜单:{},结果:{}", menuJson, result); if (menu.getMatchRule() != null) { - return new JsonParser().parse(result).getAsJsonObject().get("menuid").getAsString(); + return GsonParser.parse(result).get("menuid").getAsString(); } return null; @@ -46,16 +46,15 @@ public String menuCreate(WxMenu menu) throws WxErrorException { @Override public String menuCreate(String json) throws WxErrorException { - JsonParser jsonParser = new JsonParser(); - JsonObject jsonObject = jsonParser.parse(json).getAsJsonObject(); - String url = API_URL_PREFIX + "/create"; + JsonObject jsonObject = GsonParser.parse(json); + WxMpApiUrl.Menu url = MENU_CREATE; if (jsonObject.get("matchrule") != null) { - url = API_URL_PREFIX + "/addconditional"; + url = MENU_ADDCONDITIONAL; } String result = this.wxMpService.post(url, json); if (jsonObject.get("matchrule") != null) { - return jsonParser.parse(result).getAsJsonObject().get("menuid").getAsString(); + return GsonParser.parse(result).get("menuid").getAsString(); } return null; @@ -63,25 +62,22 @@ public String menuCreate(String json) throws WxErrorException { @Override public void menuDelete() throws WxErrorException { - String url = API_URL_PREFIX + "/delete"; - String result = this.wxMpService.get(url, null); + String result = this.wxMpService.get(MENU_DELETE, null); log.debug("删除菜单结果:{}", result); } @Override public void menuDelete(String menuId) throws WxErrorException { - String url = API_URL_PREFIX + "/delconditional"; JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("menuid", menuId); - String result = this.wxMpService.post(url, jsonObject.toString()); + String result = this.wxMpService.post(MENU_DELCONDITIONAL, jsonObject.toString()); log.debug("根据MeunId({})删除个性化菜单结果:{}", menuId, result); } @Override public WxMpMenu menuGet() throws WxErrorException { - String url = API_URL_PREFIX + "/get"; try { - String resultContent = this.wxMpService.get(url, null); + String resultContent = this.wxMpService.get(MENU_GET, null); return WxMpMenu.fromJson(resultContent); } catch (WxErrorException e) { // 46003 不存在的菜单数据 @@ -94,11 +90,10 @@ public WxMpMenu menuGet() throws WxErrorException { @Override public WxMenu menuTryMatch(String userid) throws WxErrorException { - String url = API_URL_PREFIX + "/trymatch"; JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("user_id", userid); try { - String resultContent = this.wxMpService.post(url, jsonObject.toString()); + String resultContent = this.wxMpService.post(MENU_TRYMATCH, jsonObject.toString()); return WxMenu.fromJson(resultContent); } catch (WxErrorException e) { // 46003 不存在的菜单数据;46002 不存在的菜单版本 @@ -112,8 +107,7 @@ public WxMenu menuTryMatch(String userid) throws WxErrorException { @Override public WxMpGetSelfMenuInfoResult getSelfMenuInfo() throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info"; - String resultContent = this.wxMpService.get(url, null); + String resultContent = this.wxMpService.get(GET_CURRENT_SELFMENU_INFO, null); return WxMpGetSelfMenuInfoResult.fromJson(resultContent); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMerchantInvoiceServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMerchantInvoiceServiceImpl.java new file mode 100644 index 0000000000..ffae3ddf12 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMerchantInvoiceServiceImpl.java @@ -0,0 +1,119 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpCardService; +import me.chanjar.weixin.mp.api.WxMpMerchantInvoiceService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.invoice.merchant.*; +import me.chanjar.weixin.mp.enums.WxMpApiUrl; + +import java.util.HashMap; +import java.util.Map; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Invoice.*; + + +@AllArgsConstructor +public class WxMpMerchantInvoiceServiceImpl implements WxMpMerchantInvoiceService { + + private WxMpService wxMpService; + private WxMpCardService wxMpCardService; + + private final static Gson gson; + + static { + gson = new GsonBuilder() + .disableHtmlEscaping() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + } + + @Override + public InvoiceAuthPageResult getAuthPageUrl(InvoiceAuthPageRequest params) throws WxErrorException { + String ticket = wxMpCardService.getCardApiTicket(); + params.setTicket(ticket); + return doCommonInvoiceHttpPost(GET_AUTH_URL, params, InvoiceAuthPageResult.class); + } + + @Override + public InvoiceAuthDataResult getAuthData(InvoiceAuthDataRequest params) throws WxErrorException { + return doCommonInvoiceHttpPost(GET_AUTH_DATA, params, InvoiceAuthDataResult.class); + } + + @Override + public void rejectInvoice(InvoiceRejectRequest params) throws WxErrorException { + doCommonInvoiceHttpPost(REJECT_INSERT, params, null); + } + + @Override + public void makeOutInvoice(MakeOutInvoiceRequest params) throws WxErrorException { + doCommonInvoiceHttpPost(MAKE_OUT_INVOICE, params, null); + } + + @Override + public void clearOutInvoice(ClearOutInvoiceRequest params) throws WxErrorException { + doCommonInvoiceHttpPost(CLEAR_OUT_INVOICE, params, null); + } + + @Override + public InvoiceResult queryInvoiceInfo(String fpqqlsh, String nsrsbh) throws WxErrorException { + Map data = new HashMap(); + data.put("fpqqlsh", fpqqlsh); + data.put("nsrsbh", nsrsbh); + return doCommonInvoiceHttpPost(QUERY_INVOICE_INFO, data, InvoiceResult.class); + } + + @Override + public void setMerchantContactInfo(MerchantContactInfo contact) throws WxErrorException { + MerchantContactInfoWrapper data = new MerchantContactInfoWrapper(); + data.setContact(contact); + doCommonInvoiceHttpPost(SET_CONTACT_SET_BIZ_ATTR, data, null); + } + + @Override + public MerchantContactInfo getMerchantContactInfo() throws WxErrorException { + MerchantContactInfoWrapper merchantContactInfoWrapper = doCommonInvoiceHttpPost(GET_CONTACT_SET_BIZ_ATTR, null, MerchantContactInfoWrapper.class); + return merchantContactInfoWrapper == null ? null : merchantContactInfoWrapper.getContact(); + } + + @Override + public void setAuthPageSetting(InvoiceAuthPageSetting authPageSetting) throws WxErrorException { + doCommonInvoiceHttpPost(SET_AUTH_FIELD_SET_BIZ_ATTR, authPageSetting, null); + } + + @Override + public InvoiceAuthPageSetting getAuthPageSetting() throws WxErrorException { + return doCommonInvoiceHttpPost(GET_AUTH_FIELD_SET_BIZ_ATTR, new JsonObject(), InvoiceAuthPageSetting.class); + } + + @Override + public void setMerchantInvoicePlatform(MerchantInvoicePlatformInfo paymchInfo) throws WxErrorException { + MerchantInvoicePlatformInfoWrapper data = new MerchantInvoicePlatformInfoWrapper(); + data.setPaymchInfo(paymchInfo); + doCommonInvoiceHttpPost(SET_PAY_MCH_SET_BIZ_ATTR, data, null); + } + + @Override + public MerchantInvoicePlatformInfo getMerchantInvoicePlatform(MerchantInvoicePlatformInfo merchantInvoicePlatformInfo) throws WxErrorException { + MerchantInvoicePlatformInfoWrapper result = doCommonInvoiceHttpPost(GET_PAY_MCH_SET_BIZ_ATTR, new JsonObject(), MerchantInvoicePlatformInfoWrapper.class); + return result == null ? null : result.getPaymchInfo(); + } + + /** + * 电子发票公用post请求方法 + */ + private T doCommonInvoiceHttpPost(WxMpApiUrl url, Object data, Class resultClass) throws WxErrorException { + String json = ""; + if (data != null) { + json = gson.toJson(data); + } + String responseText = wxMpService.post(url, json); + if (resultClass == null) return null; + return gson.fromJson(responseText, resultClass); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpOcrServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpOcrServiceImpl.java new file mode 100644 index 0000000000..f6748b5641 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpOcrServiceImpl.java @@ -0,0 +1,162 @@ +package me.chanjar.weixin.mp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.api.WxOcrService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.common.bean.ocr.WxOcrBankCardResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrBizLicenseResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrCommResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrDrivingLicenseResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrDrivingResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrIdCardResult; +import me.chanjar.weixin.common.requestexecuter.ocr.OcrDiscernRequestExecutor; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.BANK_CARD; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.BIZ_LICENSE; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.COMM; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.DRIVING; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.DRIVING_LICENSE; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.FILEIDCARD; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.FILE_BANK_CARD; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.FILE_BIZ_LICENSE; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.FILE_COMM; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.FILE_DRIVING; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.FILE_DRIVING_LICENSE; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Ocr.IDCARD; + +/** + * ocr 接口实现. + * + * @author Binary Wang + * @date 2019-06-22 + */ +@RequiredArgsConstructor +public class WxMpOcrServiceImpl implements WxOcrService { + private final WxMpService mainService; + + @Override + public WxOcrIdCardResult idCard(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(IDCARD.getUrl(this.mainService.getWxMpConfigStorage()), + imgUrl), null); + return WxOcrIdCardResult.fromJson(result); + } + + @Override + public WxOcrIdCardResult idCard(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILEIDCARD.getUrl(this.mainService.getWxMpConfigStorage()), imgFile); + return WxOcrIdCardResult.fromJson(result); + } + + @Override + public WxOcrBankCardResult bankCard(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(BANK_CARD.getUrl(this.mainService.getWxMpConfigStorage()), + imgUrl), null); + return WxOcrBankCardResult.fromJson(result); + } + + @Override + public WxOcrBankCardResult bankCard(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_BANK_CARD.getUrl(this.mainService.getWxMpConfigStorage()), imgFile); + return WxOcrBankCardResult.fromJson(result); + } + + @Override + public WxOcrDrivingResult driving(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(DRIVING.getUrl(this.mainService.getWxMpConfigStorage()), + imgUrl), null); + return WxOcrDrivingResult.fromJson(result); + } + + @Override + public WxOcrDrivingResult driving(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_DRIVING.getUrl(this.mainService.getWxMpConfigStorage()), imgFile); + return WxOcrDrivingResult.fromJson(result); + } + + @Override + public WxOcrDrivingLicenseResult drivingLicense(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(DRIVING_LICENSE.getUrl(this.mainService.getWxMpConfigStorage()), + imgUrl), null); + return WxOcrDrivingLicenseResult.fromJson(result); + } + + @Override + public WxOcrDrivingLicenseResult drivingLicense(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_DRIVING_LICENSE.getUrl(this.mainService.getWxMpConfigStorage()), imgFile); + return WxOcrDrivingLicenseResult.fromJson(result); + } + + @Override + public WxOcrBizLicenseResult bizLicense(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(BIZ_LICENSE.getUrl(this.mainService.getWxMpConfigStorage()), + imgUrl), null); + return WxOcrBizLicenseResult.fromJson(result); + } + + @Override + public WxOcrBizLicenseResult bizLicense(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_BIZ_LICENSE.getUrl(this.mainService.getWxMpConfigStorage()), imgFile); + return WxOcrBizLicenseResult.fromJson(result); + } + + @Override + public WxOcrCommResult comm(String imgUrl) throws WxErrorException { + try { + imgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // ignore cannot happen + } + + final String result = this.mainService.post(String.format(COMM.getUrl(this.mainService.getWxMpConfigStorage()), + imgUrl), null); + return WxOcrCommResult.fromJson(result); + } + + @Override + public WxOcrCommResult comm(File imgFile) throws WxErrorException { + String result = this.mainService.execute(OcrDiscernRequestExecutor.create(this.mainService.getRequestHttp()), + FILE_COMM.getUrl(this.mainService.getWxMpConfigStorage()), imgFile); + return WxOcrCommResult.fromJson(result); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java index 8659f2af9d..a654afb769 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java @@ -1,102 +1,111 @@ package me.chanjar.weixin.mp.api.impl; import com.google.gson.JsonObject; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpQrcodeService; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; -import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; +import me.chanjar.weixin.mp.util.requestexecuter.qrcode.QrCodeRequestExecutor; +import org.apache.commons.lang3.StringUtils; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Qrcode.*; + /** * Created by Binary Wang on 2016/7/21. + * + * @author Binary Wang */ +@RequiredArgsConstructor public class WxMpQrcodeServiceImpl implements WxMpQrcodeService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/qrcode"; - private WxMpService wxMpService; - - public WxMpQrcodeServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public WxMpQrCodeTicket qrCodeCreateTmpTicket(int sceneId, Integer expireSeconds) throws WxErrorException { if (sceneId == 0) { - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("临时二维码场景值不能为0!").build()); + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("临时二维码场景值不能为0!").build()); + } + + return this.createQrCode("QR_SCENE", null, sceneId, expireSeconds); + } + + @Override + public WxMpQrCodeTicket qrCodeCreateTmpTicket(String sceneStr, Integer expireSeconds) throws WxErrorException { + if (StringUtils.isBlank(sceneStr)) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("临时二维码场景值不能为空!").build()); } + return this.createQrCode("QR_STR_SCENE", sceneStr, null, expireSeconds); + } + + private WxMpQrCodeTicket createQrCode(String actionName, String sceneStr, Integer sceneId, Integer expireSeconds) + throws WxErrorException { //expireSeconds 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。 if (expireSeconds != null && expireSeconds > 2592000) { - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1) - .setErrorMsg("临时二维码有效时间最大不能超过2592000(即30天)!").build()); + throw new WxErrorException(WxError.builder().errorCode(-1) + .errorMsg("临时二维码有效时间最大不能超过2592000(即30天)!").build()); } if (expireSeconds == null) { expireSeconds = 30; } - String url = API_URL_PREFIX + "/create"; + return this.getQrCodeTicket(actionName, sceneStr, sceneId, expireSeconds); + } + + private WxMpQrCodeTicket getQrCodeTicket(String actionName, String sceneStr, Integer sceneId, Integer expireSeconds) + throws WxErrorException { JsonObject json = new JsonObject(); - json.addProperty("action_name", "QR_SCENE"); - json.addProperty("expire_seconds", expireSeconds); + json.addProperty("action_name", actionName); + if (expireSeconds != null) { + json.addProperty("expire_seconds", expireSeconds); + } JsonObject actionInfo = new JsonObject(); JsonObject scene = new JsonObject(); - scene.addProperty("scene_id", sceneId); + if (sceneStr != null) { + scene.addProperty("scene_str", sceneStr); + } else if (sceneId != null) { + scene.addProperty("scene_id", sceneId); + } + actionInfo.add("scene", scene); json.add("action_info", actionInfo); - String responseContent = this.wxMpService.post(url, json.toString()); + String responseContent = this.wxMpService.post(QRCODE_CREATE, json.toString()); return WxMpQrCodeTicket.fromJson(responseContent); } @Override public WxMpQrCodeTicket qrCodeCreateLastTicket(int sceneId) throws WxErrorException { if (sceneId < 1 || sceneId > 100000) { - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("永久二维码的场景值目前只支持1--100000!").build()); + throw new WxErrorException(WxError.builder().errorCode(-1) + .errorMsg("永久二维码的场景值目前只支持1--100000!") + .build()); } - String url = API_URL_PREFIX + "/create"; - JsonObject json = new JsonObject(); - json.addProperty("action_name", "QR_LIMIT_SCENE"); - JsonObject actionInfo = new JsonObject(); - JsonObject scene = new JsonObject(); - scene.addProperty("scene_id", sceneId); - actionInfo.add("scene", scene); - json.add("action_info", actionInfo); - String responseContent = this.wxMpService.post(url, json.toString()); - return WxMpQrCodeTicket.fromJson(responseContent); + return this.getQrCodeTicket("QR_LIMIT_SCENE", null, sceneId, null); } @Override public WxMpQrCodeTicket qrCodeCreateLastTicket(String sceneStr) throws WxErrorException { - String url = API_URL_PREFIX + "/create"; - JsonObject json = new JsonObject(); - json.addProperty("action_name", "QR_LIMIT_STR_SCENE"); - JsonObject actionInfo = new JsonObject(); - JsonObject scene = new JsonObject(); - scene.addProperty("scene_str", sceneStr); - actionInfo.add("scene", scene); - json.add("action_info", actionInfo); - String responseContent = this.wxMpService.post(url, json.toString()); - return WxMpQrCodeTicket.fromJson(responseContent); + return this.getQrCodeTicket("QR_LIMIT_STR_SCENE", sceneStr, null, null); } @Override public File qrCodePicture(WxMpQrCodeTicket ticket) throws WxErrorException { - String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode"; - return this.wxMpService.execute(new QrCodeRequestExecutor(), url, ticket); + return this.wxMpService.execute(QrCodeRequestExecutor.create(this.wxMpService.getRequestHttp()), SHOW_QRCODE, ticket); } @Override public String qrCodePictureUrl(String ticket, boolean needShortUrl) throws WxErrorException { - String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s"; try { - String resultUrl = String.format(url, + String resultUrl = String.format(SHOW_QRCODE_WITH_TICKET.getUrl(this.wxMpService.getWxMpConfigStorage()), URLEncoder.encode(ticket, StandardCharsets.UTF_8.name())); if (needShortUrl) { return this.wxMpService.shortUrl(resultUrl); @@ -104,15 +113,13 @@ public String qrCodePictureUrl(String ticket, boolean needShortUrl) throws WxErr return resultUrl; } catch (UnsupportedEncodingException e) { - WxError error = WxError.newBuilder().setErrorCode(-1) - .setErrorMsg(e.getMessage()).build(); - throw new WxErrorException(error); + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg(e.getMessage()).build()); } } @Override public String qrCodePictureUrl(String ticket) throws WxErrorException { - return qrCodePictureUrl(ticket, false); + return this.qrCodePictureUrl(ticket, false); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java new file mode 100644 index 0000000000..6e7ee376c7 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java @@ -0,0 +1,106 @@ +package me.chanjar.weixin.mp.api.impl; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_ACCESS_TOKEN_URL; + +/** + * apache http client方式实现. + * + * @author someone + */ +public class WxMpServiceHttpClientImpl extends BaseWxMpServiceImpl { + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.APACHE_HTTP; + } + + @Override + public void initHttp() { + WxMpConfigStorage configStorage = this.getWxMpConfigStorage(); + ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); + if (null == apacheHttpClientBuilder) { + apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); + } + + apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) + .httpProxyPort(configStorage.getHttpProxyPort()) + .httpProxyUsername(configStorage.getHttpProxyUsername()) + .httpProxyPassword(configStorage.getHttpProxyPassword()); + + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + final WxMpConfigStorage config = this.getWxMpConfigStorage(); + if (!config.isAccessTokenExpired() && !forceRefresh) { + return config.getAccessToken(); + } + + Lock lock = config.getAccessTokenLock(); + boolean locked = false; + try { + do { + locked = lock.tryLock(100, TimeUnit.MILLISECONDS); + if (!forceRefresh && !config.isAccessTokenExpired()) { + return config.getAccessToken(); + } + } while (!locked); + + String url = String.format(GET_ACCESS_TOKEN_URL.getUrl(config), config.getAppId(), config.getSecret()); + try { + HttpGet httpGet = new HttpGet(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig requestConfig = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpGet.setConfig(requestConfig); + } + try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { + return this.extractAccessToken(new BasicResponseHandler().handleResponse(response)); + } finally { + httpGet.releaseConnection(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (locked) { + lock.unlock(); + } + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java index 406e871267..79c3fad266 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java @@ -1,521 +1,12 @@ package me.chanjar.weixin.mp.api.impl; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.session.StandardSessionManager; -import me.chanjar.weixin.common.session.WxSessionManager; -import me.chanjar.weixin.common.util.RandomUtils; -import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.http.*; -import me.chanjar.weixin.mp.api.*; -import me.chanjar.weixin.mp.bean.*; -import me.chanjar.weixin.mp.bean.result.*; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.BasicResponseHandler; -import org.apache.http.impl.client.CloseableHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.concurrent.locks.Lock; - -public class WxMpServiceImpl implements WxMpService { - - private static final JsonParser JSON_PARSER = new JsonParser(); - - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - protected WxSessionManager sessionManager = new StandardSessionManager(); - private WxMpConfigStorage wxMpConfigStorage; - private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); - private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); - private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); - private WxMpUserService userService = new WxMpUserServiceImpl(this); - private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); - private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); - private WxMpCardService cardService = new WxMpCardServiceImpl(this); - private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); - private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); - private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); - private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this); - private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this); - private CloseableHttpClient httpClient; - private HttpHost httpProxy; - private int retrySleepMillis = 1000; - private int maxRetryTimes = 5; - - @Override - public boolean checkSignature(String timestamp, String nonce, String signature) { - try { - return SHA1.gen(this.getWxMpConfigStorage().getToken(), timestamp, nonce) - .equals(signature); - } catch (Exception e) { - return false; - } - } - - @Override - public String getAccessToken() throws WxErrorException { - return getAccessToken(false); - } - - @Override - public String getAccessToken(boolean forceRefresh) throws WxErrorException { - Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); - try { - lock.lock(); - - if (forceRefresh) { - this.getWxMpConfigStorage().expireAccessToken(); - } - - if (this.getWxMpConfigStorage().isAccessTokenExpired()) { - String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + - "&appid=" + this.getWxMpConfigStorage().getAppId() + "&secret=" - + this.getWxMpConfigStorage().getSecret(); - try { - HttpGet httpGet = new HttpGet(url); - if (this.httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(this.httpProxy).build(); - httpGet.setConfig(config); - } - try (CloseableHttpResponse response = getHttpclient().execute(httpGet)) { - String resultContent = new BasicResponseHandler().handleResponse(response); - WxError error = WxError.fromJson(resultContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), - accessToken.getExpiresIn()); - } finally { - httpGet.releaseConnection(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } finally { - lock.unlock(); - } - return this.getWxMpConfigStorage().getAccessToken(); - } - - @Override - public String getJsapiTicket() throws WxErrorException { - return getJsapiTicket(false); - } - - @Override - public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - Lock lock = this.getWxMpConfigStorage().getJsapiTicketLock(); - try { - lock.lock(); - - if (forceRefresh) { - this.getWxMpConfigStorage().expireJsapiTicket(); - } - - if (this.getWxMpConfigStorage().isJsapiTicketExpired()) { - String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); - JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); - String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); - int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.getWxMpConfigStorage().updateJsapiTicket(jsapiTicket, expiresInSeconds); - } - } finally { - lock.unlock(); - } - return this.getWxMpConfigStorage().getJsapiTicket(); - } - - @Override - public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { - long timestamp = System.currentTimeMillis() / 1000; - String noncestr = RandomUtils.getRandomStr(); - String jsapiTicket = getJsapiTicket(false); - String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, - "noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); - WxJsapiSignature jsapiSignature = new WxJsapiSignature(); - jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId()); - jsapiSignature.setTimestamp(timestamp); - jsapiSignature.setNonceStr(noncestr); - jsapiSignature.setUrl(url); - jsapiSignature.setSignature(signature); - return jsapiSignature; - } - - @Override - public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; - String responseContent = this.post(url, news.toJson()); - return WxMpMassUploadResult.fromJson(responseContent); - } - - @Override - public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo"; - String responseContent = this.post(url, video.toJson()); - return WxMpMassUploadResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"; - String responseContent = this.post(url, message.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/message/mass/send"; - String responseContent = this.post(url, message.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception { - String url = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"; - String responseContent = this.post(url, wxMpMassPreviewMessage.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public String shortUrl(String long_url) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/shorturl"; - JsonObject o = new JsonObject(); - o.addProperty("action", "long2short"); - o.addProperty("long_url", long_url); - String responseContent = this.post(url, o.toString()); - JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); - return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); - } - - @Override - public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { - String url = "https://api.weixin.qq.com/semantic/semproxy/search"; - String responseContent = this.post(url, semanticQuery.toJson()); - return WxMpSemanticQueryResult.fromJson(responseContent); - } - - @Override - public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) { - StringBuilder url = new StringBuilder(); - url.append("https://open.weixin.qq.com/connect/oauth2/authorize?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); - url.append("&response_type=code"); - url.append("&scope=").append(scope); - if (state != null) { - url.append("&state=").append(state); - } - url.append("#wechat_redirect"); - return url.toString(); - } - - @Override - public String buildQrConnectUrl(String redirectURI, String scope, - String state) { - StringBuilder url = new StringBuilder(); - url.append("https://open.weixin.qq.com/connect/qrconnect?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); - url.append("&response_type=code"); - url.append("&scope=").append(scope); - if (state != null) { - url.append("&state=").append(state); - } - - url.append("#wechat_redirect"); - return url.toString(); - } - - private WxMpOAuth2AccessToken getOAuth2AccessToken(StringBuilder url) throws WxErrorException { - try { - RequestExecutor executor = new SimpleGetRequestExecutor(); - String responseText = executor.execute(this.getHttpclient(), this.httpProxy, url.toString(), null); - return WxMpOAuth2AccessToken.fromJson(responseText); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/oauth2/access_token?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&secret=").append(this.getWxMpConfigStorage().getSecret()); - url.append("&code=").append(code); - url.append("&grant_type=authorization_code"); - - return this.getOAuth2AccessToken(url); - } - - @Override - public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/oauth2/refresh_token?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&grant_type=refresh_token"); - url.append("&refresh_token=").append(refreshToken); - - return this.getOAuth2AccessToken(url); - } - - @Override - public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/userinfo?"); - url.append("access_token=").append(oAuth2AccessToken.getAccessToken()); - url.append("&openid=").append(oAuth2AccessToken.getOpenId()); - if (lang == null) { - url.append("&lang=zh_CN"); - } else { - url.append("&lang=").append(lang); - } - - try { - RequestExecutor executor = new SimpleGetRequestExecutor(); - String responseText = executor.execute(getHttpclient(), this.httpProxy, url.toString(), null); - return WxMpUser.fromJson(responseText); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/auth?"); - url.append("access_token=").append(oAuth2AccessToken.getAccessToken()); - url.append("&openid=").append(oAuth2AccessToken.getOpenId()); - - try { - RequestExecutor executor = new SimpleGetRequestExecutor(); - executor.execute(getHttpclient(), this.httpProxy, url.toString(), null); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (WxErrorException e) { - return false; - } - return true; - } - - @Override - public String[] getCallbackIP() throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/getcallbackip"; - String responseContent = get(url, null); - JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); - JsonArray ipList = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); - String[] ipArray = new String[ipList.size()]; - for (int i = 0; i < ipList.size(); i++) { - ipArray[i] = ipList.get(i).getAsString(); - } - return ipArray; - } - - @Override - public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); - } - - @Override - public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); - } - - /** - * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 - */ - @Override - public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { - int retryTimes = 0; - do { - try { - T result = executeInternal(executor, uri, data); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, result); - return result; - } catch (WxErrorException e) { - if (retryTimes + 1 > this.maxRetryTimes) { - this.log.warn("重试达到最大次数【{}】", maxRetryTimes); - //最后一次重试失败后,直接抛出异常,不再等待 - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - WxError error = e.getError(); - // -1 系统繁忙, 1000ms后重试 - if (error.getErrorCode() == -1) { - int sleepMillis = this.retrySleepMillis * (1 << retryTimes); - try { - this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); - Thread.sleep(sleepMillis); - } catch (InterruptedException e1) { - throw new RuntimeException(e1); - } - } else { - throw e; - } - } - } while (retryTimes++ < this.maxRetryTimes); - - this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - protected synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { - if (uri.indexOf("access_token=") != -1) { - throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); - } - String accessToken = getAccessToken(false); - - String uriWithAccessToken = uri; - uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; - - try { - return executor.execute(getHttpclient(), this.httpProxy, uriWithAccessToken, data); - } catch (WxErrorException e) { - WxError error = e.getError(); - /* - * 发生以下情况时尝试刷新access_token - * 40001 获取access_token时AppSecret错误,或者access_token无效 - * 42001 access_token超时 - */ - if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { - // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token - this.getWxMpConfigStorage().expireAccessToken(); - if (this.getWxMpConfigStorage().autoRefreshToken()) { - return this.execute(executor, uri, data); - } - } - - if (error.getErrorCode() != 0) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, error); - throw new WxErrorException(error); - } - return null; - } catch (IOException e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uri, data, e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public HttpHost getHttpProxy() { - return this.httpProxy; - } - - public CloseableHttpClient getHttpclient() { - return this.httpClient; - } - - private void initHttpClient() { - WxMpConfigStorage configStorage = this.getWxMpConfigStorage(); - ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); - if (null == apacheHttpClientBuilder) { - apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); - } - - apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) - .httpProxyPort(configStorage.getHttpProxyPort()) - .httpProxyUsername(configStorage.getHttpProxyUsername()) - .httpProxyPassword(configStorage.getHttpProxyPassword()); - - if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { - this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); - } - - this.httpClient = apacheHttpClientBuilder.build(); - } - - @Override - public WxMpConfigStorage getWxMpConfigStorage() { - return this.wxMpConfigStorage; - } - - @Override - public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { - this.wxMpConfigStorage = wxConfigProvider; - this.initHttpClient(); - } - - @Override - public void setRetrySleepMillis(int retrySleepMillis) { - this.retrySleepMillis = retrySleepMillis; - } - - @Override - public void setMaxRetryTimes(int maxRetryTimes) { - this.maxRetryTimes = maxRetryTimes; - } - - @Override - public WxMpKefuService getKefuService() { - return this.kefuService; - } - - @Override - public WxMpMaterialService getMaterialService() { - return this.materialService; - } - - @Override - public WxMpMenuService getMenuService() { - return this.menuService; - } - - @Override - public WxMpUserService getUserService() { - return this.userService; - } - - @Override - public WxMpUserTagService getUserTagService() { - return this.tagService; - } - - @Override - public WxMpQrcodeService getQrcodeService() { - return this.qrCodeService; - } - - @Override - public WxMpCardService getCardService() { - return this.cardService; - } - - @Override - public WxMpDataCubeService getDataCubeService() { - return this.dataCubeService; - } - - @Override - public WxMpUserBlacklistService getBlackListService() { - return this.blackListService; - } - - @Override - public WxMpStoreService getStoreService() { - return this.storeService; - } - - @Override - public WxMpTemplateMsgService getTemplateMsgService() { - return this.templateMsgService; - } - - @Override - public WxMpDeviceService getDeviceService() { - return this.deviceService; - } +/** + *
    + * 默认接口实现类,使用apache httpclient实现
    + * Created by Binary Wang on 2017-5-27.
    + * 
    + * + * @author Binary Wang + */ +public class WxMpServiceImpl extends WxMpServiceHttpClientImpl { } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java new file mode 100644 index 0000000000..56b8eb12eb --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java @@ -0,0 +1,88 @@ +package me.chanjar.weixin.mp.api.impl; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.ProxyInfo; +import jodd.http.net.SocketHttpConnectionProvider; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_ACCESS_TOKEN_URL; + +/** + * jodd-http方式实现. + * + * @author someone + */ +public class WxMpServiceJoddHttpImpl extends BaseWxMpServiceImpl { + private HttpConnectionProvider httpClient; + private ProxyInfo httpProxy; + + @Override + public HttpConnectionProvider getRequestHttpClient() { + return httpClient; + } + + @Override + public ProxyInfo getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.JODD_HTTP; + } + + @Override + public void initHttp() { + + WxMpConfigStorage configStorage = this.getWxMpConfigStorage(); + + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); + } + + httpClient = new SocketHttpConnectionProvider(); + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + final WxMpConfigStorage config = this.getWxMpConfigStorage(); + if (!config.isAccessTokenExpired() && !forceRefresh) { + return config.getAccessToken(); + } + + Lock lock = config.getAccessTokenLock(); + boolean locked = false; + try { + do { + locked = lock.tryLock(100, TimeUnit.MILLISECONDS); + if (!forceRefresh && !config.isAccessTokenExpired()) { + return config.getAccessToken(); + } + } while (!locked); + String url = String.format(GET_ACCESS_TOKEN_URL.getUrl(config), config.getAppId(), config.getSecret()); + + HttpRequest request = HttpRequest.get(url); + if (this.getRequestHttpProxy() != null) { + SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); + provider.useProxy(getRequestHttpProxy()); + + request.withConnectionProvider(provider); + } + + return this.extractAccessToken(request.send().bodyText()); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (locked) { + lock.unlock(); + } + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java new file mode 100644 index 0000000000..6d6708349f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java @@ -0,0 +1,101 @@ +package me.chanjar.weixin.mp.api.impl; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import okhttp3.*; + +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_ACCESS_TOKEN_URL; + +/** + * okhttp实现. + * + * @author someone + */ +public class WxMpServiceOkHttpImpl extends BaseWxMpServiceImpl { + private OkHttpClient httpClient; + private OkHttpProxyInfo httpProxy; + + @Override + public OkHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public OkHttpProxyInfo getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.OK_HTTP; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + final WxMpConfigStorage config = this.getWxMpConfigStorage(); + if (!config.isAccessTokenExpired() && !forceRefresh) { + return config.getAccessToken(); + } + + Lock lock = config.getAccessTokenLock(); + boolean locked = false; + try { + do { + locked = lock.tryLock(100, TimeUnit.MILLISECONDS); + if (!forceRefresh && !config.isAccessTokenExpired()) { + return config.getAccessToken(); + } + } while (!locked); + String url = String.format(GET_ACCESS_TOKEN_URL.getUrl(config), config.getAppId(), config.getSecret()); + + Request request = new Request.Builder().url(url).get().build(); + Response response = getRequestHttpClient().newCall(request).execute(); + return this.extractAccessToken(Objects.requireNonNull(response.body()).string()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (locked) { + lock.unlock(); + } + } + } + + @Override + public void initHttp() { + WxMpConfigStorage wxMpConfigStorage = getWxMpConfigStorage(); + //设置代理 + if (wxMpConfigStorage.getHttpProxyHost() != null && wxMpConfigStorage.getHttpProxyPort() > 0) { + httpProxy = OkHttpProxyInfo.httpProxy(wxMpConfigStorage.getHttpProxyHost(), + wxMpConfigStorage.getHttpProxyPort(), + wxMpConfigStorage.getHttpProxyUsername(), + wxMpConfigStorage.getHttpProxyPassword()); + } + + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + if (httpProxy != null) { + clientBuilder.proxy(getRequestHttpProxy().getProxy()); + + //设置授权 + clientBuilder.authenticator(new Authenticator() { + @Override + public Request authenticate(Route route, Response response) throws IOException { + String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); + return response.request().newBuilder() + .header("Authorization", credential) + .build(); + } + }); + } + httpClient = clientBuilder.build(); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImpl.java new file mode 100644 index 0000000000..95c9fd79fd --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImpl.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.mp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxMpShakeService; +import me.chanjar.weixin.mp.bean.WxMpShakeInfoResult; +import me.chanjar.weixin.mp.bean.WxMpShakeQuery; +import me.chanjar.weixin.mp.bean.shake.*; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.ShakeAround.*; + +/** + * Created by rememberber on 2017/6/5. + * + * @author rememberber + */ +@RequiredArgsConstructor +public class WxMpShakeServiceImpl implements WxMpShakeService { + private final WxMpService wxMpService; + + @Override + public WxMpShakeInfoResult getShakeInfo(WxMpShakeQuery wxMpShakeQuery) throws WxErrorException { + String postData = wxMpShakeQuery.toJsonString(); + String responseContent = this.wxMpService.post(SHAKEAROUND_USER_GETSHAKEINFO, postData); + return WxMpShakeInfoResult.fromJson(responseContent); + } + + @Override + public WxMpShakeAroundPageAddResult pageAdd(WxMpShakeAroundPageAddQuery shakeAroundPageAddQuery) + throws WxErrorException { + String postData = shakeAroundPageAddQuery.toJsonString(); + String responseContent = this.wxMpService.post(SHAKEAROUND_PAGE_ADD, postData); + return WxMpShakeAroundPageAddResult.fromJson(responseContent); + } + + @Override + public WxError deviceBindPageQuery(WxMpShakeAroundDeviceBindPageQuery shakeAroundDeviceBindPageQuery) + throws WxErrorException { + String postData = shakeAroundDeviceBindPageQuery.toJsonString(); + String responseContent = this.wxMpService.post(SHAKEAROUND_DEVICE_BINDPAGE, postData); + return WxError.fromJson(responseContent, WxType.MP); + } + + @Override + public WxMpShakeAroundRelationSearchResult relationSearch(WxMpShakeAroundRelationSearchQuery searchQuery) + throws WxErrorException { + String postData = searchQuery.toJsonString(); + String responseContent = this.wxMpService.post(SHAKEAROUND_RELATION_SEARCH, postData); + return WxMpShakeAroundRelationSearchResult.fromJson(responseContent); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImpl.java index e1984204df..e1378efc5c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImpl.java @@ -1,41 +1,38 @@ package me.chanjar.weixin.mp.api.impl; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.google.gson.reflect.TypeToken; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.BeanUtils; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.WxMpStoreService; import me.chanjar.weixin.mp.bean.store.WxMpStoreBaseInfo; import me.chanjar.weixin.mp.bean.store.WxMpStoreInfo; import me.chanjar.weixin.mp.bean.store.WxMpStoreListResult; +import me.chanjar.weixin.mp.enums.WxMpApiUrl; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.util.List; /** - * Created by Binary Wang on 2016/9/26. - * @author binarywang (https://github.com/binarywang) + * Created by Binary Wang on 2016/9/26. * + * @author binarywang (https://github.com/binarywang) */ +@RequiredArgsConstructor public class WxMpStoreServiceImpl implements WxMpStoreService { - private static final String API_BASE_URL = "http://api.weixin.qq.com/cgi-bin/poi"; - - private WxMpService wxMpService; - - public WxMpStoreServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public void add(WxMpStoreBaseInfo request) throws WxErrorException { BeanUtils.checkRequiredFields(request); - String url = API_BASE_URL + "/addpoi"; - String response = this.wxMpService.post(url, request.toJson()); - WxError wxError = WxError.fromJson(response); + String response = this.wxMpService.post(WxMpApiUrl.Store.POI_ADD_URL, request.toJson()); + WxError wxError = WxError.fromJson(response, WxType.MP); if (wxError.getErrorCode() != 0) { throw new WxErrorException(wxError); } @@ -43,25 +40,23 @@ public void add(WxMpStoreBaseInfo request) throws WxErrorException { @Override public WxMpStoreBaseInfo get(String poiId) throws WxErrorException { - String url = API_BASE_URL + "/getpoi"; JsonObject paramObject = new JsonObject(); - paramObject.addProperty("poi_id",poiId); - String response = this.wxMpService.post(url, paramObject.toString()); - WxError wxError = WxError.fromJson(response); + paramObject.addProperty("poi_id", poiId); + String response = this.wxMpService.post(WxMpApiUrl.Store.POI_GET_URL, paramObject.toString()); + WxError wxError = WxError.fromJson(response, WxType.MP); if (wxError.getErrorCode() != 0) { throw new WxErrorException(wxError); } - return WxMpStoreBaseInfo.fromJson(new JsonParser().parse(response).getAsJsonObject() - .get("business").getAsJsonObject().get("base_info").toString()); + return WxMpStoreBaseInfo.fromJson(GsonParser.parse(response) + .get("business").getAsJsonObject().get("base_info").toString()); } @Override public void delete(String poiId) throws WxErrorException { - String url = API_BASE_URL + "/delpoi"; JsonObject paramObject = new JsonObject(); - paramObject.addProperty("poi_id",poiId); - String response = this.wxMpService.post(url, paramObject.toString()); - WxError wxError = WxError.fromJson(response); + paramObject.addProperty("poi_id", poiId); + String response = this.wxMpService.post(WxMpApiUrl.Store.POI_DEL_URL, paramObject.toString()); + WxError wxError = WxError.fromJson(response, WxType.MP); if (wxError.getErrorCode() != 0) { throw new WxErrorException(wxError); } @@ -69,14 +64,13 @@ public void delete(String poiId) throws WxErrorException { @Override public WxMpStoreListResult list(int begin, int limit) - throws WxErrorException { - String url = API_BASE_URL + "/getpoilist"; + throws WxErrorException { JsonObject params = new JsonObject(); params.addProperty("begin", begin); params.addProperty("limit", limit); - String response = this.wxMpService.post(url, params.toString()); + String response = this.wxMpService.post(WxMpApiUrl.Store.POI_LIST_URL, params.toString()); - WxError wxError = WxError.fromJson(response); + WxError wxError = WxError.fromJson(response, WxType.MP); if (wxError.getErrorCode() != 0) { throw new WxErrorException(wxError); } @@ -107,9 +101,8 @@ public List listAll() throws WxErrorException { @Override public void update(WxMpStoreBaseInfo request) throws WxErrorException { - String url = API_BASE_URL + "/updatepoi"; - String response = this.wxMpService.post(url, request.toJson()); - WxError wxError = WxError.fromJson(response); + String response = this.wxMpService.post(WxMpApiUrl.Store.POI_UPDATE_URL, request.toJson()); + WxError wxError = WxError.fromJson(response, WxType.MP); if (wxError.getErrorCode() != 0) { throw new WxErrorException(wxError); } @@ -117,16 +110,16 @@ public void update(WxMpStoreBaseInfo request) throws WxErrorException { @Override public List listCategories() throws WxErrorException { - String url = API_BASE_URL + "/getwxcategory"; - String response = this.wxMpService.get(url, null); - WxError wxError = WxError.fromJson(response); + String response = this.wxMpService.get(WxMpApiUrl.Store.POI_GET_WX_CATEGORY_URL, null); + WxError wxError = WxError.fromJson(response, WxType.MP); if (wxError.getErrorCode() != 0) { throw new WxErrorException(wxError); } return WxMpGsonBuilder.create().fromJson( - new JsonParser().parse(response).getAsJsonObject().get("category_list"), - new TypeToken>(){}.getType()); + GsonParser.parse(response).get("category_list"), + new TypeToken>() { + }.getType()); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpSubscribeMsgServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpSubscribeMsgServiceImpl.java new file mode 100644 index 0000000000..ff99b12c83 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpSubscribeMsgServiceImpl.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.mp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.URIUtil; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxMpSubscribeMsgService; +import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.SubscribeMsg.SEND_MESSAGE_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.SubscribeMsg.SUBSCRIBE_MESSAGE_AUTHORIZE_URL; + +/** + * 一次性订阅消息接口. + * + * @author Mklaus + * @date 2018-01-22 上午11:19 + */ +@RequiredArgsConstructor +public class WxMpSubscribeMsgServiceImpl implements WxMpSubscribeMsgService { + private final WxMpService wxMpService; + + @Override + public String subscribeMsgAuthorizationUrl(String redirectURI, int scene, String reserved) { + WxMpConfigStorage storage = this.wxMpService.getWxMpConfigStorage(); + return String.format(SUBSCRIBE_MESSAGE_AUTHORIZE_URL.getUrl(storage), storage.getAppId(), scene, storage.getTemplateId(), + URIUtil.encodeURIComponent(redirectURI), reserved); + } + + @Override + public boolean sendSubscribeMessage(WxMpSubscribeMessage message) throws WxErrorException { + if (message.getTemplateId() == null) { + message.setTemplateId(this.wxMpService.getWxMpConfigStorage().getTemplateId()); + } + + String responseContent = this.wxMpService.post(SEND_MESSAGE_URL, message.toJson()); + return responseContent != null; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImpl.java index 82ce829cdd..c4120022e1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImpl.java @@ -1,9 +1,11 @@ package me.chanjar.weixin.mp.api.impl; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.WxMpTemplateMsgService; import me.chanjar.weixin.mp.bean.template.WxMpTemplate; @@ -12,79 +14,72 @@ import java.util.List; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.TemplateMsg.*; + /** *
      * Created by Binary Wang on 2016-10-14.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@RequiredArgsConstructor public class WxMpTemplateMsgServiceImpl implements WxMpTemplateMsgService { - public static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/template"; - private static final JsonParser JSON_PARSER = new JsonParser(); - private WxMpService wxMpService; - public WxMpTemplateMsgServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public String sendTemplateMsg(WxMpTemplateMessage templateMessage) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; - String responseContent = this.wxMpService.post(url, templateMessage.toJson()); - final JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); + String responseContent = this.wxMpService.post(MESSAGE_TEMPLATE_SEND, templateMessage.toJson()); + final JsonObject jsonObject = GsonParser.parse(responseContent); if (jsonObject.get("errcode").getAsInt() == 0) { return jsonObject.get("msgid").getAsString(); } - throw new WxErrorException(WxError.fromJson(responseContent)); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); } @Override public boolean setIndustry(WxMpTemplateIndustry wxMpIndustry) throws WxErrorException { - if (null == wxMpIndustry.getPrimaryIndustry() || null == wxMpIndustry.getPrimaryIndustry().getId() - || null == wxMpIndustry.getSecondIndustry() || null == wxMpIndustry.getSecondIndustry().getId()) { + if (null == wxMpIndustry.getPrimaryIndustry() || null == wxMpIndustry.getPrimaryIndustry().getCode() + || null == wxMpIndustry.getSecondIndustry() || null == wxMpIndustry.getSecondIndustry().getCode()) { throw new IllegalArgumentException("行业Id不能为空,请核实"); } - String url = API_URL_PREFIX + "/api_set_industry"; - this.wxMpService.post(url, wxMpIndustry.toJson()); + this.wxMpService.post(TEMPLATE_API_SET_INDUSTRY, wxMpIndustry.toJson()); return true; } @Override public WxMpTemplateIndustry getIndustry() throws WxErrorException { - String url = API_URL_PREFIX + "/get_industry"; - String responseContent = this.wxMpService.get(url, null); + String responseContent = this.wxMpService.get(TEMPLATE_GET_INDUSTRY, null); return WxMpTemplateIndustry.fromJson(responseContent); } @Override public String addTemplate(String shortTemplateId) throws WxErrorException { - String url = API_URL_PREFIX + "/api_add_template"; JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("template_id_short", shortTemplateId); - String responseContent = this.wxMpService.post(url, jsonObject.toString()); - final JsonObject result = JSON_PARSER.parse(responseContent).getAsJsonObject(); + String responseContent = this.wxMpService.post(TEMPLATE_API_ADD_TEMPLATE, jsonObject.toString()); + final JsonObject result = GsonParser.parse(responseContent); if (result.get("errcode").getAsInt() == 0) { return result.get("template_id").getAsString(); } - throw new WxErrorException(WxError.fromJson(responseContent)); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); } @Override public List getAllPrivateTemplate() throws WxErrorException { - String url = API_URL_PREFIX + "/get_all_private_template"; - return WxMpTemplate.fromJson(this.wxMpService.get(url, null)); + return WxMpTemplate.fromJson(this.wxMpService.get(TEMPLATE_GET_ALL_PRIVATE_TEMPLATE, null)); } @Override public boolean delPrivateTemplate(String templateId) throws WxErrorException { - String url = API_URL_PREFIX + "/del_private_template"; JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("template_id", templateId); - String responseContent = this.wxMpService.post(url, jsonObject.toString()); - WxError error = WxError.fromJson(responseContent); + String responseContent = this.wxMpService.post(TEMPLATE_DEL_PRIVATE_TEMPLATE, jsonObject.toString()); + WxError error = WxError.fromJson(responseContent, WxType.MP); if (error.getErrorCode() == 0) { return true; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserBlacklistServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserBlacklistServiceImpl.java index b0b35343e8..a3c065e4d4 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserBlacklistServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserBlacklistServiceImpl.java @@ -1,50 +1,49 @@ package me.chanjar.weixin.mp.api.impl; -import com.google.gson.Gson; import com.google.gson.JsonObject; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.WxMpUserBlacklistService; import me.chanjar.weixin.mp.bean.result.WxMpUserBlacklistGetResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.util.HashMap; import java.util.List; import java.util.Map; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.UserBlacklist.*; + /** * @author miller */ +@RequiredArgsConstructor public class WxMpUserBlacklistServiceImpl implements WxMpUserBlacklistService { - private static final String API_BLACKLIST_PREFIX = "https://api.weixin.qq.com/cgi-bin/tags/members"; - private WxMpService wxMpService; - - public WxMpUserBlacklistServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public WxMpUserBlacklistGetResult getBlacklist(String nextOpenid) throws WxErrorException { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("begin_openid", nextOpenid); - String url = API_BLACKLIST_PREFIX + "/getblacklist"; - String responseContent = this.wxMpService.execute(new SimplePostRequestExecutor(), url, jsonObject.toString()); + String responseContent = this.wxMpService.execute(SimplePostRequestExecutor.create(this.wxMpService.getRequestHttp()), + GETBLACKLIST, jsonObject.toString()); return WxMpUserBlacklistGetResult.fromJson(responseContent); } @Override public void pushToBlacklist(List openidList) throws WxErrorException { - Map map = new HashMap<>(); + Map map = new HashMap<>(2); map.put("openid_list", openidList); - String url = API_BLACKLIST_PREFIX + "/batchblacklist"; - this.wxMpService.execute(new SimplePostRequestExecutor(), url, new Gson().toJson(map)); + this.wxMpService.execute(SimplePostRequestExecutor.create(this.wxMpService.getRequestHttp()), BATCHBLACKLIST, + WxMpGsonBuilder.create().toJson(map)); } @Override public void pullFromBlacklist(List openidList) throws WxErrorException { - Map map = new HashMap<>(); + Map map = new HashMap<>(2); map.put("openid_list", openidList); - String url = API_BLACKLIST_PREFIX + "/batchunblacklist"; - this.wxMpService.execute(new SimplePostRequestExecutor(), url, new Gson().toJson(map)); + this.wxMpService.execute(SimplePostRequestExecutor.create(this.wxMpService.getRequestHttp()), BATCHUNBLACKLIST, + WxMpGsonBuilder.create().toJson(map)); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java index c796cbd315..03c83e3b45 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java @@ -1,33 +1,37 @@ package me.chanjar.weixin.mp.api.impl; import com.google.gson.JsonObject; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.WxMpUserService; import me.chanjar.weixin.mp.bean.WxMpUserQuery; +import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid; import me.chanjar.weixin.mp.bean.result.WxMpUser; import me.chanjar.weixin.mp.bean.result.WxMpUserList; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.User.*; /** * Created by Binary Wang on 2016/7/21. + * + * @author BinaryWang */ +@RequiredArgsConstructor public class WxMpUserServiceImpl implements WxMpUserService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/user"; - private WxMpService wxMpService; - - public WxMpUserServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public void userUpdateRemark(String openid, String remark) throws WxErrorException { - String url = API_URL_PREFIX + "/info/updateremark"; JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("remark", remark); - this.wxMpService.post(url, json.toString()); + this.wxMpService.post(USER_INFO_UPDATE_REMARK_URL, json.toString()); } @Override @@ -37,32 +41,36 @@ public WxMpUser userInfo(String openid) throws WxErrorException { @Override public WxMpUser userInfo(String openid, String lang) throws WxErrorException { - String url = API_URL_PREFIX + "/info"; lang = lang == null ? "zh_CN" : lang; - String responseContent = this.wxMpService.get(url, - "openid=" + openid + "&lang=" + lang); + String responseContent = this.wxMpService.get(USER_INFO_URL, "openid=" + openid + "&lang=" + lang); return WxMpUser.fromJson(responseContent); } @Override - public WxMpUserList userList(String next_openid) throws WxErrorException { - String url = API_URL_PREFIX + "/get"; - String responseContent = this.wxMpService.get(url, - next_openid == null ? null : "next_openid=" + next_openid); + public WxMpUserList userList(String nextOpenid) throws WxErrorException { + String responseContent = this.wxMpService.get(USER_GET_URL, nextOpenid == null ? null : "next_openid=" + nextOpenid); return WxMpUserList.fromJson(responseContent); } @Override - public List userInfoList(List openids) - throws WxErrorException { - return this.userInfoList(new WxMpUserQuery(openids)); + public List changeOpenid(String fromAppid, List openidList) throws WxErrorException { + Map map = new HashMap<>(2); + map.put("from_appid", fromAppid); + map.put("openid_list", openidList); + String responseContent = this.wxMpService.post(USER_CHANGE_OPENID_URL, WxMpGsonBuilder.create().toJson(map)); + + return WxMpChangeOpenid.fromJsonList(responseContent); + } + + @Override + public List userInfoList(List openidList) + throws WxErrorException { + return this.userInfoList(new WxMpUserQuery(openidList)); } @Override public List userInfoList(WxMpUserQuery userQuery) throws WxErrorException { - String url = API_URL_PREFIX + "/info/batchget"; - String responseContent = this.wxMpService.post(url, - userQuery.toJsonString()); + String responseContent = this.wxMpService.post(USER_INFO_BATCH_GET_URL, userQuery.toJsonString()); return WxMpUser.fromJsonList(responseContent); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImpl.java index d1bdae1e2d..007942f096 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImpl.java @@ -1,68 +1,60 @@ package me.chanjar.weixin.mp.api.impl; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.google.gson.reflect.TypeToken; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.WxMpUserTagService; import me.chanjar.weixin.mp.bean.tag.WxTagListUser; import me.chanjar.weixin.mp.bean.tag.WxUserTag; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.UserTag.*; /** + * Created by Binary Wang on 2016/9/2. * - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/9/2. + * @author Binary Wang */ +@RequiredArgsConstructor public class WxMpUserTagServiceImpl implements WxMpUserTagService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/tags"; - - private WxMpService wxMpService; - - public WxMpUserTagServiceImpl(WxMpService wxMpService) { - this.wxMpService = wxMpService; - } + private final WxMpService wxMpService; @Override public WxUserTag tagCreate(String name) throws WxErrorException { - String url = API_URL_PREFIX + "/create"; JsonObject json = new JsonObject(); JsonObject tagJson = new JsonObject(); tagJson.addProperty("name", name); json.add("tag", tagJson); - String responseContent = this.wxMpService.post(url, json.toString()); + String responseContent = this.wxMpService.post(TAGS_CREATE, json.toString()); return WxUserTag.fromJson(responseContent); } @Override public List tagGet() throws WxErrorException { - String url = API_URL_PREFIX + "/get"; - - String responseContent = this.wxMpService.get(url, null); + String responseContent = this.wxMpService.get(TAGS_GET, null); return WxUserTag.listFromJson(responseContent); } @Override public Boolean tagUpdate(Long id, String name) throws WxErrorException { - String url = API_URL_PREFIX + "/update"; - JsonObject json = new JsonObject(); JsonObject tagJson = new JsonObject(); tagJson.addProperty("id", id); tagJson.addProperty("name", name); json.add("tag", tagJson); - String responseContent = this.wxMpService.post(url, json.toString()); - WxError wxError = WxError.fromJson(responseContent); + String responseContent = this.wxMpService.post(TAGS_UPDATE, json.toString()); + WxError wxError = WxError.fromJson(responseContent, WxType.MP); if (wxError.getErrorCode() == 0) { return true; } @@ -72,15 +64,13 @@ public Boolean tagUpdate(Long id, String name) throws WxErrorException { @Override public Boolean tagDelete(Long id) throws WxErrorException { - String url = API_URL_PREFIX + "/delete"; - JsonObject json = new JsonObject(); JsonObject tagJson = new JsonObject(); tagJson.addProperty("id", id); json.add("tag", tagJson); - String responseContent = this.wxMpService.post(url, json.toString()); - WxError wxError = WxError.fromJson(responseContent); + String responseContent = this.wxMpService.post(TAGS_DELETE, json.toString()); + WxError wxError = WxError.fromJson(responseContent, WxType.MP); if (wxError.getErrorCode() == 0) { return true; } @@ -89,23 +79,17 @@ public Boolean tagDelete(Long id) throws WxErrorException { } @Override - public WxTagListUser tagListUser(Long tagId, String nextOpenid) - throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/user/tag/get"; - + public WxTagListUser tagListUser(Long tagId, String nextOpenid) throws WxErrorException { JsonObject json = new JsonObject(); json.addProperty("tagid", tagId); json.addProperty("next_openid", StringUtils.trimToEmpty(nextOpenid)); - String responseContent = this.wxMpService.post(url, json.toString()); + String responseContent = this.wxMpService.post(TAG_GET, json.toString()); return WxTagListUser.fromJson(responseContent); } @Override - public boolean batchTagging(Long tagId, String[] openids) - throws WxErrorException { - String url = API_URL_PREFIX + "/members/batchtagging"; - + public boolean batchTagging(Long tagId, String[] openids) throws WxErrorException { JsonObject json = new JsonObject(); json.addProperty("tagid", tagId); JsonArray openidArrayJson = new JsonArray(); @@ -114,8 +98,8 @@ public boolean batchTagging(Long tagId, String[] openids) } json.add("openid_list", openidArrayJson); - String responseContent = this.wxMpService.post(url, json.toString()); - WxError wxError = WxError.fromJson(responseContent); + String responseContent = this.wxMpService.post(TAGS_MEMBERS_BATCHTAGGING, json.toString()); + WxError wxError = WxError.fromJson(responseContent, WxType.MP); if (wxError.getErrorCode() == 0) { return true; } @@ -124,10 +108,7 @@ public boolean batchTagging(Long tagId, String[] openids) } @Override - public boolean batchUntagging(Long tagId, String[] openids) - throws WxErrorException { - String url = API_URL_PREFIX + "/members/batchuntagging"; - + public boolean batchUntagging(Long tagId, String[] openids) throws WxErrorException { JsonObject json = new JsonObject(); json.addProperty("tagid", tagId); JsonArray openidArrayJson = new JsonArray(); @@ -136,8 +117,8 @@ public boolean batchUntagging(Long tagId, String[] openids) } json.add("openid_list", openidArrayJson); - String responseContent = this.wxMpService.post(url, json.toString()); - WxError wxError = WxError.fromJson(responseContent); + String responseContent = this.wxMpService.post(TAGS_MEMBERS_BATCHUNTAGGING, json.toString()); + WxError wxError = WxError.fromJson(responseContent, WxType.MP); if (wxError.getErrorCode() == 0) { return true; } @@ -147,16 +128,14 @@ public boolean batchUntagging(Long tagId, String[] openids) @Override public List userTagList(String openid) throws WxErrorException { - String url = API_URL_PREFIX + "/getidlist"; - JsonObject json = new JsonObject(); json.addProperty("openid", openid); - String responseContent = this.wxMpService.post(url, json.toString()); + String responseContent = this.wxMpService.post(TAGS_GETIDLIST, json.toString()); return WxMpGsonBuilder.create().fromJson( - new JsonParser().parse(responseContent).getAsJsonObject().get("tagid_list"), - new TypeToken>() { - }.getType()); + GsonParser.parse(responseContent).get("tagid_list"), + new TypeToken>() { + }.getType()); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpWifiServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpWifiServiceImpl.java new file mode 100644 index 0000000000..0b75bb996b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpWifiServiceImpl.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxMpWifiService; +import me.chanjar.weixin.mp.bean.wifi.WxMpWifiShopDataResult; +import me.chanjar.weixin.mp.bean.wifi.WxMpWifiShopListResult; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Wifi.*; + +/** + *
    + *  Created by BinaryWang on 2018/6/10.
    + * 
    + * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class WxMpWifiServiceImpl implements WxMpWifiService { + private final WxMpService wxMpService; + + @Override + public WxMpWifiShopListResult listShop(int pageIndex, int pageSize) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("pageindex", pageIndex); + json.addProperty("pagesize", pageSize); + final String result = this.wxMpService.post(BIZWIFI_SHOP_LIST, json.toString()); + return WxMpWifiShopListResult.fromJson(result); + } + + @Override + public WxMpWifiShopDataResult getShopWifiInfo(int shopId) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("shop_id", shopId); + return WxMpWifiShopDataResult.fromJson(this.wxMpService.post(BIZWIFI_SHOP_GET, json.toString())); + } + + @Override + public boolean updateShopWifiInfo(int shopId, String oldSsid, String ssid, String password) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("shop_id", shopId); + json.addProperty("old_ssid", oldSsid); + json.addProperty("ssid", ssid); + if (password != null) { + json.addProperty("password", password); + } + try { + this.wxMpService.post(BIZWIFI_SHOP_UPDATE, json.toString()); + return true; + } catch (WxErrorException e) { + throw e; + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxOAuth2ServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxOAuth2ServiceImpl.java new file mode 100644 index 0000000000..3c6287b7dd --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxOAuth2ServiceImpl.java @@ -0,0 +1,95 @@ +package me.chanjar.weixin.mp.api.impl; + +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.URIUtil; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxOAuth2Service; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.mp.bean.result.WxMpUser; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.*; + +/** + * oauth2 相关接口实现类. + * + * @author Binary Wang + * @date 2020-08-08 + */ +@RequiredArgsConstructor +public class WxOAuth2ServiceImpl implements WxOAuth2Service { + private final WxMpService wxMpService; + + @Override + public String buildAuthorizationUrl(String redirectURI, String scope, String state) { + return String.format(CONNECT_OAUTH2_AUTHORIZE_URL.getUrl(getMpConfigStorage()), + getMpConfigStorage().getAppId(), URIUtil.encodeURIComponent(redirectURI), scope, StringUtils.trimToEmpty(state)); + } + + private WxMpOAuth2AccessToken getOAuth2AccessToken(String url) throws WxErrorException { + try { + RequestExecutor executor = SimpleGetRequestExecutor.create(this.wxMpService.getRequestHttp()); + String responseText = executor.execute(url, null, WxType.MP); + return WxMpOAuth2AccessToken.fromJson(responseText); + } catch (IOException e) { + throw new WxErrorException(WxError.builder().errorCode(99999).errorMsg(e.getMessage()).build(), e); + } + } + + @Override + public WxMpOAuth2AccessToken getAccessToken(String code) throws WxErrorException { + String url = String.format(OAUTH2_ACCESS_TOKEN_URL.getUrl(getMpConfigStorage()), getMpConfigStorage().getAppId(), + getMpConfigStorage().getSecret(), code); + return this.getOAuth2AccessToken(url); + } + + @Override + public WxMpOAuth2AccessToken refreshAccessToken(String refreshToken) throws WxErrorException { + String url = String.format(OAUTH2_REFRESH_TOKEN_URL.getUrl(getMpConfigStorage()), getMpConfigStorage().getAppId(), refreshToken); + return this.getOAuth2AccessToken(url); + } + + protected WxMpConfigStorage getMpConfigStorage() { + return this.wxMpService.getWxMpConfigStorage(); + } + + @Override + public WxMpUser getUserInfo(WxMpOAuth2AccessToken token, String lang) throws WxErrorException { + if (lang == null) { + lang = "zh_CN"; + } + + String url = String.format(OAUTH2_USERINFO_URL.getUrl(getMpConfigStorage()), token.getAccessToken(), token.getOpenId(), lang); + + try { + RequestExecutor executor = SimpleGetRequestExecutor.create(this.wxMpService.getRequestHttp()); + String responseText = executor.execute(url, null, WxType.MP); + return WxMpUser.fromJson(responseText); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean validateAccessToken(WxMpOAuth2AccessToken token) { + String url = String.format(OAUTH2_VALIDATE_TOKEN_URL.getUrl(getMpConfigStorage()), token.getAccessToken(), token.getOpenId()); + + try { + SimpleGetRequestExecutor.create(this.wxMpService.getRequestHttp()).execute(url, null, WxType.MP); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (WxErrorException e) { + return false; + } + return true; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java deleted file mode 100644 index bc50ce059e..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.chanjar.weixin.mp.bean; - - -import me.chanjar.weixin.common.util.ToStringUtils; - -/** - * 微信卡券 - * - * @author YuJian - * @version 15/11/11 - */ -public class WxMpCard { - - private String cardId; - - private Long beginTime; - - private Long endTime; - - private String userCardStatus; - - private Boolean canConsume; - - public String getCardId() { - return this.cardId; - } - - public void setCardId(String cardId) { - this.cardId = cardId; - } - - public Long getBeginTime() { - return this.beginTime; - } - - public void setBeginTime(Long beginTime) { - this.beginTime = beginTime; - } - - public Long getEndTime() { - return this.endTime; - } - - public void setEndTime(Long endTime) { - this.endTime = endTime; - } - - public String getUserCardStatus() { - return this.userCardStatus; - } - - public void setUserCardStatus(String userCardStatus) { - this.userCardStatus = userCardStatus; - } - - public Boolean getCanConsume() { - return this.canConsume; - } - - public void setCanConsume(Boolean canConsume) { - this.canConsume = canConsume; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpHostConfig.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpHostConfig.java new file mode 100644 index 0000000000..9fff434e1f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpHostConfig.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.mp.bean; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 微信接口地址域名部分的自定义设置信息. + * + * @author Binary Wang + * @date 2019-06-09 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMpHostConfig { + public static final String API_DEFAULT_HOST_URL = "https://api.weixin.qq.com"; + public static final String MP_DEFAULT_HOST_URL = "https://mp.weixin.qq.com"; + public static final String OPEN_DEFAULT_HOST_URL = "https://open.weixin.qq.com"; + + /** + * 对应于:https://api.weixin.qq.com + */ + private String apiHost; + + /** + * 对应于:https://open.weixin.qq.com + */ + private String openHost; + /** + * 对应于:https://mp.weixin.qq.com + */ + private String mpHost; + + public static String buildUrl(WxMpHostConfig hostConfig, String prefix, String path) { + if (hostConfig == null) { + return prefix + path; + } + + if (hostConfig.getApiHost() != null && prefix.equals(API_DEFAULT_HOST_URL)) { + return hostConfig.getApiHost() + path; + } + + if (hostConfig.getMpHost() != null && prefix.equals(MP_DEFAULT_HOST_URL)) { + return hostConfig.getMpHost() + path; + } + + if (hostConfig.getOpenHost() != null && prefix.equals(OPEN_DEFAULT_HOST_URL)) { + return hostConfig.getOpenHost() + path; + } + + return prefix + path; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassNews.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassNews.java index 3e65fcded9..d4cbc046da 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassNews.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassNews.java @@ -1,149 +1,39 @@ package me.chanjar.weixin.mp.bean; -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import lombok.Data; +import me.chanjar.weixin.mp.bean.material.WxMpNewsArticle; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + /** - * 群发时用到的图文消息素材 + * 群发时用到的图文消息素材. * * @author chanjarster */ +@Data public class WxMpMassNews implements Serializable { - - /** - * - */ private static final long serialVersionUID = 565937155013581016L; - private List articles = new ArrayList<>(); - public List getArticles() { - return this.articles; - } + private List articles = new ArrayList<>(); - public void addArticle(WxMpMassNewsArticle article) { + public void addArticle(WxMpNewsArticle article) { this.articles.add(article); } public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } public boolean isEmpty() { return this.articles == null || this.articles.isEmpty(); } - /** - *
    -   * 群发图文消息article
    -   * 1. thumbMediaId  (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得
    -   * 2. author          图文消息的作者
    -   * 3. title           (必填) 图文消息的标题
    -   * 4. contentSourceUrl 在图文消息页面点击“阅读原文”后的页面链接
    -   * 5. content (必填)  图文消息页面的内容,支持HTML标签
    -   * 6. digest          图文消息的描述
    -   * 7, showCoverPic  是否显示封面,true为显示,false为不显示
    -   * 
    - * - * @author chanjarster - */ - public static class WxMpMassNewsArticle { - /** - * (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得 - */ - private String thumbMediaId; - /** - * 图文消息的作者 - */ - private String author; - /** - * (必填) 图文消息的标题 - */ - private String title; - /** - * 在图文消息页面点击“阅读原文”后的页面链接 - */ - private String contentSourceUrl; - /** - * (必填) 图文消息页面的内容,支持HTML标签 - */ - private String content; - /** - * 图文消息的描述 - */ - private String digest; - /** - * 是否显示封面,true为显示,false为不显示 - */ - private boolean showCoverPic; - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public String getAuthor() { - return this.author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getContentSourceUrl() { - return this.contentSourceUrl; - } - - public void setContentSourceUrl(String contentSourceUrl) { - this.contentSourceUrl = contentSourceUrl; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getDigest() { - return this.digest; - } - - public void setDigest(String digest) { - this.digest = digest; - } - - public boolean isShowCoverPic() { - return this.showCoverPic; - } - - public void setShowCoverPic(boolean showCoverPic) { - this.showCoverPic = showCoverPic; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java index e0b33f0dd7..ce1d77b62e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java @@ -1,5 +1,7 @@ package me.chanjar.weixin.mp.bean; +import lombok.Data; +import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; @@ -11,91 +13,52 @@ * * @author chanjarster */ +@Data public class WxMpMassOpenIdsMessage implements Serializable { private static final long serialVersionUID = -8022910911104788999L; + /** + * openid列表,最多支持10,000个 + */ private List toUsers = new ArrayList<>(); - private String msgType; - private String content; - private String mediaId; - private boolean sendIgnoreReprint = false; - - public WxMpMassOpenIdsMessage() { - super(); - } - - public String getMsgType() { - return this.msgType; - } /** *
        * 请使用
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_NEWS}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_VOICE}
    +   * {@link WxConsts.MassMsgType#IMAGE}
    +   * {@link WxConsts.MassMsgType#MPNEWS}
    +   * {@link WxConsts.MassMsgType#TEXT}
    +   * {@link WxConsts.MassMsgType#MPVIDEO}
    +   * {@link WxConsts.MassMsgType#VOICE}
        * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
        * 
    - * @param msgType */ - public void setMsgType(String msgType) { - this.msgType = msgType; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } + private String msgType; + private String content; + private String mediaId; + /** + * 文章被判定为转载时,是否继续进行群发操作。 + */ + private boolean sendIgnoreReprint = false; - public String getMediaId() { - return this.mediaId; - } + /** + * 开发者侧群发msgid,长度限制64字节,如不填,则后台默认以群发范围和群发内容的摘要值做为clientmsgid + */ + private String clientMsgId; - public void setMediaId(String mediaId) { - this.mediaId = mediaId; + public WxMpMassOpenIdsMessage() { + super(); } public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); - } - - /** - * openid列表,最多支持10,000个 - */ - public List getToUsers() { - return this.toUsers; + return WxMpGsonBuilder.create().toJson(this); } /** * 添加openid,最多支持10,000个 - * @param openid */ public void addUser(String openid) { this.toUsers.add(openid); } - /** - * 提供set方法,方便客户端直接设置所有群发对象的openid列表 - * @param toUsers - */ - public void setToUsers(List toUsers) { - this.toUsers = toUsers; - } - - public boolean isSendIgnoreReprint() { - return sendIgnoreReprint; - } - - /** - * - * @param sendIgnoreReprint 文章被判定为转载时,是否继续进行群发操作。 - */ - public void setSendIgnoreReprint(boolean sendIgnoreReprint) { - this.sendIgnoreReprint = sendIgnoreReprint; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java index d70ac57071..dca743c9c3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java @@ -1,5 +1,7 @@ package me.chanjar.weixin.mp.bean; +import lombok.Data; +import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; @@ -7,72 +9,33 @@ /** * @author miller */ +@Data public class WxMpMassPreviewMessage implements Serializable { private static final long serialVersionUID = 9095211638358424020L; + private String toWxUserName; private String toWxUserOpenid; - private String msgType; - private String content; - private String mediaId; - - public WxMpMassPreviewMessage() { - super(); - } - - public String getToWxUserName() { - return this.toWxUserName; - } - - public void setToWxUserName(String toWxUserName) { - this.toWxUserName = toWxUserName; - } - - public String getMsgType() { - return this.msgType; - } - /** *
    +   * 消息类型
        * 请使用
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_NEWS}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_VOICE}
    +   * {@link WxConsts.MassMsgType#IMAGE}
    +   * {@link WxConsts.MassMsgType#MPNEWS}
    +   * {@link WxConsts.MassMsgType#TEXT}
    +   * {@link WxConsts.MassMsgType#MPVIDEO}
    +   * {@link WxConsts.MassMsgType#VOICE}
        * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
        * 
    - * - * @param msgType 消息类型 */ - public void setMsgType(String msgType) { - this.msgType = msgType; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getToWxUserOpenid() { - return this.toWxUserOpenid; - } + private String msgType; + private String content; + private String mediaId; - public void setToWxUserOpenid(String toWxUserOpenid) { - this.toWxUserOpenid = toWxUserOpenid; + public WxMpMassPreviewMessage() { + super(); } public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java index bf1ded1a92..5e0b638e9f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java @@ -1,103 +1,64 @@ package me.chanjar.weixin.mp.bean; +import lombok.Data; +import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; /** - * 按标签群发的消息 + * 按标签群发的消息. * * @author chanjarster */ +@Data public class WxMpMassTagMessage implements Serializable { - private static final long serialVersionUID = -6625914040986749286L; - private Long tagId; - private String msgType; - private String content; - private String mediaId; - private boolean isSendAll = false; - private boolean sendIgnoreReprint = false; - - public WxMpMassTagMessage() { - super(); - } - - public String getMsgType() { - return this.msgType; - } + /** + * 标签id,如果不设置则就意味着发给所有用户. + */ + private Long tagId; /** *
    +   * 消息类型.
        * 请使用
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_NEWS}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#MASS_MSG_VOICE}
    +   * {@link WxConsts.MassMsgType#IMAGE}
    +   * {@link WxConsts.MassMsgType#MPNEWS}
    +   * {@link WxConsts.MassMsgType#TEXT}
    +   * {@link WxConsts.MassMsgType#MPVIDEO}
    +   * {@link WxConsts.MassMsgType#VOICE}
        * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
        * 
    - * - * @param msgType 消息类型 */ - public void setMsgType(String msgType) { - this.msgType = msgType; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); - } - - public Long getTagId() { - return this.tagId; - } - + private String msgType; + private String content; + private String mediaId; /** - * 如果不设置则就意味着发给所有用户 - * - * @param tagId 标签id + * 是否群发给所有用户. */ - public void setTagId(Long tagId) { - this.tagId = tagId; - } - - public boolean isSendIgnoreReprint() { - return sendIgnoreReprint; - } + private boolean isSendAll = false; /** - * - * @param sendIgnoreReprint 文章被判定为转载时,是否继续进行群发操作。 + * 文章被判定为转载时,是否继续进行群发操作. */ - public void setSendIgnoreReprint(boolean sendIgnoreReprint) { - this.sendIgnoreReprint = sendIgnoreReprint; - } + private boolean sendIgnoreReprint = false; /** - * 是否群发给所有用户 + * 开发者侧群发msgid,长度限制64字节,如不填,则后台默认以群发范围和群发内容的摘要值做为clientmsgid. */ - public boolean isSendAll() { - return isSendAll; + private String clientMsgId; + + public WxMpMassTagMessage() { + super(); + } + + public String toJson() { + return WxMpGsonBuilder.create().toJson(this); } public void setSendAll(boolean sendAll) { - if(sendAll){ + if (sendAll) { this.tagId = null; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassVideo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassVideo.java index ec94092cd1..2271e35370 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassVideo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassVideo.java @@ -1,49 +1,24 @@ package me.chanjar.weixin.mp.bean; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; /** * 群发时用到的视频素材 - * + * * @author chanjarster */ +@Data public class WxMpMassVideo implements Serializable { - - /** - * - */ private static final long serialVersionUID = 9153925016061915637L; + private String mediaId; private String title; private String description; - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpSemanticQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpSemanticQuery.java index c17f38e99f..f9de30f552 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpSemanticQuery.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpSemanticQuery.java @@ -1,22 +1,21 @@ package me.chanjar.weixin.mp.bean; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; /** * 语义理解查询用对象 - * + *

    * http://mp.weixin.qq.com/wiki/index.php?title=语义理解 * * @author Daniel Qian */ +@Data public class WxMpSemanticQuery implements Serializable { - - /** - * - */ private static final long serialVersionUID = 7685873048199870690L; + private String query; private String category; private Float latitude; @@ -26,70 +25,6 @@ public class WxMpSemanticQuery implements Serializable { private String appid; private String uid; - public String getQuery() { - return this.query; - } - - public void setQuery(String query) { - this.query = query; - } - - public String getCategory() { - return this.category; - } - - public void setCategory(String category) { - this.category = category; - } - - public Float getLatitude() { - return this.latitude; - } - - public void setLatitude(Float latitude) { - this.latitude = latitude; - } - - public Float getLongitude() { - return this.longitude; - } - - public void setLongitude(Float longitude) { - this.longitude = longitude; - } - - public String getCity() { - return this.city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getRegion() { - return this.region; - } - - public void setRegion(String region) { - this.region = region; - } - - public String getAppid() { - return this.appid; - } - - public void setAppid(String appid) { - this.appid = appid; - } - - public String getUid() { - return this.uid; - } - - public void setUid(String uid) { - this.uid = uid; - } - public String toJson() { return WxMpGsonBuilder.create().toJson(this); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeInfoResult.java new file mode 100644 index 0000000000..4cd6430000 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeInfoResult.java @@ -0,0 +1,60 @@ +package me.chanjar.weixin.mp.bean; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * 摇一摇周边:获取设备及用户信息接口返回JSON数据接收类 + * Created by rememberber on 2017/6/5. + * + * @author rememberber + */ +@Data +public class WxMpShakeInfoResult implements Serializable { + private static final long serialVersionUID = -1604561297395395468L; + + private Integer errcode; + + private String errmsg; + + private ShakeInfoData data; + + public static WxMpShakeInfoResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpShakeInfoResult.class); + } + + @Data + public class ShakeInfoData implements Serializable { + private static final long serialVersionUID = -4828142206067489488L; + + private String page_id; + + private String openid; + + private String poi_id; + + private String brand_userame; + + private BeaconInfo beacon_info; + + @Data + public class BeaconInfo implements Serializable { + private static final long serialVersionUID = -8995733049982933362L; + + private double distance; + + private Integer major; + + private Integer measure_power; + + private Integer minor; + + private Integer rssi; + + private String uuid; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeQuery.java new file mode 100644 index 0000000000..709e59e50f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeQuery.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.mp.bean; + +import com.google.gson.Gson; +import lombok.Data; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by rememberber on 2017/6/5. + * + * @author rememberber + */ +@Data +public class WxMpShakeQuery implements Serializable { + private static final long serialVersionUID = 4316527352035275412L; + + private String ticket; + + private int needPoi; + + public String toJsonString() { + Map map = new HashMap<>(); + map.put("ticket", this.ticket); + map.put("need_poi", this.needPoi); + return new Gson().toJson(map); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java index e97389b8f7..9e73b46159 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java @@ -1,189 +1,141 @@ -package me.chanjar.weixin.mp.bean; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.google.gson.Gson; - -/** - * 批量查询用户信息查询参数
    - * Created by LiuJunGuang on 2016/8/31. - * - * @author LiuJunGuang - */ -public class WxMpUserQuery { - private List queryParamList = new ArrayList<>(); - - public WxMpUserQuery() { - super(); - } - - /** - * 语言使用默认(zh_CN) - * - * @param openids - */ - public WxMpUserQuery(List openids) { - super(); - add(openids); - } - - /** - * 添加OpenId列表,语言使用默认(zh_CN) - * - * @param openids - * @return {@link WxMpUserQuery} - */ - public WxMpUserQuery add(List openids) { - for (String openid : openids) { - this.add(openid); - } - return this; - } - - /** - * 添加一个OpenId - * - * @param openid - * @param lang 国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 - * @return {@link WxMpUserQuery} - */ - public WxMpUserQuery add(String openid, String lang) { - this.queryParamList.add(new WxMpUserQueryParam(openid, lang)); - return this; - } - - /** - * 添加一个OpenId到列表中,并返回本对象 - * - *

    -   * 该方法默认lang = zh_CN
    -   * 
    - * - * @param openid - * @return {@link WxMpUserQuery} - */ - public WxMpUserQuery add(String openid) { - this.queryParamList.add(new WxMpUserQueryParam(openid)); - return this; - } - - /** - * 删除指定的OpenId,语言使用默认(zh_CN) - * - * @param openid - * @return {@link WxMpUserQuery} - */ - public WxMpUserQuery remove(String openid) { - this.queryParamList.remove(new WxMpUserQueryParam(openid)); - return this; - } - - /** - * 删除指定的OpenId - * - * @param openid - * @param lang 国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 - * @return {@link WxMpUserQuery} - */ - public WxMpUserQuery remove(String openid, String lang) { - this.queryParamList.remove(new WxMpUserQueryParam(openid, lang)); - return this; - } - - /** - * 获取查询参数列表 - * - */ - public List getQueryParamList() { - return this.queryParamList; - } - - public String toJsonString() { - Map map = new HashMap<>(); - map.put("user_list", this.queryParamList); - return new Gson().toJson(map); - } - - // 查询参数封装 - public class WxMpUserQueryParam implements Serializable { - private static final long serialVersionUID = -6863571795702385319L; - private String openid; - private String lang; - - public WxMpUserQueryParam(String openid, String lang) { - super(); - this.openid = openid; - this.lang = lang; - } - - public WxMpUserQueryParam(String openid) { - super(); - this.openid = openid; - this.lang = "zh_CN"; - } - - public WxMpUserQueryParam() { - super(); - } - - public String getOpenid() { - return this.openid; - } - - public void setOpenid(String openid) { - this.openid = openid; - } - - public String getLang() { - return this.lang; - } - - public void setLang(String lang) { - this.lang = lang; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + getOuterType().hashCode(); - result = prime * result + ((this.lang == null) ? 0 : this.lang.hashCode()); - result = prime * result + ((this.openid == null) ? 0 : this.openid.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - WxMpUserQueryParam other = (WxMpUserQueryParam) obj; - if (!getOuterType().equals(other.getOuterType())) - return false; - if (this.lang == null) { - if (other.lang != null) - return false; - } else if (!this.lang.equals(other.lang)) - return false; - if (this.openid == null) { - if (other.openid != null) - return false; - } else if (!this.openid.equals(other.openid)) - return false; - return true; - } - - private WxMpUserQuery getOuterType() { - return WxMpUserQuery.this; - } - - } - -} +package me.chanjar.weixin.mp.bean; + +import com.google.gson.Gson; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 批量查询用户信息查询参数
    + * Created by LiuJunGuang on 2016/8/31. + * + * @author LiuJunGuang + */ +@Data +public class WxMpUserQuery implements Serializable { + private static final long serialVersionUID = -1344224837373149313L; + + private List queryParamList = new ArrayList<>(); + + public WxMpUserQuery() { + super(); + } + + /** + * 语言使用默认(zh_CN) + * + * @param openids openid列表 + */ + public WxMpUserQuery(List openids) { + super(); + add(openids); + } + + /** + * 添加OpenId列表,语言使用默认(zh_CN) + * + * @param openids openid列表 + * @return {@link WxMpUserQuery} + */ + public WxMpUserQuery add(List openids) { + for (String openid : openids) { + this.add(openid); + } + return this; + } + + /** + * 添加一个OpenId + * + * @param openid openid + * @param lang 国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 + * @return {@link WxMpUserQuery} + */ + public WxMpUserQuery add(String openid, String lang) { + this.queryParamList.add(new WxMpUserQueryParam(openid, lang)); + return this; + } + + /** + * 添加一个OpenId到列表中,并返回本对象 + *

    + *

    +   * 该方法默认lang = zh_CN
    +   * 
    + * + * @param openid openid + * @return {@link WxMpUserQuery} + */ + public WxMpUserQuery add(String openid) { + this.queryParamList.add(new WxMpUserQueryParam(openid)); + return this; + } + + /** + * 删除指定的OpenId,语言使用默认(zh_CN) + * + * @param openid openid + * @return {@link WxMpUserQuery} + */ + public WxMpUserQuery remove(String openid) { + this.queryParamList.remove(new WxMpUserQueryParam(openid)); + return this; + } + + /** + * 删除指定的OpenId + * + * @param openid openid + * @param lang 国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 + * @return {@link WxMpUserQuery} + */ + public WxMpUserQuery remove(String openid, String lang) { + this.queryParamList.remove(new WxMpUserQueryParam(openid, lang)); + return this; + } + + /** + * 获取查询参数列表 + */ + public List getQueryParamList() { + return this.queryParamList; + } + + public String toJsonString() { + Map map = new HashMap<>(); + map.put("user_list", this.queryParamList); + return new Gson().toJson(map); + } + + // 查询参数封装 + @Data + public class WxMpUserQueryParam implements Serializable { + private static final long serialVersionUID = -6863571795702385319L; + private String openid; + private String lang; + + public WxMpUserQueryParam(String openid, String lang) { + this.openid = openid; + this.lang = lang; + } + + public WxMpUserQueryParam(String openid) { + this.openid = openid; + this.lang = "zh_CN"; + } + + public WxMpUserQueryParam() { + super(); + } + + private WxMpUserQuery getOuterType() { + return WxMpUserQuery.this; + } + + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Abstract.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Abstract.java new file mode 100644 index 0000000000..11e4f3a347 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Abstract.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 封面摘要. + * @author yuanqixun + * date:2018-08-25 00:35 + */ +@Data +public class Abstract implements Serializable { + private static final long serialVersionUID = -2612656133201770573L; + + /** + * 摘要. + */ + @SerializedName("abstract") + private String abstractInfo; + + /** + * 封面图片列表. + * 仅支持填入一 个封面图片链接, 上传图片接口 上传获取图片获得链接,填写 非CDN链接会报错,并在此填入。 建议图片尺寸像素850*350 + */ + @SerializedName("icon_url_list") + private String iconUrlList; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AbstractCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AbstractCardCreateRequest.java new file mode 100644 index 0000000000..7655b240db --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AbstractCardCreateRequest.java @@ -0,0 +1,17 @@ +package me.chanjar.weixin.mp.bean.card; + +import lombok.Data; + +import java.io.Serializable; + +/** + * . + * + * @author leeis + * @date 2018/12/29 + */ +@Data +public abstract class AbstractCardCreateRequest implements Serializable { + private static final long serialVersionUID = -260291223712818801L; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AdvancedInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AdvancedInfo.java new file mode 100644 index 0000000000..7ab5786dec --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AdvancedInfo.java @@ -0,0 +1,75 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.card.enums.BusinessServiceType; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 微信会员卡高级字段信息. + * + * @author yuanqixun + * date:2018-08-25 00:36 + */ +@Data +public class AdvancedInfo implements Serializable { + private static final long serialVersionUID = -8470424140133771841L; + + /** + * 使用门槛(条件). + * 若不填写使用条件则在券面拼写 :无最低消费限制,全场通用,不限品类;并在使用说明显示: 可与其他优惠共享 + */ + @SerializedName("use_condition") + private UseCondition useCondition; + + /** + * 封面摘要. + */ + @SerializedName("abstract") + private Abstract abstractInfo; + + /** + * 图文列表. + * 显示在详情内页 ,优惠券券开发者须至少传入 一组图文列表 + */ + @SerializedName("text_image_list") + private List textImageList; + + /** + * 商家服务类型. + * 数组类型:BIZ_SERVICE_DELIVER 外卖服务; BIZ_SERVICE_FREE_PARK 停车位; BIZ_SERVICE_WITH_PET 可带宠物; BIZ_SERVICE_FREE_WIFI 免费wifi, 可多选 + */ + @SerializedName("business_service") + private List businessServiceList; + + /** + * 使用时段限制. + */ + @SerializedName("time_limit") + private List timeLimits; + + /** + * 是否可以分享朋友. + */ + @SerializedName("share_friends") + private Boolean shareFriends; + + public void addBusinessService(BusinessServiceType businessServiceType) { + if (businessServiceType != null) { + if (businessServiceList == null) { + businessServiceList = new ArrayList<>(); + } + + businessServiceList.add(businessServiceType.name()); + } + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseInfo.java new file mode 100644 index 0000000000..8d73460d00 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseInfo.java @@ -0,0 +1,253 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 微信会员卡基本信息. + * @author yuanqixun + * date:2018-08-25 00:36 + */ +@Data +public class BaseInfo implements Serializable { + + /** + * 卡券的商户logo,建议像素为300*300. + */ + @SerializedName("logo_url") + private String logoUrl; + + /** + * Code展示类型. + * "CODE_TYPE_TEXT" 文本 "CODE_TYPE_BARCODE" 一维码 "CODE_TYPE_QRCODE" 二维码 "CODE_TYPE_ONLY_QRCODE" 仅显示二维码 "CODE_TYPE_ONLY_BARCODE" 仅显示一维码 "CODE_TYPE_NONE" 不显示任何码型 + */ + @SerializedName("code_type") + private String codeType = "CODE_TYPE_QRCODE"; + + /** + * 支付功能结构体,swipe_card结构. + */ + @SerializedName("pay_info") + private PayInfo payInfo; + + /** + * 是否设置该会员卡中部的按钮同时支持微信支付刷卡和会员卡二维码. + */ + @SerializedName("is_pay_and_qrcode") + private boolean isPayAndQrcode; + + /** + * 商户名字,字数上限为12个汉字. + */ + @SerializedName("brand_name") + private String brandName; + + /** + * 卡券名,字数上限为9个汉字 (建议涵盖卡券属性、服务及金额). + */ + @SerializedName("title") + private String title; + + /** + * 券颜色,按色彩规范标注填写Color010-Color100. + */ + @SerializedName("color") + private String color; + + /** + * 卡券使用提醒,字数上限为16个汉字. + */ + @SerializedName("notice") + private String notice; + + /** + * 卡券使用说明,字数上限为1024个汉字. + */ + @SerializedName("description") + private String description; + + /** + * 商品信息. + */ + @SerializedName("sku") + private Sku sku; + + /** + * 使用日期,有效期的信息. + */ + @SerializedName("date_info") + private DateInfo dateInfo; + + /** + * 是否自定义Code码,填写true或false. + * 默认为false 通常自有优惠码系统的开发者选择自定义Code码,详情见 是否自定义code + */ + @SerializedName("use_custom_code") + private boolean useCustomCode; + + /** + * 是否指定用户领取,填写true或false。默认为false. + */ + @SerializedName("bind_openid") + private boolean bindOpenid; + + /** + * 客服电话. + */ + @SerializedName("service_phone") + private String servicePhone; + + /** + * 门店位置ID,调用 POI门店管理接口 获取门店位置ID. + */ + @SerializedName("location_id_list") + private List locationIdList; + + /** + * 会员卡是否支持全部门店,填写后商户门店更新时会自动同步至卡券. + */ + @SerializedName("use_all_locations") + private boolean useAllLocations = true; + + /** + * 卡券中部居中的按钮,仅在卡券激活后且可用状态 时显示. + */ + @SerializedName("center_title") + private String centerTitle; + + /** + * 显示在入口下方的提示语,仅在卡券激活后且可用状态时显示. + */ + @SerializedName("center_sub_title") + private String centerSubTitle; + + /** + * 顶部居中的url,仅在卡券激活后且可用状态时显示. + */ + @SerializedName("center_url") + private String centerUrl; + + /** + * 自定义跳转外链的入口名字. + */ + @SerializedName("custom_url_name") + private String customUrlName; + + /** + * 自定义跳转的URL. + */ + @SerializedName("custom_url") + private String customUrl; + + /** + * 显示在入口右侧的提示语. + */ + @SerializedName("custom_url_sub_title") + private String customUrlSubTitle; + + /** + * 营销场景的自定义入口名称. + */ + @SerializedName("promotion_url_name") + private String promotionUrlName; + + /** + * 入口跳转外链的地址链接. + */ + @SerializedName("promotion_url") + private String promotionUrl; + + /** + * 显示在营销入口右侧的提示语. + */ + @SerializedName("promotion_url_sub_title") + private String promotionUrlSubTitle; + + /** + * 每人可领券的数量限制,建议会员卡每人限领一张. + */ + @SerializedName("get_limit") + private Integer getLimit = 1; + + /** + * 卡券领取页面是否可分享,默认为true. + */ + @SerializedName("can_share") + private boolean canShare; + + /** + * 卡券是否可转赠,默认为true. + */ + @SerializedName("can_give_friend") + private boolean canGiveFriend; + + /** + * 用户点击进入会员卡时推送事件. + * 填写true为用户点击进入会员卡时推送事件,默认为false。详情见 进入会员卡事件推送 + */ + @SerializedName("need_push_on_view") + private boolean needPushOnView; + + /** + * 微信小程序开放功能 小程序&卡券打通部分新增8个字段 https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1490190158&version=1&lang=zh_CN&platform=2 + * 自定义使用入口跳转小程序的user_name,格式为原始id+@app + */ + @SerializedName("custom_app_brand_user_name") + private String customAppBrandUserName; + /** + * 自定义使用入口小程序页面地址 + */ + @SerializedName("custom_app_brand_pass") + private String customAppBrandPass; + /** + * 小程序的user_name + */ + @SerializedName("center_app_brand_user_name") + private String centerAppBrandUserName; + /** + * 自定义居中使用入口小程序页面地址 + */ + @SerializedName("center_app_brand_pass") + private String centerAppBrandPass; + /** + * 小程序的user_name + */ + @SerializedName("promotion_app_brand_user_name") + private String promotionAppBrandUserName; + /** + * 自定义营销入口小程序页面地址 + */ + @SerializedName("promotion_app_brand_pass") + private String promotionAppBrandPass; + + /** + * 小程序的user_name, + */ + @SerializedName("activate_app_brand_user_name") + private String activateAppBrandUserName; + /** + * 激活小程序页面地址 + */ + @SerializedName("activate_app_brand_pass") + private String activateAppBrandPass; + + /** + * + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#2 + * “CARD_STATUS_NOT_VERIFY”,待审核 ; + * “CARD_STATUS_VERIFY_FAIL”,审核失败; + * “CARD_STATUS_VERIFY_OK”,通过审核; + * “CARD_STATUS_DELETE”,卡券被商户删除; + * “CARD_STATUS_DISPATCH”,在公众平台投放过的卡券 ; + */ + private String status; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseInfoUpdate.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseInfoUpdate.java new file mode 100644 index 0000000000..b0ec28082d --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseInfoUpdate.java @@ -0,0 +1,210 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信会员卡基本信息更新 + * @author yuanqixun + * date:2018-08-25 00:36 + */ +@Data +public class BaseInfoUpdate implements Serializable { + + /** + * 需要审核:卡券名,字数上限为9个汉字 (建议涵盖卡券属性、服务及金额). + */ + @SerializedName("title") + private String title; + + /** + * 卡券的商户logo,建议像素为300*300. + */ + @SerializedName("logo_url") + private String logoUrl; + + /** + * 卡券使用提醒,字数上限为16个汉字. + */ + @SerializedName("notice") + private String notice; + + /** + * 需要审核:卡券使用说明,字数上限为1024个汉字. + */ + @SerializedName("description") + private String description; + + /** + * 客服电话. + */ + @SerializedName("service_phone") + private String servicePhone; + + /** + * 券颜色,按色彩规范标注填写Color010-Color100. + */ + @SerializedName("color") + private String color; + + /** + * 门店位置ID,调用 POI门店管理接口 获取门店位置ID. + */ + @SerializedName("location_id_list") + private List locationIdList; + + /** + * 会员卡是否支持全部门店,填写后商户门店更新时会自动同步至卡券. + */ + @SerializedName("use_all_locations") + private Boolean useAllLocations; + + /** + * 卡券中部居中的按钮,仅在卡券激活后且可用状态 时显示. + */ + @SerializedName("center_title") + private String centerTitle; + + /** + * 显示在入口下方的提示语,仅在卡券激活后且可用状态时显示. + */ + @SerializedName("center_sub_title") + private String centerSubTitle; + + /** + * 顶部居中的url,仅在卡券激活后且可用状态时显示. + */ + @SerializedName("center_url") + private String centerUrl; + + /** + * 自定义跳转外链的入口名字. + */ + @SerializedName("custom_url_name") + private String customUrlName; + + /** + * 自定义跳转的URL. + */ + @SerializedName("custom_url") + private String customUrl; + + /** + * 显示在入口右侧的提示语. + */ + @SerializedName("custom_url_sub_title") + private String customUrlSubTitle; + + /** + * 营销场景的自定义入口名称. + */ + @SerializedName("promotion_url_name") + private String promotionUrlName; + + /** + * 入口跳转外链的地址链接. + */ + @SerializedName("promotion_url") + private String promotionUrl; + + /** + * 显示在营销入口右侧的提示语. + */ + @SerializedName("promotion_url_sub_title") + private String promotionUrlSubTitle; + + /** + * Code展示类型. + * "CODE_TYPE_TEXT" 文本 "CODE_TYPE_BARCODE" 一维码 "CODE_TYPE_QRCODE" 二维码 "CODE_TYPE_ONLY_QRCODE" 仅显示二维码 "CODE_TYPE_ONLY_BARCODE" 仅显示一维码 "CODE_TYPE_NONE" 不显示任何码型 + */ + @SerializedName("code_type") + private String codeType; + + /** + * 支付功能结构体,swipe_card结构. + */ + @SerializedName("pay_info") + private PayInfo payInfo; + + /** + * 是否设置该会员卡中部的按钮同时支持微信支付刷卡和会员卡二维码. + */ + @SerializedName("is_pay_and_qrcode") + private Boolean isPayAndQrcode; + + /** + * 每人可领券的数量限制,建议会员卡每人限领一张. + */ + @SerializedName("get_limit") + private Integer getLimit; + + /** + * 卡券领取页面是否可分享,默认为true. + */ + @SerializedName("can_share") + private Boolean canShare; + + /** + * 卡券是否可转赠,默认为true. + */ + @SerializedName("can_give_friend") + private Boolean canGiveFriend; + + /** + * 使用日期,有效期的信息. + */ + @SerializedName("date_info") + private DateInfo dateInfo; + + /** + * 微信小程序开放功能 小程序&卡券打通部分新增8个字段 https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1490190158&version=1&lang=zh_CN&platform=2 + * 自定义使用入口跳转小程序的user_name,格式为原始id+@app + */ + @SerializedName("custom_app_brand_user_name") + private String customAppBrandUserName; + /** + * 自定义使用入口小程序页面地址 + */ + @SerializedName("custom_app_brand_pass") + private String customAppBrandPass; + /** + * 小程序的user_name + */ + @SerializedName("center_app_brand_user_name") + private String centerAppBrandUserName; + /** + * 自定义居中使用入口小程序页面地址 + */ + @SerializedName("center_app_brand_pass") + private String centerAppBrandPass; + /** + * 小程序的user_name + */ + @SerializedName("promotion_app_brand_user_name") + private String promotionAppBrandUserName; + /** + * 自定义营销入口小程序页面地址 + */ + @SerializedName("promotion_app_brand_pass") + private String promotionAppBrandPass; + + /** + * 小程序的user_name, + */ + @SerializedName("activate_app_brand_user_name") + private String activateAppBrandUserName; + /** + * 激活小程序页面地址 + */ + @SerializedName("activate_app_brand_pass") + private String activateAppBrandPass; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseWxMpCardResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseWxMpCardResult.java new file mode 100644 index 0000000000..72c7420f01 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseWxMpCardResult.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +/** + * 卡券返回结果基础类. + * + * @author fanxl + * @date 2019/1/22 0022 10:08 + */ +public class BaseWxMpCardResult implements Serializable { + private static final long serialVersionUID = -3502867243738689870L; + + /** + * 错误码 + */ + private Integer errcode; + + /** + * 错误信息 + */ + private String errmsg; + + public boolean isSuccess() { + return 0 == errcode; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BonusRule.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BonusRule.java new file mode 100644 index 0000000000..6a5c425a21 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BonusRule.java @@ -0,0 +1,71 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 积分规则. + * + * @author yuanqixun + * date:2018-08-25 00:33 + */ +@Data +public class BonusRule implements Serializable { + private static final long serialVersionUID = -8698218402074475078L; + + /** + * 消费金额,以分为单位. + */ + @SerializedName("cost_money_unit") + private Integer costMoneyUnit; + + /** + * 对应增加的积分. + */ + @SerializedName("increase_bonus") + private Integer increaseBonus; + + /** + * 用户单次可获取的积分上限. + */ + @SerializedName("max_increase_bonus") + private Integer maxIncreaseBonus; + + /** + * 初始设置积分. + */ + @SerializedName("init_increase_bonus") + private Integer initIncreaseBonus; + + /** + * 每使用积分. + */ + @SerializedName("cost_bonus_unit") + private Integer costBonusUnit; + + /** + * 抵扣xx元,这里以分为单位). + */ + @SerializedName("reduce_money") + private Integer reduceMoney; + + /** + * 抵扣条件,满xx元(这里以分为单位)可用. + */ + @SerializedName("least_money_to_use_bonus") + private Integer leastMoneyToUseBonus; + + /** + * 抵扣条件,单笔最多使用xx积分. + */ + @SerializedName("max_reduce_bonus") + private Integer maxReduceBonus; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java new file mode 100644 index 0000000000..6e0f32edc4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @date 2018/12/29 + */ +@Data +public class Card implements Serializable { + private static final long serialVersionUID = -3697110761983756780L; + + /** + * 基本信息. + */ + @SerializedName("base_info") + private BaseInfo baseInfo; + + /** + * 创建优惠券特有的高级字段. + */ + @SerializedName("advanced_info") + private AdvancedInfo advancedInfo; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardUpdateResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardUpdateResult.java new file mode 100644 index 0000000000..42df19ff0f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardUpdateResult.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + * @author yqx + * @date 2018/11/07 + */ +@Data +public class CardUpdateResult { + + private int errcode; + + private String errmsg; + + /** + * 此次更新是否需要提审,true为需要,false为不需要。 + */ + @SerializedName("send_check") + private boolean sendCheck; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java new file mode 100644 index 0000000000..df5290b218 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public final class CashCard extends Card implements Serializable { + private static final long serialVersionUID = 6965491956462769745L; + + /** + * 代金券专用,表示起用金额(单位为分),如果无起用门槛则填0 + */ + @SerializedName("least_cost") + private int leastCost; + + /** + * 代金券专用,表示减免金额。(单位为分) + */ + @SerializedName("reduce_cost") + private int reduceCost; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static CashCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, CashCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java new file mode 100644 index 0000000000..ab4d54e477 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * + * @author leeis + * @date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class CashCardCreateRequest extends AbstractCardCreateRequest implements Serializable { + private static final long serialVersionUID = 8251635683908302125L; + + @SerializedName("card_type") + private String cardType = "CASH"; + + private CashCard cash; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CustomCell1.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CustomCell1.java new file mode 100644 index 0000000000..c598d24471 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CustomCell1.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 自定义会员信息类目. + * @author yuanqixun + * date:2018-08-25 00:34 + */ +@Data +public class CustomCell1 implements Serializable { + private static final long serialVersionUID = -6446192667149800447L; + + /** + * 入口名称. + */ + @SerializedName("name") + private String name; + + /** + * 入口右侧提示语,6个汉字内. + */ + @SerializedName("tips") + private String tips; + + /** + * 入口跳转链接. + */ + @SerializedName("url") + private String url; + + /** + * 参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V卡券内跳转小程序参数说明:会员卡自定义入口,包含以下两个字段 + */ + /** + * 自定义入口小程序user_name,格式为原始id+@app. + */ + @SerializedName("app_brand_user_name") + private String appBrandUserName; + /** + * 自定义入口小程序的页面路径. + */ + @SerializedName("app_brand_pass") + private String appBrandPass; + + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CustomField.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CustomField.java new file mode 100644 index 0000000000..a6b56b028f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CustomField.java @@ -0,0 +1,60 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 自定义会员信息类目. + * @author yuanqixun + * date:2018-08-25 00:34 + */ +@Data +public class CustomField implements Serializable { + private static final long serialVersionUID = -5364412812328198195L; + + /** + * 半自定义名称,当开发者变更这类类目信息的value值时 可以选择触发系统模板消息通知用户。 FIELD_NAME_TYPE_LEVEL 等级 FIELD_NAME_TYPE_COUPON 优惠券 FIELD_NAME_TYPE_STAMP 印花 FIELD_NAME_TYPE_DISCOUNT 折扣 FIELD_NAME_TYPE_ACHIEVEMEN 成就 FIELD_NAME_TYPE_MILEAGE 里程 FIELD_NAME_TYPE_SET_POINTS 集点 FIELD_NAME_TYPE_TIMS 次数 + */ + @SerializedName("name_type") + private String nameType; + + /** + * 自定义名称,当开发者变更这类类目信息的value值时 不会触发系统模板消息通知用户 + */ + @SerializedName("name") + private String name; + + /** + * 点击类目跳转外链url + */ + @SerializedName("url") + private String url; + + /** + * 参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V卡券内跳转小程序参数说明:会员卡顶部的信息类目字段,包含以下两个字段 + */ + /** + * 自定义信息类目小程序user_name,格式为原始id+@app + */ + @SerializedName("app_brand_user_name") + private String appBrandUserName; + /** + * 自定义信息类目小程序的页面路径 + */ + @SerializedName("app_brand_pass") + private String appBrandPass; + + + + public String getNameType() { + return nameType; + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DateInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DateInfo.java new file mode 100644 index 0000000000..a3fac5221f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DateInfo.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 使用日期,有效期的信息. + * @author yuanqixun + * date:2018-08-25 00:31 + */ +@Data +public class DateInfo implements Serializable { + private static final long serialVersionUID = 2734999880412106549L; + + /** + * 使用时间的类型. + * 支持固定时长有效类型 固定日期有效类型 永久有效类型:DATE_TYPE_FIX_TERM_RANGE、DATE_TYPE_FIX_TERM 、DATE_TYPE_PERMANENT + */ + @SerializedName("type") + private String type = "DATE_TYPE_PERMANENT"; + + /** + * 起用时间. + * type为DATE_TYPE_FIX_TIME_RANGE时专用, 表示起用时间。从1970年1月1日00:00:00至起用时间的秒数 ( 东八区时间,UTC+8,单位为秒 ) + */ + @SerializedName("begin_timestamp") + private Long beginTimestamp; + + /** + * 结束时间. + * type为DATE_TYPE_FIX_TERM_RANGE时专用,表示结束时间 ( 东八区时间,UTC+8,单位为秒 ) + */ + @SerializedName("end_timestamp") + private Long endTimestamp; + + /** + * 自领取后多少天内有效. + * type为DATE_TYPE_FIX_TERM时专用,表示自领取后多少天内有效,领取后当天有效填写0(单位为天) + */ + @SerializedName("fixed_term") + private Integer fixedTerm; + + /** + * 自领取后多少天开始生效. + * type为DATE_TYPE_FIX_TERM时专用,表示自领取后多少天开始生效。(单位为天) + */ + @SerializedName("fixed_begin_term") + private Integer fixedBeginTerm; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java new file mode 100644 index 0000000000..60c7c911bd --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.card; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public final class DiscountCard extends Card implements Serializable { + private static final long serialVersionUID = 1704610082472315077L; + + /** + * 折扣券专用,表示打折额度(百分比)。填30就是七折。 + */ + private int discount; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static DiscountCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, DiscountCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java new file mode 100644 index 0000000000..e125c19057 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DiscountCardCreateRequest extends AbstractCardCreateRequest implements Serializable { + private static final long serialVersionUID = 1190518086576489692L; + + @SerializedName("card_type") + private String cardType = "DISCOUNT"; + + private DiscountCard discount; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCoupon.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCoupon.java new file mode 100644 index 0000000000..df8194a6ac --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCoupon.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public final class GeneralCoupon extends Card implements Serializable { + private static final long serialVersionUID = -1577656733441132585L; + + /** + * 兑换券专用,填写兑换内容的名称. + */ + @SerializedName("default_detail") + private String defaultDetail; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static GeneralCoupon fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, GeneralCoupon.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCouponCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCouponCreateRequest.java new file mode 100644 index 0000000000..b44dc74cf8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCouponCreateRequest.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GeneralCouponCreateRequest extends AbstractCardCreateRequest implements Serializable { + private static final long serialVersionUID = 1771355872211267723L; + + @SerializedName("card_type") + private String cardType = "GENERAL_COUPON"; + + @SerializedName("general_coupon") + private GeneralCoupon generalCoupon; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java new file mode 100644 index 0000000000..1712b19eb7 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.mp.bean.card; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public final class GiftCard extends Card implements Serializable { + + private static final long serialVersionUID = -6168739707511792266L; + + /** + * 兑换券专用,填写兑换内容的名称。 + */ + private String gift; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static GiftCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, GiftCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java new file mode 100644 index 0000000000..a757b00f48 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GiftCardCreateRequest extends AbstractCardCreateRequest implements Serializable { + private static final long serialVersionUID = 1283655452584811858L; + + @SerializedName("card_type") + private String cardType = "GIFT"; + + private GiftCard gift; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java new file mode 100644 index 0000000000..ba343a435b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public final class GrouponCard extends Card implements Serializable { + + private static final long serialVersionUID = 3221312561666697005L; + + /** + * 团购券专用,团购详情 + */ + @SerializedName("deal_detail") + private String dealDetail; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static GrouponCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, GrouponCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java new file mode 100644 index 0000000000..1f001549e7 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GrouponCardCreateRequest extends AbstractCardCreateRequest implements Serializable { + private static final long serialVersionUID = 7551441058859934512L; + + @SerializedName("card_type") + private String cardType = "GROUPON"; + + private GrouponCard groupon; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/PayInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/PayInfo.java new file mode 100644 index 0000000000..c16c55c92c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/PayInfo.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 支付功能. + * @author yuanqixun + * date:2018-08-25 00:33 + */ +@Data +public class PayInfo implements Serializable { + + /** + * 刷卡功能 + */ + @SerializedName("swipe_card") + private SwipeCard swipeCard; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Sku.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Sku.java new file mode 100644 index 0000000000..005da7f06e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Sku.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 商品信息. + * @author yuanqixun + * date:2018-08-25 00:32 + */ +@Data +public class Sku implements Serializable { + + /** + * 卡券库存的数量,不支持填写0,上限为100000000 + */ + @SerializedName("quantity") + private Integer quantity = 100000000; + + /** + * 卡券全部库存的数量,上限为100000000。 + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#4 + */ + @SerializedName("total_quantity") + private Integer totalQuantity = 100000000; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/SwipeCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/SwipeCard.java new file mode 100644 index 0000000000..2e50a3cb9c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/SwipeCard.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 刷卡功能. + * @author yuanqixun + * date:2018-08-25 00:33 + */ +@Data +public class SwipeCard implements Serializable { + + /** + * 是否设置该会员卡支持拉出微信支付刷卡界面 + */ + @SerializedName("is_swipe_card") + private boolean isSwipeCard; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/TextImageList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/TextImageList.java new file mode 100644 index 0000000000..f974d1e4dc --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/TextImageList.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 图文列表. + * @author yuanqixun + * date:2018-08-25 00:35 + */ +@Data +public class TextImageList implements Serializable { + + /** + * 图片链接,必须调用 上传图片接口 上传图片获得链接,并在此填入, 否则报错 + */ + @SerializedName("image_url") + private String imageUrl; + + /** + * 图文描述. + */ + @SerializedName("text") + private String text; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/TimeLimit.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/TimeLimit.java new file mode 100644 index 0000000000..92bacfe1f6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/TimeLimit.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 使用时段限制. + * @author yuanqixun + * date:2018-08-25 00:34 + */ +@Data +public class TimeLimit implements Serializable { + + /** + * 限制类型枚举值,支持填入 MONDAY 周一 TUESDAY 周二 WEDNESDAY 周三 THURSDAY 周四 FRIDAY 周五 SATURDAY 周六 SUNDAY 周日 此处只控制显示, 不控制实际使用逻辑,不填默认不显示 + */ + @SerializedName("type") + private String type; + + /** + * 起始时间(小时),当前type类型下的起始时间(小时) ,如当前结构体内填写了MONDAY, 此处填写了10,则此处表示周一 10:00可用 + */ + @SerializedName("begin_hour") + private Integer beginHour; + + /** + * 起始时间(分钟),如当前结构体内填写了MONDAY, begin_hour填写10,此处填写了59, 则此处表示周一 10:59可用 + */ + @SerializedName("begin_minute") + private Integer beginMinute; + + /** + * 结束时间(小时),如当前结构体内填写了MONDAY, 此处填写了20, 则此处表示周一 10:00-20:00可用 + */ + @SerializedName("end_hour") + private Integer endHour; + + /** + * 结束时间(分钟),如当前结构体内填写了MONDAY, begin_hour填写10,此处填写了59, 则此处表示周一 10:59-00:59可用 + */ + @SerializedName("end_minute") + private Integer endMinute; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UseCondition.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UseCondition.java new file mode 100644 index 0000000000..a62066df66 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UseCondition.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +//子对象列表 + +/** + * 使用门槛 + * @author yuanqixun + * date:2018-08-25 00:35 + */ +@Data +public class UseCondition implements Serializable { + + /** + * 指定可用的商品类目,仅用于代金券类型 ,填入后将在券面拼写适用于xxx + */ + @SerializedName("accept_category") + private String acceptCategory; + + /** + * 指定不可用的商品类目,仅用于代金券类型 ,填入后将在券面拼写不适用于xxxx + */ + @SerializedName("reject_category") + private String rejectCategory; + + /** + * 满减门槛字段,可用于兑换券和代金券 ,填入后将在全面拼写消费满xx元可用 + */ + @SerializedName("least_cost") + private Integer leastCost; + + /** + * 购买xx可用类型门槛,仅用于兑换 ,填入后自动拼写购买xxx可用 + */ + @SerializedName("object_use_for") + private String objectUseFor; + + /** + * 不可以与其他类型共享门槛,填写false时系统将在使用须知里 拼写“不可与其他优惠共享”, 填写true时系统将在使用须知里 拼写“可与其他优惠共享”, 默认为true + */ + @SerializedName("can_use_with_other_discount") + private boolean canUseWithOtherDiscount; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UserCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UserCard.java new file mode 100644 index 0000000000..5985988e01 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UserCard.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 用户已领卡圈对象 + * @author yang229 + * @date 2019/12/22 + */ +@Data +public class UserCard implements java.io.Serializable { + /** + * 用户卡券code码 + */ + @SerializedName("code") + private String code; + + /** + * 卡券ID + */ + @SerializedName("card_id") + private String cardId; + + public static UserCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, UserCard.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCard.java new file mode 100644 index 0000000000..17314c90f6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCard.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.mp.bean.card; + + +import java.io.Serializable; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 微信卡券. + * + * @author YuJian + * @version 15/11/11 + */ +@Data +public class WxMpCard implements Serializable { + private static final long serialVersionUID = 9214301870017772921L; + + private String cardId; + + private Long beginTime; + + private Long endTime; + + private String userCardStatus; + + private String membershipNumber; + + private String code; + + private Integer bonus; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeCheckcodeResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeCheckcodeResult.java new file mode 100644 index 0000000000..84768c0916 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeCheckcodeResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.result.WxMpResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + + +@Data +public class WxMpCardCodeCheckcodeResult extends WxMpResult implements Serializable { + + private static final long serialVersionUID = -5128692403997016750L; + + /** + * 已经成功存入的code数目 + */ + @SerializedName("exist_code") + private List existCode; + + @SerializedName("not_exist_code") + private List notExistCode; + + + public static WxMpCardCodeCheckcodeResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardCodeCheckcodeResult.class); + } + + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeDepositCountResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeDepositCountResult.java new file mode 100644 index 0000000000..a7a114bf58 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeDepositCountResult.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.result.WxMpResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + + +@Data +public class WxMpCardCodeDepositCountResult extends WxMpResult implements Serializable { + + private static final long serialVersionUID = -6707587956061215868L; + + /** + * 已经成功存入的code数目 + */ + @SerializedName("count") + private Integer count; + + + public static WxMpCardCodeDepositCountResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardCodeDepositCountResult.class); + } + + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeDepositResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeDepositResult.java new file mode 100644 index 0000000000..aeb1246b8e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCodeDepositResult.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.result.WxMpResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + + +@Data +public class WxMpCardCodeDepositResult extends WxMpResult implements Serializable { + + private static final long serialVersionUID = 2955588617765355420L; + + /** + * 成功个数 + */ + @SerializedName("succ_code") + private Integer succCode; + + /** + * 重复导入的code会自动被过滤 + */ + @SerializedName("duplicate_code") + private Integer duplicateCode; + + /** + * 失败个数 + */ + @SerializedName("fail_code") + private Integer failCode; + + + public static WxMpCardCodeDepositResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardCodeDepositResult.class); + } + + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateRequest.java new file mode 100644 index 0000000000..051dad2911 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateRequest.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * + * @author IOMan + */ +@Data +public final class WxMpCardCreateRequest implements Serializable { + private static final long serialVersionUID = 5951280855309617585L; + + @SerializedName("card") + private AbstractCardCreateRequest cardCreateRequest; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static WxMpCardCreateRequest fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardCreateRequest.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateResult.java new file mode 100644 index 0000000000..60aded5aaa --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateResult.java @@ -0,0 +1,49 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * . + * @author yuanqixun + */ +@Data +public class WxMpCardCreateResult implements Serializable { + private static final long serialVersionUID = -128818731449449537L; + @SerializedName("card_id") + private String cardId; + private Integer errcode; + private String errmsg; + + public boolean isSuccess() { + return 0 == errcode; + } + + public static WxMpCardCreateResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardCreateResult.class); + } + + public static WxMpCardCreateResult failure(String errmsg) { + WxMpCardCreateResult result = new WxMpCardCreateResult(); + result.setErrcode(500); + result.setErrmsg(errmsg); + return result; + } + + public static WxMpCardCreateResult success() { + WxMpCardCreateResult result = new WxMpCardCreateResult(); + result.setErrcode(0); + result.setErrmsg("ok"); + return result; + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardDeleteResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardDeleteResult.java new file mode 100644 index 0000000000..8eedbebf60 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardDeleteResult.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.card; + +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 删除卡券结果. + * + * @author fanxl + * @date 2019/1/22 0022 10:24 + */ +public class WxMpCardDeleteResult extends BaseWxMpCardResult { + private static final long serialVersionUID = -4367717540650523290L; + + public static WxMpCardDeleteResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardDeleteResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardLandingPageCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardLandingPageCreateRequest.java new file mode 100644 index 0000000000..162cbd0afb --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardLandingPageCreateRequest.java @@ -0,0 +1,67 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import org.apache.commons.lang3.StringUtils; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +@Data +public class WxMpCardLandingPageCreateRequest implements Serializable { + + /** + * 页面的banner图片链接,须调用,建议尺寸为640*300。 + */ + private String banner; + + /** + * 页面的title + */ + @SerializedName("page_title") + private String title; + + @SerializedName("can_share") + private boolean canShare; + + /** + * 投放页面的场景值; + * SCENE_NEAR_BY 附近 + * SCENE_MENU 自定义菜单 + * SCENE_QRCODE 二维码 + * SCENE_ARTICLE 公众号文章 + * SCENE_H5 h5页面 + * SCENE_IVR 自动回复 + * SCENE_CARD_CUSTOM_CELL 卡券自定义cell + */ + private String scene; + + @SerializedName("card_list") + private JsonArray cardList; + + public void addCard(String cardId, String thumbUrl) { + if (StringUtils.isNoneBlank(cardId, thumbUrl)) { + if (cardList == null) { + cardList = new JsonArray(); + } + JsonObject cardJson = new JsonObject(); + cardJson.addProperty("card_id", cardId); + cardJson.addProperty("thumb_url", thumbUrl); + cardList.add(cardJson); + } + } + + public static WxMpCardLandingPageCreateRequest fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardLandingPageCreateRequest.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardLandingPageCreateResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardLandingPageCreateResult.java new file mode 100644 index 0000000000..8d7c4960ac --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardLandingPageCreateResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +@Data +public class WxMpCardLandingPageCreateResult implements Serializable { + private Integer errcode; + private String errmsg; + + /** + * 货架链接。 + */ + private String url; + /** + * 货架ID。货架的唯一标识 + */ + @SerializedName("page_id") + private Integer pageId; + + public boolean isSuccess() { + return 0 == errcode; + } + + public static WxMpCardLandingPageCreateResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardLandingPageCreateResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardMpnewsGethtmlResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardMpnewsGethtmlResult.java new file mode 100644 index 0000000000..13db310d5b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardMpnewsGethtmlResult.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.result.WxMpResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + + +@Data +public class WxMpCardMpnewsGethtmlResult extends WxMpResult implements Serializable { + + private static final long serialVersionUID = 6435268886823478711L; + + /** + * 返回一段html代码,可以直接嵌入到图文消息的正文里。即可以把这段代码嵌入到 上传图文消息素材接口 中的content字段里 + */ + @SerializedName("content") + private String content; + + public static WxMpCardMpnewsGethtmlResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardMpnewsGethtmlResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardQrcodeCreateResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardQrcodeCreateResult.java new file mode 100644 index 0000000000..d0140f35e2 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardQrcodeCreateResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +@Data +public class WxMpCardQrcodeCreateResult implements Serializable { + private static final long serialVersionUID = -128818731449449537L; + private Integer errcode; + private String errmsg; + private String ticket; + + @SerializedName("expire_seconds") + private Integer expireSeconds; + + private String url; + + @SerializedName("show_qrcode_url") + private String showQrcodeUrl; + + public boolean isSuccess() { + return 0 == errcode; + } + + public static WxMpCardQrcodeCreateResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardQrcodeCreateResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardResult.java new file mode 100644 index 0000000000..b85dd7a88a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardResult.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.mp.bean.card; + +import java.io.Serializable; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 卡券查询Code,核销Code接口返回结果. + * + * @author YuJian + * @version 15/11/11 + */ +@Data +public class WxMpCardResult implements Serializable { + private static final long serialVersionUID = -7950878428289035637L; + + private String errorCode; + + private String errorMsg; + + private String openId; + + private WxMpCard card; + + private String userCardStatus; + + private Boolean canConsume; + + private String outStr; + + private String backgroundPicUrl; + + private String unionid; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxUserCardListResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxUserCardListResult.java new file mode 100644 index 0000000000..9133a32f17 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxUserCardListResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.result.WxMpResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.util.List; + +/** + * 用户已领卡券返回 + * @author yang229 + * @date 2019/12/22 + */ +@Data +public class WxUserCardListResult extends WxMpResult implements java.io.Serializable { + + /** + * 卡券列表 + */ + @SerializedName("card_list") + private List cardList; + + /** + * 是否有可用的朋友的券 + */ + @SerializedName("has_share_card") + private Boolean hasShareCard; + + public static WxUserCardListResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxUserCardListResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/BusinessServiceType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/BusinessServiceType.java new file mode 100644 index 0000000000..3ae9cf8937 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/BusinessServiceType.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +/** + * 商户提供服务类型 + */ +public enum BusinessServiceType { + BIZ_SERVICE_DELIVER("外卖服务"), + BIZ_SERVICE_FREE_PARK("停车位"), + BIZ_SERVICE_WITH_PET("可带宠物"), + BIZ_SERVICE_FREE_WIFI("可带宠物"); + + private String description; + + BusinessServiceType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardCodeType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardCodeType.java new file mode 100644 index 0000000000..35263188e0 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardCodeType.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +public enum CardCodeType { + + CODE_TYPE_TEXT("文本"), + CODE_TYPE_NONE("不显示任何码型"), + CODE_TYPE_ONLY_BARCODE("仅显示一维码"), + CODE_TYPE_ONLY_QRCODE("仅显示二维码"), + CODE_TYPE_BARCODE("一维码"), + CODE_TYPE_QRCODE("二维码"); + + private String description; + + CardCodeType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardColor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardColor.java new file mode 100644 index 0000000000..0977cc9239 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardColor.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +/** + * 会员卡颜色 + * + * @author yuanqixun + * @date 2018-08-29 + */ +public enum CardColor { + Color010("#63b359"), + Color020("#2c9f67"), + Color030("#509fc9"), + Color040("#5885cf"), + Color050("#9062c0"), + Color060("#d09a45"), + Color070("#e4b138"), + Color080("#ee903c"), + Color081("#f08500"), + Color082("#a9d92d"), + Color090("#dd6549"), + Color100("#cc463d"), + Color101("#cf3e36"), + Color102("#5E6671"); + + private String type; + + CardColor(String type) { + this.type = type; + } + + public String getValue() { + return type; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardFieldType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardFieldType.java new file mode 100644 index 0000000000..4134f3e543 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardFieldType.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +/** + * 微信卡券激活字段类型 + * + * @author yuanqixun + * @date 2018-08-30 + */ +public enum CardFieldType { + COMMON_FIELD("微信选项"), + CUSTOM_FIELD("自定义选项"), + RICH_FIELD("自定义富文本类型"); + + private String description; + + CardFieldType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardRichFieldType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardRichFieldType.java new file mode 100644 index 0000000000..40d4b79fac --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardRichFieldType.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +/** + * 会员卡富文本字段类型 + * + * @author yuanqixun + * @date 2018-08-30 + */ +public enum CardRichFieldType { + FORM_FIELD_RADIO("自定义单选"), + FORM_FIELD_SELECT("自定义选择项"), + FORM_FIELD_CHECK_BOX("自定义多选"); + + private String description; + + CardRichFieldType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardSceneType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardSceneType.java new file mode 100644 index 0000000000..ec5b9fcfbc --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardSceneType.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +public enum CardSceneType { + SCENE_NEAR_BY("附近"), + SCENE_MENU("自定义菜单"), + SCENE_QRCODE("二维码"), + SCENE_ARTICLE("公众号文章"), + SCENE_H5("H5"), + SCENE_IVR("自动回复"), + SCENE_CARD_CUSTOM_CELL("卡券自定义cell"); + + private String description; + + CardSceneType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardStatusType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardStatusType.java new file mode 100644 index 0000000000..4108b7d4c2 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardStatusType.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +public enum CardStatusType { + CARD_STATUS_NOT_VERIFY("待审核"), + CARD_STATUS_VERIFY_FAIL("审核失败"), + CARD_STATUS_VERIFY_OK("通过审核"), + CARD_STATUS_DELETE("卡券被商户删除"), + CARD_STATUS_DISPATCH("在公众平台投放过的卡券"); + + private String description; + + CardStatusType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardWechatFieldType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardWechatFieldType.java new file mode 100644 index 0000000000..1d57bbda97 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CardWechatFieldType.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +/** + * 微信卡券激活字段类型 + * + * @author yuanqixun + * @date 2018-08-30 + */ +public enum CardWechatFieldType { + USER_FORM_INFO_FLAG_MOBILE("手机号"), + USER_FORM_INFO_FLAG_SEX("性别"), + USER_FORM_INFO_FLAG_NAME("姓名"), + USER_FORM_INFO_FLAG_BIRTHDAY("生日"), + USER_FORM_INFO_FLAG_IDCARD("身份证"), + USER_FORM_INFO_FLAG_EMAIL("邮箱"), + USER_FORM_INFO_FLAG_LOCATION("详细地址"), + + /** + * 原文档为 USER_FORM_INFO_FLAG_EDUCATION_BACKGRO, 测试不通过,可能是文档错误 + */ + USER_FORM_INFO_FLAG_EDUCATION_BACKGROUND("教育背景"), + USER_FORM_INFO_FLAG_INDUSTRY("行业"), + USER_FORM_INFO_FLAG_INCOME("收入"), + USER_FORM_INFO_FLAG_HABIT("兴趣爱好"); + + private String description; + + CardWechatFieldType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CustomFieldNameType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CustomFieldNameType.java new file mode 100644 index 0000000000..53f3df8cf9 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/CustomFieldNameType.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +/** + * 会员信息类目半自定义名称,当开发者变更这类类目信息的value值时 可以选择触发系统模板消息通知用户。 + */ +public enum CustomFieldNameType { + + FIELD_NAME_TYPE_LEVEL("等级"), + FIELD_NAME_TYPE_COUPON("优惠券"), + FIELD_NAME_TYPE_STAMP("印花"), + FIELD_NAME_TYPE_DISCOUNT("折扣"), + FIELD_NAME_TYPE_ACHIEVEMEN("成就"), + FIELD_NAME_TYPE_MILEAGE("里程"), + FIELD_NAME_TYPE_SET_POINTS("集点"), + FIELD_NAME_TYPE_TIMS("次数"); + + private String description; + + CustomFieldNameType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/DateInfoType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/DateInfoType.java new file mode 100644 index 0000000000..bd8a23551c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/enums/DateInfoType.java @@ -0,0 +1,17 @@ +package me.chanjar.weixin.mp.bean.card.enums; + +public enum DateInfoType { + DATE_TYPE_PERMANENT("永久有效类型"), + DATE_TYPE_FIX_TIME_RANGE("固定日期"), + DATE_TYPE_FIX_TERM("固定时长"); + + private String description; + + DateInfoType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/ActivatePluginParam.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/ActivatePluginParam.java new file mode 100644 index 0000000000..1ce8ef128c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/ActivatePluginParam.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + * @author yqx + * @date 2018/9/19 + */ +@Data +public class ActivatePluginParam { + + @SerializedName("encrypt_card_id") + String encryptCardId; + + @SerializedName("outer_str") + String outerStr; + + @SerializedName("biz") + String biz; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/ActivatePluginParamResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/ActivatePluginParamResult.java new file mode 100644 index 0000000000..8e7be799a4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/ActivatePluginParamResult.java @@ -0,0 +1,18 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import lombok.Data; + +/** + * @author yqx + * @date 2018/9/19 + */ +@Data +public class ActivatePluginParamResult { + + private int errcode; + + private String errmsg; + + private String url; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCard.java new file mode 100644 index 0000000000..5d23f5edb3 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCard.java @@ -0,0 +1,206 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.card.*; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * . + * @author yuanqixun + */ +@Data +public final class MemberCard implements Serializable { + private static final long serialVersionUID = 2922028551810647622L; + + /** + * 会员卡背景图. + */ + @SerializedName("background_pic_url") + private String backgroundPicUrl; + + /** + * 基本信息. + */ + @SerializedName("base_info") + private BaseInfo baseInfo; + + /** + * 特权说明. + */ + @SerializedName("prerogative") + private String prerogative; + + /** + * 自动激活. + */ + @SerializedName("auto_activate") + private boolean autoActivate; + + /** + * 显示积分. + */ + @SerializedName("supply_bonus") + private boolean supplyBonus; + + /** + * 查看积分外链,设置跳转外链查看积分详情。仅适用于积分无法通过激活接口同步的情况下使用该字段. + */ + @SerializedName("bonus_url") + private String bonusUrl; + + /** + * 支持储值. + */ + @SerializedName("supply_balance") + private boolean supplyBalance; + + /** + * 余额外链,仅适用于余额无法通过激活接口同步的情况下使用该字段. + */ + @SerializedName("balance_url") + private String balanceUrl; + + /** + * 自定义会员类目1,会员卡激活后显示. + */ + @SerializedName("custom_field1") + private CustomField customField1; + + /** + * 自定义会员类目2. + */ + @SerializedName("custom_field2") + private CustomField customField2; + + /** + * 自定义会员类目3. + */ + @SerializedName("custom_field3") + private CustomField customField3; + + /** + * 积分清零规则. + */ + @SerializedName("bonus_cleared") + private String bonusCleared; + + /** + * 积分规则. + */ + @SerializedName("bonus_rules") + private String bonusRules; + + /** + * 储值规则. + */ + @SerializedName("balance_rules") + private String balanceRules; + + /** + * 激活会员卡的url. + */ + @SerializedName("activate_url") + private String activateUrl; + + /** + * 激活会原卡url对应的小程序user_name,仅可跳转该公众号绑定的小程序. + */ + @SerializedName("activate_app_brand_user_name") + private String activateAppBrandUserName; + + /** + * 激活会原卡url对应的小程序path. + */ + @SerializedName("activate_app_brand_pass") + private String activateAppBrandPass; + + /** + * 自定义会员信息类目,会员卡激活后显示. + */ + @SerializedName("custom_cell1") + private CustomCell1 customCell1; + + /** + * 自定义会员信息类目,会员卡激活后显示. + */ + @SerializedName("custom_cell2") + private CustomCell1 customCell2; + + + /** + * 自定义会员信息类目,会员卡激活后显示. + */ + @SerializedName("custom_cell3") + private CustomCell1 customCell3; + + /** + * 积分规则,JSON结构积分规则. + */ + @SerializedName("bonus_rule") + private BonusRule bonusRule; + + /** + * 折扣,该会员卡享受的折扣优惠,填10就是九折. + */ + private Integer discount; + + /** + * 创建优惠券特有的高级字段. + */ + @SerializedName("advanced_info") + private AdvancedInfo advancedInfo; + + /** + * 是否支持一键激活 ,填true或false. + */ + @SerializedName("wx_activate") + private boolean wxActivate; + + /** + * 是否支持跳转型一键激活,填true或false. + */ + @SerializedName("wx_activate_after_submit") + private boolean wxActivateAfterSubmit; + + /** + * 跳转型一键激活跳转的地址链接,请填写http:// 或者https://开头的链接. + */ + @SerializedName("wx_activate_after_submit_url") + private String wxActivateAfterSubmitUrl; + + /** + * 参照https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V卡券内跳转小程序 + */ + /** + * 积分信息类目对应的小程序 user_name,格式为原始id+@app + */ + @SerializedName("bonus_app_brand_user_name") + private String bonusAppBrandUserName; + /** + *积分入口小程序的页面路径 + */ + @SerializedName("bonus_app_brand_pass") + private String bonusAppBrandPass; + /** + *余额信息类目对应的小程序 user_name,格式为原始id+@app + */ + @SerializedName("balance_app_brand_user_name") + private String balanceAppBrandUserName; + /** + *余额入口小程序的页面路径 + */ + @SerializedName("balance_app_brand_pass") + private String balanceAppBrandPass; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static MemberCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, MemberCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormRequest.java new file mode 100644 index 0000000000..8dd758c372 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormRequest.java @@ -0,0 +1,72 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * 会员卡激活,用户字段提交请求 + * + * @author yuanqixun + * @date 2018-08-30 + */ +@Data +public class MemberCardActivateUserFormRequest implements Serializable { + @SerializedName("card_id") + private String cardId; + + @SerializedName("service_statement") + private JsonObject serviceStatement; + + @SerializedName("bind_old_card") + private JsonObject bindOldCard; + + /** + * 必填项 + */ + @SerializedName("required_form") + private MemberCardUserForm requiredForm; + + /** + * 可选项 + */ + @SerializedName("optional_form") + private MemberCardUserForm optionalForm; + + /** + * 绑定老会员卡信息 + * + * @param name + * @param url + */ + public void setBindOldCard(String name, String url) { + if (StringUtils.isAnyEmpty(name, url)) { + return; + } + if (bindOldCard == null) { + bindOldCard = new JsonObject(); + } + bindOldCard.addProperty("name", name); + bindOldCard.addProperty("url", url); + } + + /** + * 设置服务声明,用于放置商户会员卡守则 + * + * @param name + * @param url + */ + public void setServiceStatement(String name, String url) { + if (StringUtils.isAnyEmpty(name, url)) { + return; + } + if (serviceStatement == null) { + serviceStatement = new JsonObject(); + } + serviceStatement.addProperty("name", name); + serviceStatement.addProperty("url", url); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormResult.java new file mode 100644 index 0000000000..8346770bf4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormResult.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import java.io.Serializable; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +@Data +public class MemberCardActivateUserFormResult implements Serializable { + private Integer errcode; + private String errmsg; + + public boolean isSuccess() { + return 0 == errcode; + } + + public static MemberCardActivateUserFormResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, MemberCardActivateUserFormResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardCreateRequest.java new file mode 100644 index 0000000000..9fb94e9ffa --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardCreateRequest.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * 创建会员卡请求对象. + * + * @author yuanqixun + */ +@Data +public class MemberCardCreateRequest implements Serializable { + private static final long serialVersionUID = -1044836608401698097L; + + @SerializedName("card_type") + private String cardType = "MEMBER_CARD"; + + @SerializedName("member_card") + private MemberCard memberCard; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUpdate.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUpdate.java new file mode 100644 index 0000000000..7220de7e50 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUpdate.java @@ -0,0 +1,159 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.card.BaseInfoUpdate; +import me.chanjar.weixin.mp.bean.card.BonusRule; +import me.chanjar.weixin.mp.bean.card.CustomCell1; +import me.chanjar.weixin.mp.bean.card.CustomField; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * 会员卡更新对象 + * @author yuanqixun + */ +@Data +public final class MemberCardUpdate implements Serializable { + private static final long serialVersionUID = -4755309390784904858L; + + //以下字段顺序根据微信官方文档顺序相同,不能传入非文档之外的字段 + //https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1466494654_K9rNz + + /** + * 基本信息. + */ + @SerializedName("base_info") + private BaseInfoUpdate baseInfo; + + /** + * 会员卡背景图. + */ + @SerializedName("background_pic_url") + private String backgroundPicUrl; + + /** + * 是否支持积分,仅支持从false变为true,默认为false + */ + @SerializedName("supply_bonus") + private Boolean supplyBonus; + + /** + * 积分清零规则. + */ + @SerializedName("bonus_cleared") + private String bonusCleared; + + /** + * 积分规则. + */ + @SerializedName("bonus_rules") + private String bonusRules; + + /** + * 查看积分外链,设置跳转外链查看积分详情。仅适用于积分无法通过激活接口同步的情况下使用该字段. + */ + @SerializedName("bonus_url") + private String bonusUrl; + + /** + * 余额外链,仅适用于余额无法通过激活接口同步的情况下使用该字段. + */ + @SerializedName("balance_url") + private String balanceUrl; + + /** + * 是否支持储值,仅支持从false变为true,默认为fals e 该字段须开通储值功能后方可使用, 详情见: 获取特殊权限 + */ + @SerializedName("supply_balance") + private Boolean supplyBalance; + + /** + * 储值规则. + */ + @SerializedName("balance_rules") + private String balanceRules; + + /** + * 特权说明. + */ + @SerializedName("prerogative") + private String prerogative; + + /** + * 自动激活. + */ + @SerializedName("auto_activate") + private Boolean autoActivate; + + /** + * 是否一键开卡. + */ + @SerializedName("wx_activate") + private Boolean wxActivate; + + /** + * 激活会员卡的url. + */ + @SerializedName("activate_url") + private String activateUrl; + + /** + * 自定义会员类目1,会员卡激活后显示. + */ + @SerializedName("custom_field1") + private CustomField customField1; + + /** + * 自定义会员类目2. + */ + @SerializedName("custom_field2") + private CustomField customField2; + + /** + * 自定义会员类目3. + */ + @SerializedName("custom_field3") + private CustomField customField3; + + /** + * 自定义会员信息类目,会员卡激活后显示. + */ + @SerializedName("custom_cell1") + private CustomCell1 customCell1; + + /** + * 自定义会员信息类目,会员卡激活后显示. + */ + @SerializedName("custom_cell2") + private CustomCell1 customCell2; + + + /** + * 自定义会员信息类目,会员卡激活后显示. + */ + @SerializedName("custom_cell3") + private CustomCell1 customCell3; + + /** + * 积分规则,JSON结构积分规则. + */ + @SerializedName("bonus_rule") + private BonusRule bonusRule; + + /** + * 折扣,该会员卡享受的折扣优惠,填10就是九折. + */ + private Integer discount; + + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static MemberCardUpdate fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, MemberCardUpdate.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUpdateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUpdateRequest.java new file mode 100644 index 0000000000..75aa990493 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUpdateRequest.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * 更新会员卡请求对象. + * + * @author yuanqixun + */ +@Data +public class MemberCardUpdateRequest implements Serializable { + private static final long serialVersionUID = -1025759626161614466L; + + @SerializedName("card_id") + private String cardId; + + @SerializedName("member_card") + private MemberCardUpdate memberCardUpdate; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserForm.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserForm.java new file mode 100644 index 0000000000..9edc0f89b4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserForm.java @@ -0,0 +1,97 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.card.enums.CardWechatFieldType; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 用户表单对象. + * + * @author yuanqixun + * @date 2018-08-30 + */ +@Data +public class MemberCardUserForm implements Serializable { + private static final long serialVersionUID = -1142881966808073662L; + + /** + * 当前结构(required_form或者optional_form )内的字段是否允许用户激活后再次修改, + * 商户设置为true 时,需要接收相应事件通知处理修改事件 + */ + @SerializedName("can_modify") + private boolean canModify; + + /** + * 富文本类型字段列表 + */ + @SerializedName("rich_field_list") + List richFieldList; + + /** + * 文本选项类型列表 + */ + @SerializedName("custom_field_list") + private List customFieldList; + + + /** + * 微信格式化的选项类型 + */ + @SerializedName("common_field_id_list") + private List wechatFieldIdList; + + /** + * 添加富文本类型字段 + * + */ + public void addRichField(MemberCardUserFormRichField field) { + if (field == null) { + return; + } + if (richFieldList == null) { + richFieldList = new ArrayList<>(); + } + richFieldList.add(field); + } + + /** + * 添加微信选项类型字段 + * + */ + public void addWechatField(CardWechatFieldType fieldType) { + if (fieldType == null) { + return; + } + if (wechatFieldIdList == null) { + wechatFieldIdList = new ArrayList<>(); + } + wechatFieldIdList.add(fieldType.name()); + } + + /** + * 添加文本类型字段 + * + */ + public void addCustomField(String field) { + if (StringUtils.isBlank(field)) { + return; + } + if (customFieldList == null) { + customFieldList = new ArrayList<>(); + } + customFieldList.add(field); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserFormRichField.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserFormRichField.java new file mode 100644 index 0000000000..a9837029df --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserFormRichField.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.card.enums.CardRichFieldType; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 富文本字段. + * + * @author yuanqixun + * @date 2018-08-30 + */ +@Data +public class MemberCardUserFormRichField { + + /** + * 富文本类型 + */ + @SerializedName("type") + private CardRichFieldType type; + + @SerializedName("name") + private String name; + + @SerializedName("values") + private List valueList; + + public void add(String value) { + if (valueList == null) { + valueList = new ArrayList<>(); + } + valueList.add(value); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserInfo.java new file mode 100644 index 0000000000..e411e19e96 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserInfo.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author YuJian + * @date 2017/7/11 + */ +@Data +public class MemberCardUserInfo implements Serializable { + private static final long serialVersionUID = -4259196162619282129L; + + private NameValues[] commonFieldList; + + private NameValues[] customFieldList; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NameValues.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NameValues.java new file mode 100644 index 0000000000..32275e8ce6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NameValues.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import lombok.Data; + +import java.io.Serializable; + +/** + * + * @author YuJian + * @date 2017/7/11 + */ +@Data +public class NameValues implements Serializable{ + private static final long serialVersionUID = -8529369702944594330L; + + private String name; + + private String value; + + private String[] valueList; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NotifyOptional.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NotifyOptional.java new file mode 100644 index 0000000000..139db68557 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NotifyOptional.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + *
    + * 控制原生消息结构体,包含各字段的消息控制字段。
    + *
    + * 用于 `7 更新会员信息` 的接口参数调用
    + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
    + * 
    + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/15 + */ +@Data +public class NotifyOptional implements Serializable { + private static final long serialVersionUID = 4488842021504939176L; + + /** + * 积分变动时是否触发系统模板消息,默认为true + */ + @SerializedName("is_notify_bonus") + private Boolean isNotifyBonus; + + /** + * 余额变动时是否触发系统模板消息,默认为true + */ + @SerializedName("is_notify_balance") + private Boolean isNotifyBalance; + + /** + * 自定义group1变动时是否触发系统模板消息,默认为false。(2、3同理) + */ + @SerializedName("is_notify_custom_field1") + private Boolean isNotifyCustomField1; + + @SerializedName("is_notify_custom_field2") + private Boolean isNotifyCustomField2; + + @SerializedName("is_notify_custom_field3") + private Boolean isNotifyCustomField3; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardActivateTempInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardActivateTempInfoResult.java new file mode 100644 index 0000000000..07b2d25719 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardActivateTempInfoResult.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + + +/** + * @author thomas + * @date 2019/4/26 + */ +@Data +public class WxMpMemberCardActivateTempInfoResult { + + private String errorCode; + + private String errorMsg; + + private MemberCardUserInfo userInfo; + + public static WxMpMemberCardActivateTempInfoResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpMemberCardActivateTempInfoResult.class); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardActivatedMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardActivatedMessage.java new file mode 100644 index 0000000000..b7379dfb86 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardActivatedMessage.java @@ -0,0 +1,78 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 会员卡激活接口的参数 + * + * @author YuJian(mgcnrx11@hotmail.com) + * @version 2017/7/8 + */ +@Data +public class WxMpMemberCardActivatedMessage implements Serializable { + private static final long serialVersionUID = -5972713484594266480L; + + /** + * 会员卡编号,由开发者填入,作为序列号显示在用户的卡包里。可与Code码保持等值。 + */ + @SerializedName("membership_number") + private String membershipNumber; + /** + * 领取会员卡用户获得的code + */ + private String code; + /** + * 卡券ID,自定义code卡券必填 + */ + @SerializedName("card_id") + private String cardId; + /** + * 商家自定义会员卡背景图,须先调用上传图片接口将背景图上传至CDN,否则报错。卡面设计请遵循微信会员卡自定义背景设计规范 + */ + @SerializedName("background_pic_url") + private String backgroundPicUrl; + /** + * 激活后的有效起始时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。 + */ + @SerializedName("activate_begin_time") + private Integer activateBeginTime; + /** + * 激活后的有效截至时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。 + */ + @SerializedName("activate_end_time") + private Integer activateEndTime; + /** + * 初始积分,不填为0。 + */ + @SerializedName("init_bonus") + private Integer initBonus; + /** + * 积分同步说明。 + */ + @SerializedName("init_bonus_record") + private String initBonusRecord; + /** + * 初始余额,不填为0。 + */ + @SerializedName("init_balance") + private Double initBalance; + /** + * 创建时字段custom_field1定义类型的初始值,限制为4个汉字,12字节。 + */ + @SerializedName("init_custom_field_value1") + private String initCustomFieldValue1; + /** + * 创建时字段custom_field2定义类型的初始值,限制为4个汉字,12字节。 + */ + @SerializedName("init_custom_field_value2") + private String initCustomFieldValue2; + /** + * 创建时字段custom_field3定义类型的初始值,限制为4个汉字,12字节。 + */ + @SerializedName("init_custom_field_value3") + private String initCustomFieldValue3; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardCreateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardCreateMessage.java new file mode 100644 index 0000000000..c1979751f8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardCreateMessage.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +@Data +public final class WxMpMemberCardCreateMessage implements Serializable { + + @SerializedName("card") + private MemberCardCreateRequest cardCreateRequest; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static WxMpMemberCardCreateMessage fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpMemberCardCreateMessage.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateMessage.java new file mode 100644 index 0000000000..86d3c0aff9 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateMessage.java @@ -0,0 +1,81 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + *
    + * 更新会员信息所需字段消息。
    + *
    + * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时
    + * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。
    + * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。
    + * 
    + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/15 + */ +@Data +public class WxMpMemberCardUpdateMessage implements Serializable { + private static final long serialVersionUID = 4953923160718911058L; + + /** + * 领取会员卡用户获得的code + */ + private String code; + /** + * 卡券ID,自定义code卡券必填 + */ + @SerializedName("card_id") + private String cardId; + /** + * 支持商家激活时针对单个会员卡分配自定义的会员卡背景 + */ + @SerializedName("background_pic_url") + private String backgroundPicUrl; + /** + * 需要设置的积分全量值,传入的数值会直接显示 + */ + private Integer bonus; + + /** + * 本次积分变动值,传负数代表减少 + */ + @SerializedName("add_bonus") + private Integer addBonus; + /** + * 商家自定义积分消耗记录,不超过14个汉字 + */ + @SerializedName("record_bonus") + private String recordBonus; + /** + * 需要设置的余额全量值,传入的数值会直接显示在卡面 + */ + private Double balance; + /** + * 本次余额变动值,传负数代表减少 + */ + @SerializedName("add_balance") + private Double addBalance; + /** + * 商家自定义金额消耗记录,不超过14个汉字。 + */ + @SerializedName("record_balance") + private String recordBalance; + + /** + * 创建时字段custom_field定义类型的最新数值,限制为4个汉字,12字节。 + */ + @SerializedName("custom_field_value1") + private String customFieldValue1; + @SerializedName("custom_field_value2") + private String customFieldValue2; + @SerializedName("custom_field_value3") + private String customFieldValue3; + + @SerializedName("notify_optional") + private NotifyOptional notifyOptional; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResult.java new file mode 100644 index 0000000000..663fe1f1e5 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResult.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import java.io.Serializable; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + *
    + * 用于 `7 更新会员信息` 的接口调用后的返回结果
    + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
    + * 
    + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/15 + */ +@Data +public class WxMpMemberCardUpdateResult implements Serializable { + + private static final long serialVersionUID = 9084886191442098311L; + + private String errorCode; + + private String errorMsg; + + private String openId; + + private Integer resultBonus; + + private Double resultBalance; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static WxMpMemberCardUpdateResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpMemberCardUpdateResult.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResult.java new file mode 100644 index 0000000000..8fad40ccf8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResult.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import java.io.Serializable; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + *
    + * 拉取会员信息返回的结果
    + *
    + * 字段格式参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283  6.2.1小节的步骤5
    + * 
    + * + * @author YuJian + * @version 2017/7/9 + */ +@Data +public class WxMpMemberCardUserInfoResult implements Serializable { + + private static final long serialVersionUID = 9084777967442098311L; + + private String errorCode; + + private String errorMsg; + + private String openId; + + private String nickname; + + private String membershipNumber; + + private Integer bonus; + + private Double balance; + + private String sex; + + private MemberCardUserInfo userInfo; + + private String userCardStatus; + + private Boolean hasActive; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static WxMpMemberCardUserInfoResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpMemberCardUserInfoResult.class); + } +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/comment/WxMpCommentListVo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/comment/WxMpCommentListVo.java new file mode 100644 index 0000000000..10d1dafcad --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/comment/WxMpCommentListVo.java @@ -0,0 +1,87 @@ +package me.chanjar.weixin.mp.bean.comment; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 群发图文评论数据. + * + * @author Binary Wang + * @date 2019-08-30 + */ +@Data +public class WxMpCommentListVo implements Serializable { + private static final long serialVersionUID = 7604754799359751247L; + + /** + * 总数,非comment的size. + */ + private Integer total; + + /** + * 评论列表. + */ + private List comment; + + @Data + public static class Reply implements Serializable { + private static final long serialVersionUID = 9174739515408520429L; + + /** + * 作者回复时间 . + */ + @SerializedName("create_time") + private String createTime; + + /** + * 作者回复内容. + */ + private String content; + } + + @Data + public static class WxMpComment implements Serializable { + private static final long serialVersionUID = 5401188720891942634L; + + /** + * 用户评论id . + */ + @SerializedName("user_comment_id") + private Integer userCommentId; + + /** + * 用户openid. + */ + private String openid; + + /** + * 评论时间. + */ + @SerializedName("create_time") + private String createTime; + + /** + * 评论内容. + */ + private String content; + + /** + * 是否精选评论,0为即非精选,1为true,即精选. + */ + @SerializedName("comment_type") + private Integer commentType; + + /** + * 作者回复. + */ + private Reply reply; + } + + public static WxMpCommentListVo fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCommentListVo.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleResult.java index 35cae74d5e..d90aba2e36 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleResult.java @@ -1,20 +1,25 @@ package me.chanjar.weixin.mp.bean.datacube; -import com.google.gson.JsonParser; +import java.util.List; + import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** * 图文分析数据接口返回结果对象 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/8/24. + *

    + * Created by Binary Wang on 2016/8/24. + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) public class WxDataCubeArticleResult extends WxDataCubeBaseResult { - - private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = -9222452497954511765L; /** * ref_hour @@ -104,119 +109,17 @@ public class WxDataCubeArticleResult extends WxDataCubeBaseResult { /** * user_source - * 在获取图文阅读分时数据时才有该字段,代表用户从哪里进入来阅读该图文。0:会话;1.好友;2.朋友圈;3.腾讯微博;4.历史消息页;5.其他 + * 在获取图文统计数据、图文阅读分时数据时才有该字段,代表用户从哪里进入来阅读该图文。 + * 99999999.全部;0:会话;1.好友;2.朋友圈;3.腾讯微博;4.历史消息页;5.其他;6.看一看;7.搜一搜; */ @SerializedName("user_source") private Integer userSource; - public Integer getRefHour() { - return this.refHour; - } - - public void setRefHour(Integer refHour) { - this.refHour = refHour; - } - - public String getMsgId() { - return this.msgId; - } - - public void setMsgId(String msgId) { - this.msgId = msgId; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public Integer getIntPageReadUser() { - return this.intPageReadUser; - } - - public void setIntPageReadUser(Integer intPageReadUser) { - this.intPageReadUser = intPageReadUser; - } - - public Integer getIntPageReadCount() { - return this.intPageReadCount; - } - - public void setIntPageReadCount(Integer intPageReadCount) { - this.intPageReadCount = intPageReadCount; - } - - public Integer getOriPageReadUser() { - return this.oriPageReadUser; - } - - public void setOriPageReadUser(Integer oriPageReadUser) { - this.oriPageReadUser = oriPageReadUser; - } - - public Integer getOriPageReadCount() { - return this.oriPageReadCount; - } - - public void setOriPageReadCount(Integer oriPageReadCount) { - this.oriPageReadCount = oriPageReadCount; - } - - public Integer getShareScene() { - return this.shareScene; - } - - public void setShareScene(Integer shareScene) { - this.shareScene = shareScene; - } - - public Integer getShareUser() { - return this.shareUser; - } - - public void setShareUser(Integer shareUser) { - this.shareUser = shareUser; - } - - public Integer getShareCount() { - return this.shareCount; - } - - public void setShareCount(Integer shareCount) { - this.shareCount = shareCount; - } - - public Integer getAddToFavUser() { - return this.addToFavUser; - } - - public void setAddToFavUser(Integer addToFavUser) { - this.addToFavUser = addToFavUser; - } - - public Integer getAddToFavCount() { - return this.addToFavCount; - } - - public void setAddToFavCount(Integer addToFavCount) { - this.addToFavCount = addToFavCount; - } - - public Integer getUserSource() { - return this.userSource; - } - - public void setUserSource(Integer userSource) { - this.userSource = userSource; - } - public static List fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson( - JSON_PARSER.parse(json).getAsJsonObject().get("list"), - new TypeToken>() { - }.getType()); + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("list"), + new TypeToken>() { + }.getType()); } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotal.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotal.java index d3236be3be..bb732c34d7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotal.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotal.java @@ -2,70 +2,50 @@ import java.util.List; -import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; - +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * 图文分析数据接口返回结果对象 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/8/24. + * 图文分析数据接口返回结果对象. + * Created by Binary Wang on 2016/8/24. + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) public class WxDataCubeArticleTotal extends WxDataCubeBaseResult { - - private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = -7634365687303052699L; /** - * msgid + * msgid. * 请注意:这里的msgid实际上是由msgid(图文消息id,这也就是群发接口调用后返回的msg_data_id)和index(消息次序索引)组成, 例如12003_3, 其中12003是msgid,即一次群发的消息的id; 3为index,假设该次群发的图文消息共5个文章(因为可能为多图文),3表示5个中的第3个 */ @SerializedName("msgid") private String msgId; /** - * title + * title. * 图文消息的标题 */ @SerializedName("title") private String title; /** - * details + * details. * 详细信息 */ @SerializedName("details") private List details; - public String getMsgId() { - return this.msgId; - } - - public void setMsgId(String msgId) { - this.msgId = msgId; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public List getDetails() { - return this.details; - } - - public void setDetails(List details) { - this.details = details; - } - public static List fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson( - JSON_PARSER.parse(json).getAsJsonObject().get("list"), - new TypeToken>() { - }.getType()); + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("list"), + new TypeToken>() { + }.getType()); } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java index 251c5e767c..a07974f42c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java @@ -1,13 +1,20 @@ package me.chanjar.weixin.mp.bean.datacube; import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; /** * 获取图文群发总数据接口(getarticletotal)中的详细字段 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/8/24. + *

    + * Created by Binary Wang on 2016/8/24. + * + * @author Binary Wang */ -public class WxDataCubeArticleTotalDetail { +@Data +public class WxDataCubeArticleTotalDetail implements Serializable { + private static final long serialVersionUID = -5136169129771430052L; /** * stat_date @@ -80,326 +87,140 @@ public class WxDataCubeArticleTotalDetail { private Integer addToFavCount; /** - * int_page_from_session_read_user - * 公众号会话阅读人数 - */ + * int_page_from_session_read_user + * 公众号会话阅读人数 + */ @SerializedName("int_page_from_session_read_user") private Integer intPageFromSessionReadUser; /** - * int_page_from_session_read_count - * 公众号会话阅读次数 - */ + * int_page_from_session_read_count + * 公众号会话阅读次数 + */ @SerializedName("int_page_from_session_read_count") private Integer intPageFromSessionReadCount; /** - * int_page_from_hist_msg_read_user - * 历史消息页阅读人数 - */ + * int_page_from_hist_msg_read_user + * 历史消息页阅读人数 + */ @SerializedName("int_page_from_hist_msg_read_user") private Integer intPageFromHistMsgReadUser; /** - * int_page_from_hist_msg_read_count - * 历史消息页阅读次数 - */ + * int_page_from_hist_msg_read_count + * 历史消息页阅读次数 + */ @SerializedName("int_page_from_hist_msg_read_count") private Integer intPageFromHistMsgReadCount; /** - * int_page_from_feed_read_user - * 朋友圈阅读人数 - */ + * int_page_from_feed_read_user + * 朋友圈阅读人数 + */ @SerializedName("int_page_from_feed_read_user") private Integer intPageFromFeedReadUser; /** - * int_page_from_feed_read_count - * 朋友圈阅读次数 - */ + * int_page_from_feed_read_count + * 朋友圈阅读次数 + */ @SerializedName("int_page_from_feed_read_count") private Integer intPageFromFeedReadCount; /** - * int_page_from_friends_read_user - * 好友转发阅读人数 - */ + * int_page_from_friends_read_user + * 好友转发阅读人数 + */ @SerializedName("int_page_from_friends_read_user") private Integer intPageFromFriendsReadUser; /** - * int_page_from_friends_read_count - * 好友转发阅读次数 - */ + * int_page_from_friends_read_count + * 好友转发阅读次数 + */ @SerializedName("int_page_from_friends_read_count") private Integer intPageFromFriendsReadCount; /** - * int_page_from_other_read_user - * 其他场景阅读人数 - */ + * int_page_from_other_read_user + * 其他场景阅读人数 + */ @SerializedName("int_page_from_other_read_user") private Integer intPageFromOtherReadUser; /** - * int_page_from_other_read_count - * 其他场景阅读次数 - */ + * int_page_from_other_read_count + * 其他场景阅读次数 + */ @SerializedName("int_page_from_other_read_count") private Integer intPageFromOtherReadCount; /** - * feed_share_from_session_user - * 公众号会话转发朋友圈人数 - */ + * feed_share_from_session_user + * 公众号会话转发朋友圈人数 + */ @SerializedName("feed_share_from_session_user") private Integer feedShareFromSessionUser; /** - * feed_share_from_session_cnt - * 公众号会话转发朋友圈次数 - */ + * feed_share_from_session_cnt + * 公众号会话转发朋友圈次数 + */ @SerializedName("feed_share_from_session_cnt") private Integer feedShareFromSessionCnt; /** - * feed_share_from_feed_user - * 朋友圈转发朋友圈人数 - */ + * feed_share_from_feed_user + * 朋友圈转发朋友圈人数 + */ @SerializedName("feed_share_from_feed_user") private Integer feedShareFromFeedUser; /** - * feed_share_from_feed_cnt - * 朋友圈转发朋友圈次数 - */ + * feed_share_from_feed_cnt + * 朋友圈转发朋友圈次数 + */ @SerializedName("feed_share_from_feed_cnt") private Integer feedShareFromFeedCnt; /** - * feed_share_from_other_user - * 其他场景转发朋友圈人数 - */ + * feed_share_from_other_user + * 其他场景转发朋友圈人数 + */ @SerializedName("feed_share_from_other_user") private Integer feedShareFromOtherUser; /** - * feed_share_from_other_cnt - * 其他场景转发朋友圈次数 - */ + * feed_share_from_other_cnt + * 其他场景转发朋友圈次数 + */ @SerializedName("feed_share_from_other_cnt") private Integer feedShareFromOtherCnt; - public String getStatDate() { - return this.statDate; - } - - public void setStatDate(String statDate) { - this.statDate = statDate; - } - - public Integer getTargetUser() { - return this.targetUser; - } - - public void setTargetUser(Integer targetUser) { - this.targetUser = targetUser; - } - - public Integer getIntPageReadUser() { - return this.intPageReadUser; - } - - public void setIntPageReadUser(Integer intPageReadUser) { - this.intPageReadUser = intPageReadUser; - } - - public Integer getIntPageReadCount() { - return this.intPageReadCount; - } - public void setIntPageReadCount(Integer intPageReadCount) { - this.intPageReadCount = intPageReadCount; - } - - public Integer getOriPageReadUser() { - return this.oriPageReadUser; - } - - public void setOriPageReadUser(Integer oriPageReadUser) { - this.oriPageReadUser = oriPageReadUser; - } - - public Integer getOriPageReadCount() { - return this.oriPageReadCount; - } - - public void setOriPageReadCount(Integer oriPageReadCount) { - this.oriPageReadCount = oriPageReadCount; - } - - public Integer getShareUser() { - return this.shareUser; - } - - public void setShareUser(Integer shareUser) { - this.shareUser = shareUser; - } - - public Integer getShareCount() { - return this.shareCount; - } - - public void setShareCount(Integer shareCount) { - this.shareCount = shareCount; - } - - public Integer getAddToFavUser() { - return this.addToFavUser; - } - - public void setAddToFavUser(Integer addToFavUser) { - this.addToFavUser = addToFavUser; - } - - public Integer getAddToFavCount() { - return this.addToFavCount; - } - - public void setAddToFavCount(Integer addToFavCount) { - this.addToFavCount = addToFavCount; - } - - public Integer getIntPageFromSessionReadUser() { - return this.intPageFromSessionReadUser; - } - - public void setIntPageFromSessionReadUser(Integer intPageFromSessionReadUser) { - this.intPageFromSessionReadUser = intPageFromSessionReadUser; - } + /** + * intpagefromkanyikanreaduser 看一看来源阅读人数 + */ + @SerializedName("int_page_from_kanyikan_read_user") + private Integer intPageFromKanyikanReadUser; - public Integer getIntPageFromSessionReadCount() { - return this.intPageFromSessionReadCount; - } + /** + * intpagefromkanyikanreadcount 看一看来源阅读次数 + */ + @SerializedName("int_page_from_kanyikan_read_count") + private Integer intPageFromKanyikanReadCount; - public void setIntPageFromSessionReadCount( - Integer intPageFromSessionReadCount) { - this.intPageFromSessionReadCount = intPageFromSessionReadCount; - } + /** + * intpagefromsouyisoureaduser 搜一搜来源阅读人数 + */ + @SerializedName("int_page_from_souyisou_read_user") + private Integer intPageFromSouyisouReadUser; - public Integer getIntPageFromHistMsgReadUser() { - return this.intPageFromHistMsgReadUser; - } - - public void setIntPageFromHistMsgReadUser(Integer intPageFromHistMsgReadUser) { - this.intPageFromHistMsgReadUser = intPageFromHistMsgReadUser; - } - - public Integer getIntPageFromHistMsgReadCount() { - return this.intPageFromHistMsgReadCount; - } - - public void setIntPageFromHistMsgReadCount( - Integer intPageFromHistMsgReadCount) { - this.intPageFromHistMsgReadCount = intPageFromHistMsgReadCount; - } - - public Integer getIntPageFromFeedReadUser() { - return this.intPageFromFeedReadUser; - } - - public void setIntPageFromFeedReadUser(Integer intPageFromFeedReadUser) { - this.intPageFromFeedReadUser = intPageFromFeedReadUser; - } - - public Integer getIntPageFromFeedReadCount() { - return this.intPageFromFeedReadCount; - } - - public void setIntPageFromFeedReadCount(Integer intPageFromFeedReadCount) { - this.intPageFromFeedReadCount = intPageFromFeedReadCount; - } - - public Integer getIntPageFromFriendsReadUser() { - return this.intPageFromFriendsReadUser; - } - - public void setIntPageFromFriendsReadUser(Integer intPageFromFriendsReadUser) { - this.intPageFromFriendsReadUser = intPageFromFriendsReadUser; - } - - public Integer getIntPageFromFriendsReadCount() { - return this.intPageFromFriendsReadCount; - } - - public void setIntPageFromFriendsReadCount( - Integer intPageFromFriendsReadCount) { - this.intPageFromFriendsReadCount = intPageFromFriendsReadCount; - } - - public Integer getIntPageFromOtherReadUser() { - return this.intPageFromOtherReadUser; - } - - public void setIntPageFromOtherReadUser(Integer intPageFromOtherReadUser) { - this.intPageFromOtherReadUser = intPageFromOtherReadUser; - } - - public Integer getIntPageFromOtherReadCount() { - return this.intPageFromOtherReadCount; - } - - public void setIntPageFromOtherReadCount(Integer intPageFromOtherReadCount) { - this.intPageFromOtherReadCount = intPageFromOtherReadCount; - } - - public Integer getFeedShareFromSessionUser() { - return this.feedShareFromSessionUser; - } - - public void setFeedShareFromSessionUser(Integer feedShareFromSessionUser) { - this.feedShareFromSessionUser = feedShareFromSessionUser; - } - - public Integer getFeedShareFromSessionCnt() { - return this.feedShareFromSessionCnt; - } - - public void setFeedShareFromSessionCnt(Integer feedShareFromSessionCnt) { - this.feedShareFromSessionCnt = feedShareFromSessionCnt; - } - - public Integer getFeedShareFromFeedUser() { - return this.feedShareFromFeedUser; - } - - public void setFeedShareFromFeedUser(Integer feedShareFromFeedUser) { - this.feedShareFromFeedUser = feedShareFromFeedUser; - } - - public Integer getFeedShareFromFeedCnt() { - return this.feedShareFromFeedCnt; - } - - public void setFeedShareFromFeedCnt(Integer feedShareFromFeedCnt) { - this.feedShareFromFeedCnt = feedShareFromFeedCnt; - } - - public Integer getFeedShareFromOtherUser() { - return this.feedShareFromOtherUser; - } - - public void setFeedShareFromOtherUser(Integer feedShareFromOtherUser) { - this.feedShareFromOtherUser = feedShareFromOtherUser; - } - - public Integer getFeedShareFromOtherCnt() { - return this.feedShareFromOtherCnt; - } - - public void setFeedShareFromOtherCnt(Integer feedShareFromOtherCnt) { - this.feedShareFromOtherCnt = feedShareFromOtherCnt; - } + /** + * intpagefromsouyisoureadcount 搜一搜来源阅读次数 + */ + @SerializedName("int_page_from_souyisou_read_count") + private Integer intPageFromSouyisouReadCount; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeBaseResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeBaseResult.java index a9415d9f9e..0e9051d231 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeBaseResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeBaseResult.java @@ -1,32 +1,33 @@ package me.chanjar.weixin.mp.bean.datacube; +import java.io.Serializable; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * 统计接口的共用属性类 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/8/25. + *

    + *  统计接口的共用属性类.
    + *  Created by Binary Wang on 2016/8/25.
    + * 
    + * + * @author Binary Wang */ -public class WxDataCubeBaseResult { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } +@Data +public abstract class WxDataCubeBaseResult implements Serializable { + private static final long serialVersionUID = 8780389911053297600L; /** - * ref_date + * ref_date. * 数据的日期,需在begin_date和end_date之间 */ @SerializedName("ref_date") private String refDate; - public String getRefDate() { - return this.refDate; - } - - public void setRefDate(String refDate) { - this.refDate = refDate; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeInterfaceResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeInterfaceResult.java index 2088453cee..0cc54c7cc9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeInterfaceResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeInterfaceResult.java @@ -1,21 +1,25 @@ package me.chanjar.weixin.mp.bean.datacube; -import java.util.List; - -import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; - +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import java.util.List; + /** * 接口分析数据接口返回结果对象 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/8/30. + *

    + * Created by Binary Wang on 2016/8/30. + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) public class WxDataCubeInterfaceResult extends WxDataCubeBaseResult { - - private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = 597734329161281398L; /** * ref_hour @@ -52,51 +56,11 @@ public class WxDataCubeInterfaceResult extends WxDataCubeBaseResult { @SerializedName("max_time_cost") private Integer maxTimeCost; - public Integer getRefHour() { - return this.refHour; - } - - public void setRefHour(Integer refHour) { - this.refHour = refHour; - } - - public Integer getCallbackCount() { - return this.callbackCount; - } - - public void setCallbackCount(Integer callbackCount) { - this.callbackCount = callbackCount; - } - - public Integer getFailCount() { - return this.failCount; - } - - public void setFailCount(Integer failCount) { - this.failCount = failCount; - } - - public Integer getTotalTimeCost() { - return this.totalTimeCost; - } - - public void setTotalTimeCost(Integer totalTimeCost) { - this.totalTimeCost = totalTimeCost; - } - - public Integer getMaxTimeCost() { - return this.maxTimeCost; - } - - public void setMaxTimeCost(Integer maxTimeCost) { - this.maxTimeCost = maxTimeCost; - } - public static List fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson( - JSON_PARSER.parse(json).getAsJsonObject().get("list"), - new TypeToken>() { - }.getType()); + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("list"), + new TypeToken>() { + }.getType()); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeMsgResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeMsgResult.java index 01e40e806c..c556e51758 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeMsgResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeMsgResult.java @@ -1,52 +1,55 @@ package me.chanjar.weixin.mp.bean.datacube; -import java.util.List; - -import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; - +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import java.util.List; + /** - * 消息分析数据接口返回结果对象 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/8/29. + * 消息分析数据接口返回结果对象. + * Created by Binary Wang on 2016/8/29. + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) public class WxDataCubeMsgResult extends WxDataCubeBaseResult { - - private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = 6932121822150573659L; /** - * ref_hour + * ref_hour. * 数据的小时,包括从000到2300,分别代表的是[000,100)到[2300,2400),即每日的第1小时和最后1小时 */ @SerializedName("ref_hour") private Integer refHour; /** - * msg_type + * msg_type. * 消息类型,代表含义如下:1代表文字 2代表图片 3代表语音 4代表视频 6代表第三方应用消息(链接消息) */ @SerializedName("msg_type") private Integer msgType; /** - * msg_user + * msg_user. * 上行发送了(向公众号发送了)消息的用户数 */ @SerializedName("msg_user") private Integer msgUser; /** - * msg_count + * msg_count. * 上行发送了消息的消息总数 */ @SerializedName("msg_count") private Integer msgCount; /** - * count_interval + * count_interval. * 当日发送消息量分布的区间,0代表 “0”,1代表“1-5”,2代表“6-10”,3代表“10次以上” */ @SerializedName("count_interval") @@ -60,73 +63,17 @@ public class WxDataCubeMsgResult extends WxDataCubeBaseResult { private Integer intPageReadCount; /** - * ori_page_read_user + * ori_page_read_user. * 原文页(点击图文页“阅读原文”进入的页面)的阅读人数,无原文页时此处数据为0 */ @SerializedName("ori_page_read_user") private Integer oriPageReadUser; - public Integer getRefHour() { - return this.refHour; - } - - public void setRefHour(Integer refHour) { - this.refHour = refHour; - } - - public Integer getMsgType() { - return this.msgType; - } - - public void setMsgType(Integer msgType) { - this.msgType = msgType; - } - - public Integer getMsgUser() { - return this.msgUser; - } - - public void setMsgUser(Integer msgUser) { - this.msgUser = msgUser; - } - - public Integer getMsgCount() { - return this.msgCount; - } - - public void setMsgCount(Integer msgCount) { - this.msgCount = msgCount; - } - - public Integer getCountInterval() { - return this.countInterval; - } - - public void setCountInterval(Integer countInterval) { - this.countInterval = countInterval; - } - - public Integer getIntPageReadCount() { - return this.intPageReadCount; - } - - public void setIntPageReadCount(Integer intPageReadCount) { - this.intPageReadCount = intPageReadCount; - } - - public Integer getOriPageReadUser() { - return this.oriPageReadUser; - } - - public void setOriPageReadUser(Integer oriPageReadUser) { - this.oriPageReadUser = oriPageReadUser; - } - public static List fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson( - JSON_PARSER.parse(json).getAsJsonObject().get("list"), - new TypeToken>() { - }.getType()); + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("list"), + new TypeToken>() { + }.getType()); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserCumulate.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserCumulate.java index 40e35ded37..c278861dc5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserCumulate.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserCumulate.java @@ -1,23 +1,25 @@ package me.chanjar.weixin.mp.bean.datacube; -import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; import java.util.Date; import java.util.List; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + /** *

      * 累计用户数据接口的返回JSON数据包
      * 详情查看文档:用户分析数据接口
      * 
    + * + * @author BinaryWang */ +@Data public class WxDataCubeUserCumulate implements Serializable { - private static final JsonParser JSON_PARSER = new JsonParser(); private static final long serialVersionUID = -3570981300225093657L; @@ -25,31 +27,15 @@ public class WxDataCubeUserCumulate implements Serializable { private Integer cumulateUser; - public Date getRefDate() { - return this.refDate; - } - - public void setRefDate(Date refDate) { - this.refDate = refDate; - } - - public Integer getCumulateUser() { - return this.cumulateUser; - } - - public void setCumulateUser(Integer cumulateUser) { - this.cumulateUser = cumulateUser; + public static List fromJson(String json) { + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("list"), + new TypeToken>() { + }.getType()); } @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public static List fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson( - JSON_PARSER.parse(json).getAsJsonObject().get("list"), - new TypeToken>() { - }.getType()); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserSummary.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserSummary.java index 8f47888512..75df60c55f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserSummary.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeUserSummary.java @@ -1,24 +1,25 @@ package me.chanjar.weixin.mp.bean.datacube; -import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; import java.util.Date; import java.util.List; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + /** *
      * 用户增减数据接口的返回JSON数据包
      * 详情查看文档:用户分析数据接口
      * 
    */ +@Data public class WxDataCubeUserSummary implements Serializable { private static final long serialVersionUID = -2336654489906694173L; - private static final JsonParser JSON_PARSER = new JsonParser(); + private Date refDate; @@ -28,47 +29,15 @@ public class WxDataCubeUserSummary implements Serializable { private Integer cancelUser; - public Date getRefDate() { - return this.refDate; - } - - public void setRefDate(Date refDate) { - this.refDate = refDate; - } - - public Integer getUserSource() { - return this.userSource; - } - - public void setUserSource(Integer userSource) { - this.userSource = userSource; - } - - public Integer getNewUser() { - return this.newUser; - } - - public void setNewUser(Integer newUser) { - this.newUser = newUser; - } - - public Integer getCancelUser() { - return this.cancelUser; - } - - public void setCancelUser(Integer cancelUser) { - this.cancelUser = cancelUser; + public static List fromJson(String json) { + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("list"), + new TypeToken>() { + }.getType()); } @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public static List fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson( - JSON_PARSER.parse(json).getAsJsonObject().get("list"), - new TypeToken>() { - }.getType()); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/AbstractDeviceBean.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/AbstractDeviceBean.java index 3a327069bc..d49999c504 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/AbstractDeviceBean.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/AbstractDeviceBean.java @@ -2,10 +2,17 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import java.io.Serializable; + /** - * Created by keungtung on 14/12/2016. + * 设备抽象类. + * + * @author keungtung + * @date 14/12/2016 */ -public abstract class AbstractDeviceBean { +public abstract class AbstractDeviceBean implements Serializable { + private static final long serialVersionUID = 4359729626772515385L; + public String toJson() { return WxGsonBuilder.create().toJson(this); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java index b0dd6ec3a7..5c66b0cd60 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java @@ -1,11 +1,18 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; /** - * Created by keungtung on 10/12/2016. + * @author keungtung. + * @date 10/12/2016 */ -public class BaseResp extends AbstractDeviceBean{ +@Data +@EqualsAndHashCode(callSuper = false) +public class BaseResp extends AbstractDeviceBean { + private static final long serialVersionUID = 4252655933699659073L; + @SerializedName("base_info") private BaseInfo baseInfo; @SerializedName("errcode") @@ -13,50 +20,12 @@ public class BaseResp extends AbstractDeviceBean{ @SerializedName("errmsg") private String errMsg; - public Integer getErrCode() { - return errCode; - } - - public void setErrCode(Integer errCode) { - this.errCode = errCode; - } - - public BaseInfo getBaseInfo() { - return baseInfo; - } - - public void setBaseInfo(BaseInfo baseInfo) { - this.baseInfo = baseInfo; - } - - public String getErrMsg() { - return errMsg; - } - - public void setErrMsg(String errMsg) { - this.errMsg = errMsg; - } - + @Data private class BaseInfo { @SerializedName("device_type") private String deviceType; + @SerializedName("device_id") private String deviceId; - - public String getDeviceType() { - return deviceType; - } - - public void setDeviceType(String deviceType) { - this.deviceType = deviceType; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/RespMsg.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/RespMsg.java index f1fdedff94..601f848223 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/RespMsg.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/RespMsg.java @@ -1,30 +1,21 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; /** - * Created by keungtung on 10/12/2016. + * + * @author keungtung. + * @date 10/12/2016 */ +@Data +@EqualsAndHashCode(callSuper = false) +public class RespMsg extends AbstractDeviceBean { + private static final long serialVersionUID = -4241272701707684136L; -public class RespMsg extends AbstractDeviceBean{ @SerializedName("ret_code") private Integer retCode; @SerializedName("error_info") private String errorInfo; - - public Integer getRetCode() { - return retCode; - } - - public void setRetCode(Integer retCode) { - this.retCode = retCode; - } - - public String getErrorInfo() { - return errorInfo; - } - - public void setErrorInfo(String errorInfo) { - this.errorInfo = errorInfo; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/TransMsgResp.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/TransMsgResp.java index 8828b25c9e..f2b35da5ea 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/TransMsgResp.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/TransMsgResp.java @@ -1,12 +1,20 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.util.json.WxGsonBuilder; /** - * Created by keungtung on 14/12/2016. + * + * @author keungtung. + * @date 14/12/2016 */ -public class TransMsgResp extends AbstractDeviceBean{ +@Data +@EqualsAndHashCode(callSuper = false) +public class TransMsgResp extends AbstractDeviceBean { + private static final long serialVersionUID = 5386954916622816491L; + private Integer ret; @SerializedName("ret_info") private String retInfo; @@ -18,36 +26,4 @@ public class TransMsgResp extends AbstractDeviceBean{ public static TransMsgResp fromJson(String json) { return WxGsonBuilder.create().fromJson(json, TransMsgResp.class); } - - public Integer getRet() { - return ret; - } - - public void setRet(Integer ret) { - this.ret = ret; - } - - public String getRetInfo() { - return retInfo; - } - - public void setRetInfo(String retInfo) { - this.retInfo = retInfo; - } - - public Integer getErrCode() { - return errCode; - } - - public void setErrCode(Integer errCode) { - this.errCode = errCode; - } - - public String getErrMsg() { - return errMsg; - } - - public void setErrMsg(String errMsg) { - this.errMsg = errMsg; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDevice.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDevice.java index c0f95dbf2a..84c5b2d66d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDevice.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDevice.java @@ -1,11 +1,18 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; /** - * Created by keungtung on 10/12/2016. + * @author keungtung + * @date 10/12/2016 */ -public class WxDevice { +@Data +public class WxDevice implements Serializable { + private static final long serialVersionUID = -3284819760735456195L; + private String id; private String mac; @SerializedName("connect_protocol") @@ -26,92 +33,4 @@ public class WxDevice { private String serMacPos; @SerializedName("ble_simple_protocol") private String bleSimpleProtocol; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getMac() { - return mac; - } - - public void setMac(String mac) { - this.mac = mac; - } - - public String getConnectProtocol() { - return connectProtocol; - } - - public void setConnectProtocol(String connectProtocol) { - this.connectProtocol = connectProtocol; - } - - public String getAuthKey() { - return authKey; - } - - public void setAuthKey(String authKey) { - this.authKey = authKey; - } - - public String getCloseStrategy() { - return closeStrategy; - } - - public void setCloseStrategy(String closeStrategy) { - this.closeStrategy = closeStrategy; - } - - public String getConnStrategy() { - return connStrategy; - } - - public void setConnStrategy(String connStrategy) { - this.connStrategy = connStrategy; - } - - public String getCryptMethod() { - return cryptMethod; - } - - public void setCryptMethod(String cryptMethod) { - this.cryptMethod = cryptMethod; - } - - public String getAuthVer() { - return authVer; - } - - public void setAuthVer(String authVer) { - this.authVer = authVer; - } - - public String getManuMacPos() { - return manuMacPos; - } - - public void setManuMacPos(String manuMacPos) { - this.manuMacPos = manuMacPos; - } - - public String getSerMacPos() { - return serMacPos; - } - - public void setSerMacPos(String serMacPos) { - this.serMacPos = serMacPos; - } - - public String getBleSimpleProtocol() { - return bleSimpleProtocol; - } - - public void setBleSimpleProtocol(String bleSimpleProtocol) { - this.bleSimpleProtocol = bleSimpleProtocol; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorize.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorize.java index 0318754e57..5e00c4faea 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorize.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorize.java @@ -1,15 +1,22 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; import java.util.Arrays; import java.util.LinkedList; import java.util.List; /** - * Created by keungtung on 10/12/2016. + * @author keungtung + * @date 10/12/2016 */ +@Data +@EqualsAndHashCode(callSuper = false) public class WxDeviceAuthorize extends AbstractDeviceBean { + private static final long serialVersionUID = 8786321356569903887L; + @SerializedName("device_num") private String deviceNum; @SerializedName("op_type") @@ -19,38 +26,6 @@ public class WxDeviceAuthorize extends AbstractDeviceBean { @SerializedName("device_list") private List deviceList = new LinkedList<>(); - public String getDeviceNum() { - return deviceNum; - } - - public void setDeviceNum(String deviceNum) { - this.deviceNum = deviceNum; - } - - public String getOpType() { - return opType; - } - - public void setOpType(String opType) { - this.opType = opType; - } - - public String getProductId() { - return productId; - } - - public void setProductId(String productId) { - this.productId = productId; - } - - public List getDeviceList() { - return deviceList; - } - - public void setDeviceList(List deviceList) { - this.deviceList = deviceList; - } - public void addDevice(WxDevice... devices) { this.deviceList.addAll(Arrays.asList(devices)); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorizeResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorizeResult.java index dfe8d3b7fc..9608452ce1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorizeResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorizeResult.java @@ -1,24 +1,24 @@ package me.chanjar.weixin.mp.bean.device; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import java.util.List; /** - * Created by keungtung on 10/12/2016. + * @author keungtung. + * @date 10/12/2016 */ -public class WxDeviceAuthorizeResult extends AbstractDeviceBean{ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxDeviceAuthorizeResult extends AbstractDeviceBean { + private static final long serialVersionUID = 9105294570912249811L; + private List resp; public static WxDeviceAuthorizeResult fromJson(String response) { return WxGsonBuilder.create().fromJson(response, WxDeviceAuthorizeResult.class); } - public List getResp() { - return resp; - } - - public void setResp(List resp) { - this.resp = resp; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBind.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBind.java index 467f3c42d9..aeb7f819ce 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBind.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBind.java @@ -1,38 +1,22 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; /** - * Created by keungtung on 10/12/2016. + * @author keungtung. + * @date 10/12/2016 */ -public class WxDeviceBind extends AbstractDeviceBean{ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxDeviceBind extends AbstractDeviceBean { + private static final long serialVersionUID = 467559769037590880L; + private String ticket; @SerializedName("device_id") private String deviceId; @SerializedName("openid") private String openId; - public String getTicket() { - return ticket; - } - - public void setTicket(String ticket) { - this.ticket = ticket; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getOpenId() { - return openId; - } - - public void setOpenId(String openId) { - this.openId = openId; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java index d0ed2dcd60..6227a6ef44 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java @@ -1,14 +1,21 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.util.List; /** - * Created by keungtung on 16/12/2016. + * @author keungtung. + * @date 16/12/2016 */ +@Data +@EqualsAndHashCode(callSuper = false) public class WxDeviceBindDeviceResult extends AbstractDeviceBean { + private static final long serialVersionUID = 725870295905935355L; + @SerializedName("resp_msg") private RespMsg respMsg; @SerializedName("openid") @@ -20,27 +27,13 @@ public static WxDeviceBindDeviceResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxDeviceBindDeviceResult.class); } - private class Device { + @Data + public static class Device { @SerializedName("device_type") private String deviceType; + @SerializedName("device_id") private String deviceId; - - public String getDeviceType() { - return deviceType; - } - - public void setDeviceType(String deviceType) { - this.deviceType = deviceType; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindResult.java index 26bdb84c6a..f6c702aa29 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindResult.java @@ -1,12 +1,19 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * Created by keungtung on 10/12/2016. + * @author keungtung. + * @date 10/12/2016 */ -public class WxDeviceBindResult extends AbstractDeviceBean{ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxDeviceBindResult extends AbstractDeviceBean { + private static final long serialVersionUID = 4687725146279339359L; + @SerializedName("base_resp") private BaseResp baseResp; @@ -14,11 +21,4 @@ public static WxDeviceBindResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxDeviceBindResult.class); } - public BaseResp getBaseResp() { - return baseResp; - } - - public void setBaseResp(BaseResp baseResp) { - this.baseResp = baseResp; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceMsg.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceMsg.java index bed1a8a23a..2b554abc27 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceMsg.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceMsg.java @@ -1,12 +1,19 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * Created by keungtung on 10/12/2016. + * @author keungtung. + * @date 10/12/2016 */ -public class WxDeviceMsg extends AbstractDeviceBean{ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxDeviceMsg extends AbstractDeviceBean { + private static final long serialVersionUID = -5567110858455277963L; + @SerializedName("device_type") private String deviceType; @SerializedName("device_id") @@ -17,38 +24,6 @@ public class WxDeviceMsg extends AbstractDeviceBean{ @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getDeviceType() { - return deviceType; - } - - public void setDeviceType(String deviceType) { - this.deviceType = deviceType; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getOpenId() { - return openId; - } - - public void setOpenId(String openId) { - this.openId = openId; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java index 0a23eddd3a..95cf2a94ff 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java @@ -1,14 +1,21 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.util.List; /** - * Created by keungtung on 16/12/2016. + * @author keungtung. + * @date 16/12/2016 */ +@Data +@EqualsAndHashCode(callSuper = false) public class WxDeviceOpenIdResult extends AbstractDeviceBean { + private static final long serialVersionUID = 4980885167833836220L; + @SerializedName("errcode") private Integer errCode; @SerializedName("errmsg") @@ -22,35 +29,4 @@ public static WxDeviceOpenIdResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxDeviceOpenIdResult.class); } - public Integer getErrCode() { - return errCode; - } - - public void setErrCode(Integer errCode) { - this.errCode = errCode; - } - - public String getErrMsg() { - return errMsg; - } - - public void setErrMsg(String errMsg) { - this.errMsg = errMsg; - } - - public List getOpenIds() { - return openIds; - } - - public void setOpenIds(List openIds) { - this.openIds = openIds; - } - - public RespMsg getRespMsg() { - return respMsg; - } - - public void setRespMsg(RespMsg respMsg) { - this.respMsg = respMsg; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java index ff3028be4f..816354818c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java @@ -1,12 +1,19 @@ package me.chanjar.weixin.mp.bean.device; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * Created by keungtung on 10/12/2016. + * @author keungtung. + * @date 10/12/2016 */ +@Data +@EqualsAndHashCode(callSuper = false) public class WxDeviceQrCodeResult extends AbstractDeviceBean { + private static final long serialVersionUID = -4312858303060918266L; + @SerializedName("deviceid") private String deviceId; @SerializedName("qrticket") @@ -17,38 +24,7 @@ public class WxDeviceQrCodeResult extends AbstractDeviceBean { private BaseResp baseResp; public static WxDeviceQrCodeResult fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxDeviceQrCodeResult.class); - } - - public String getDeviceLicence() { - return deviceLicence; - } - - public void setDeviceLicence(String deviceLicence) { - this.deviceLicence = deviceLicence; - } - - public BaseResp getBaseResp() { - return baseResp; - } - - public void setBaseResp(BaseResp baseResp) { - this.baseResp = baseResp; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getQrTicket() { - return qrTicket; + return WxMpGsonBuilder.create().fromJson(json, WxDeviceQrCodeResult.class); } - public void setQrTicket(String qrTicket) { - this.qrTicket = qrTicket; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/ClearOutInvoiceRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/ClearOutInvoiceRequest.java new file mode 100644 index 0000000000..108e76ca23 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/ClearOutInvoiceRequest.java @@ -0,0 +1,50 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 发票充红请求参数 + */ +@Data +public class ClearOutInvoiceRequest implements Serializable { + + + private ClearOutInvoiceInfo invoiceinfo; + + @Data + public static class ClearOutInvoiceInfo implements Serializable { + + /** + * 用户的openid 用户知道是谁在开票 + */ + private String wxopenid; + + /** + * 发票请求流水号,唯一查询发票的流水号 + */ + private String fpqqlsh; + + /** + * 纳税人识别码 + */ + private String nsrsbh; + + /** + * 纳税人名称 + */ + private String nsrmc; + + /** + * 原发票代码,即要冲红的蓝票的发票代码 + */ + private String yfpdm; + + /** + * 原发票号码,即要冲红的蓝票的发票号码 + */ + private String yfphm; + + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthDataRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthDataRequest.java new file mode 100644 index 0000000000..092e16410a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthDataRequest.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 获取电子开票用户授权数据 + */ +@Data +public class InvoiceAuthDataRequest implements Serializable { + + /** + * 开票平台在微信的标识号,商户需要找开票平台提供 + */ + private String sPappid; + + /** + * 订单id,在商户内单笔开票请求的唯一识别号 + */ + private String orderId; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthDataResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthDataResult.java new file mode 100644 index 0000000000..99d8f57cc6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthDataResult.java @@ -0,0 +1,66 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 用户开票认证信息返回结果DTO + */ +@Data +public class InvoiceAuthDataResult implements Serializable { + + /** + * 订单授权状态,当errcode为0时会出现 + */ + private String invoiceStatus; + + /** + * 授权时间,为十位时间戳(utc+8),当errcode为0时会出现 + */ + private Long authTime; + + /** + * 用户授权信息 + */ + private UserAuthInfo userAuthInfo; + + @Data + public static class UserAuthInfo implements Serializable { + /** + * 个人抬头 + */ + private UserField userField; + + /** + * 单位抬头 + */ + private BizField bizField; + } + + @Data + public static class UserField implements Serializable { + private String title; + private String phone; + private String email; + private List customField; + } + + @Data + public static class BizField implements Serializable { + private String title; + private String taxNo; + private String addr; + private String phone; + private String bankType; + private String bankNo; + private List customField; + } + + @Data + public static class KeyValuePair implements Serializable { + private String key; + private String value; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageRequest.java new file mode 100644 index 0000000000..07a8a24e55 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageRequest.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 获取授权地址的输入参数 + */ +@Data +public class InvoiceAuthPageRequest implements Serializable { + + /** + * 开票平台在微信的标识号,商户需要找开票平台提供 + */ + private String sPappid; + + /** + * 订单id,在商户内单笔开票请求的唯一识别号 + */ + private String orderId; + + /** + * 订单金额,以分为单位 + */ + private Long money; + + /** + * 开票来源 + */ + private String source; + + /** + * 授权成功后跳转页面。本字段只有在source为H5的时候需要填写,引导用户在微信中进行下一步流程。app开票因为从外部app拉起微信授权页,授权完成后自动回到原来的app,故无需填写。 + */ + private String redirectUrl; + + /** + * 授权类型,0:开票授权,1:填写字段开票授权,2:领票授权 + */ + private Integer type; + + /** + * 时间戳单位s + */ + private Long timestamp; + + /** + * 内部填充(请务设置) + */ + private String ticket; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageResult.java new file mode 100644 index 0000000000..d137adb49a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageResult.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 获取授权链接返回结果DTO + */ +@Data +public class InvoiceAuthPageResult implements Serializable { + + /** + * 授权页地址 + */ + private String authUrl; + + /** + * 当发起端为小程序时, 返回 + */ + private String appid; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageSetting.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageSetting.java new file mode 100644 index 0000000000..dbc04816ec --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceAuthPageSetting.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class InvoiceAuthPageSetting implements Serializable { + + private AuthField authField; + + @Data + public static class AuthField implements Serializable { + private UserField userField; + private BizField bizField; + } + + @Data + public static class UserField implements Serializable { + private Integer showTitle; + private Integer showPhone; + private Integer showEmail; + private Integer requirePhone; + private Integer requireEmail; + private List customField; + } + + @Data + public static class BizField implements Serializable { + private Integer showTitle; + private Integer showTaxNo; + private Integer showAddr; + private Integer showPhone; + private Integer showBankType; + private Integer showBankNo; + + private Integer requireTaxNo; + private Integer requireAddr; + private Integer requirePhone; + private Integer requireBankType; + private Integer requireBankNo; + private List customField; + } + + @Data + public static class CustomField implements Serializable { + /** + * 字段名 + */ + private String key; + /** + * 0:否,1:是, 默认为0 + */ + private Integer isRequire; + /** + * 提示文案 + */ + private String notice; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceRejectRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceRejectRequest.java new file mode 100644 index 0000000000..9048ceb059 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceRejectRequest.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import java.io.Serializable; + +/** + * 拒绝开票请求参数 + */ +public class InvoiceRejectRequest implements Serializable { + + /** + * 开票平台标示 + */ + private String sPappid; + + /** + * 订单id + */ + private String orderId; + + /** + * 拒绝原因 + */ + private String reason; + + /** + * 引导用户跳转url + */ + private String url; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceResult.java new file mode 100644 index 0000000000..6f4da63a20 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceResult.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 电子发票信息查询结果 + */ +@Data +public class InvoiceResult implements Serializable { + + /** + * 发票相关信息 + */ + private InvoiceDetail invoicedetail; + + @Data + public static class InvoiceDetail implements Serializable { + /** + * 发票流水号 + */ + private String fpqqlsh; + + /** + * 检验码 + */ + private String jym; + + /** + * 校验码 + */ + private String kprq; + + /** + * 发票代码 + */ + private String fpdm; + + /** + * 发票号码 + */ + private String fphm; + + /** + * 发票url + */ + private String pdfurl; + + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MakeOutInvoiceRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MakeOutInvoiceRequest.java new file mode 100644 index 0000000000..d9336eac12 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MakeOutInvoiceRequest.java @@ -0,0 +1,200 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 开票信息请求参数 + */ +@Data +public class MakeOutInvoiceRequest implements Serializable { + + private InvoiceInfo invoiceinfo; + + /** + * 发票信息 + */ + @Data + public static class InvoiceInfo implements Serializable { + /** + * 维修openid + */ + private String wxopenid; + + /** + * 订单号 + */ + private String ddh; + + /** + * 发票请求流水号,唯一识别开票请求的流水号 + */ + private String fpqqlsh; + + /** + * 纳税人识别码 + */ + private String nsrsbh; + + /** + * 纳税人名称 + */ + private String nsrmc; + + /** + * 纳税人地址 + */ + private String nsrdz; + + /** + * 纳税人电话 + */ + private String nsrdh; + + /** + * 纳税人开户行 + */ + private String nsrbank; + + /** + * 纳税人银行账号 + */ + private String nsrbankid; + + /** + * 购货方名称 + */ + private String ghfnsrsbh; + + /** + * 购货方识别号 + */ + private String ghfmc; + + /** + * 购货方地址 + */ + private String ghfdz; + + /** + * 购货方电话 + */ + private String ghfdh; + + /** + * 购货方开户行 + */ + private String ghfbank; + + /** + * 购货方银行帐号 + */ + private String ghfbankid; + + /** + * 开票人 + */ + private String kpr; + + /** + * 收款人 + */ + private String skr; + + /** + * 复核人 + */ + private String fhr; + + /** + * 价税合计 + */ + private String jshj; + + /** + * 合计金额 + */ + private String hjje; + + /** + * 合计税额 + */ + private String hjse; + + /** + * 备注 + */ + private String bz; + + /** + * 行业类型 0 商业 1其它 + */ + private String hylx; + + /** + * 发票商品条目 + */ + private List invoicedetailList; + + } + + /** + * 发票条目 + */ + @Data + public static class InvoiceDetailItem implements Serializable { + /** + * 发票性质 + */ + private String fphxz; + + /** + * 19位税收分类编码 + */ + private String spbm; + + /** + * 项目名称 + */ + private String xmmc; + + /** + * 计量单位 + */ + private String dw; + + /** + * 规格型号 + */ + private String ggxh; + + /** + * 项目数量 + */ + private String xmsl; + + /** + * 项目单价 + */ + private String xmdj; + + /** + * 项目金额 不含税,单位元 两位小数 + */ + private String xmje; + + /** + * 税率 精确到两位小数 如0.01 + */ + private String sl; + + /** + * 税额 单位元 两位小数 + */ + private String se; + + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantContactInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantContactInfo.java new file mode 100644 index 0000000000..569fffa6b6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantContactInfo.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 商户联系信息 + */ +@Data +public class MerchantContactInfo implements Serializable { + /** + * 联系电话 + */ + private String phone; + + /** + * 开票超时时间 + */ + private Integer timeout; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantContactInfoWrapper.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantContactInfoWrapper.java new file mode 100644 index 0000000000..3ceed2b6d0 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantContactInfoWrapper.java @@ -0,0 +1,16 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 设置商户联系信息和发票过时时间参数 + */ +@Data +public class MerchantContactInfoWrapper implements Serializable { + + private MerchantContactInfo contact; + + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfo.java new file mode 100644 index 0000000000..f7a64edfc3 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfo.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import java.io.Serializable; + +/** + * 商户的开票平台信息 + */ +public class MerchantInvoicePlatformInfo implements Serializable { + + /** + * 微信支付商户号 + */ + private String mchid; + + /** + * 为该商户提供开票服务的开票平台 id ,由开票平台提供给商户 + */ + private String sPappid; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfoWrapper.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfoWrapper.java new file mode 100644 index 0000000000..0351466164 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfoWrapper.java @@ -0,0 +1,15 @@ +package me.chanjar.weixin.mp.bean.invoice.merchant; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 设置商户联系信息和发票过时时间参数 + */ +@Data +public class MerchantInvoicePlatformInfoWrapper implements Serializable { + + private MerchantInvoicePlatformInfo paymchInfo; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java index 228351fdab..37beb77e78 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java @@ -1,24 +1,22 @@ package me.chanjar.weixin.mp.bean.kefu; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.mp.builder.kefu.*; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.mp.builder.kefu.ImageBuilder; -import me.chanjar.weixin.mp.builder.kefu.MpNewsBuilder; -import me.chanjar.weixin.mp.builder.kefu.MusicBuilder; -import me.chanjar.weixin.mp.builder.kefu.NewsBuilder; -import me.chanjar.weixin.mp.builder.kefu.TextBuilder; -import me.chanjar.weixin.mp.builder.kefu.VideoBuilder; -import me.chanjar.weixin.mp.builder.kefu.VoiceBuilder; -import me.chanjar.weixin.mp.builder.kefu.WxCardBuilder; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - /** - * 客服消息 + * 客服消息. * * @author chanjarster */ +@Data public class WxMpKefuMessage implements Serializable { private static final long serialVersionUID = -9196732086954365246L; @@ -34,224 +32,130 @@ public class WxMpKefuMessage implements Serializable { private String kfAccount; private String cardId; private String mpNewsMediaId; + private String miniProgramAppId; + private String miniProgramPagePath; + private String headContent; + private String tailContent; private List articles = new ArrayList<>(); /** - * 获得文本消息builder + * 菜单消息里的菜单内容. + */ + private List msgMenus = new ArrayList<>(); + + /** + * 获得文本消息builder. */ public static TextBuilder TEXT() { return new TextBuilder(); } /** - * 获得图片消息builder + * 获得图片消息builder. */ public static ImageBuilder IMAGE() { return new ImageBuilder(); } /** - * 获得语音消息builder + * 获得语音消息builder. */ public static VoiceBuilder VOICE() { return new VoiceBuilder(); } /** - * 获得视频消息builder + * 获得视频消息builder. */ public static VideoBuilder VIDEO() { return new VideoBuilder(); } /** - * 获得音乐消息builder + * 获得音乐消息builder. */ public static MusicBuilder MUSIC() { return new MusicBuilder(); } /** - * 获得图文消息(点击跳转到外链)builder + * 获得图文消息(点击跳转到外链)builder. */ public static NewsBuilder NEWS() { return new NewsBuilder(); } /** - * 获得图文消息(点击跳转到图文消息页面)builder + * 获得图文消息(点击跳转到图文消息页面)builder. */ public static MpNewsBuilder MPNEWS() { return new MpNewsBuilder(); } /** - * 获得卡券消息builder + * 获得卡券消息builder. */ public static WxCardBuilder WXCARD() { return new WxCardBuilder(); } - public String getToUser() { - return this.toUser; - } - - public void setToUser(String toUser) { - this.toUser = toUser; - } - - public String getMsgType() { - return this.msgType; - } - - public String getMpNewsMediaId() { - return this.mpNewsMediaId; + /** + * 获得菜单消息builder. + */ + public static WxMsgMenuBuilder MSGMENU() { + return new WxMsgMenuBuilder(); } - public void setMpNewsMediaId(String mpNewsMediaId) { - this.mpNewsMediaId = mpNewsMediaId; + /** + * 小程序卡片. + */ + public static MiniProgramPageBuilder MINIPROGRAMPAGE() { + return new MiniProgramPageBuilder(); } /** *
        * 请使用
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VOICE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MUSIC}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MPNEWS}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_WXCARD}
    +   * {@link WxConsts.KefuMsgType#TEXT}
    +   * {@link WxConsts.KefuMsgType#IMAGE}
    +   * {@link WxConsts.KefuMsgType#VOICE}
    +   * {@link WxConsts.KefuMsgType#MUSIC}
    +   * {@link WxConsts.KefuMsgType#VIDEO}
    +   * {@link WxConsts.KefuMsgType#NEWS}
    +   * {@link WxConsts.KefuMsgType#MPNEWS}
    +   * {@link WxConsts.KefuMsgType#WXCARD}
    +   * {@link WxConsts.KefuMsgType#MINIPROGRAMPAGE}
    +   * {@link WxConsts.KefuMsgType#TASKCARD}
    +   * {@link WxConsts.KefuMsgType#MSGMENU}
        * 
    - * - * @param msgType */ public void setMsgType(String msgType) { this.msgType = msgType; } - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getMusicUrl() { - return this.musicUrl; - } - - public void setMusicUrl(String musicUrl) { - this.musicUrl = musicUrl; - } - - public String getHqMusicUrl() { - return this.hqMusicUrl; - } - - public void setHqMusicUrl(String hqMusicUrl) { - this.hqMusicUrl = hqMusicUrl; - } - - public String getCardId() { - return this.cardId; - } - - public void setCardId(String cardId) { - this.cardId = cardId; - } - - public List getArticles() { - return this.articles; - } - - public void setArticles(List articles) { - this.articles = articles; - } - public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); - } - - public String getKfAccount() { - return this.kfAccount; + return WxMpGsonBuilder.create().toJson(this); } - public void setKfAccount(String kfAccount) { - this.kfAccount = kfAccount; - } + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class WxArticle implements Serializable { + private static final long serialVersionUID = 5145137235440507379L; - public static class WxArticle { private String title; private String description; private String url; private String picUrl; + } - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getPicUrl() { - return this.picUrl; - } - - public void setPicUrl(String picUrl) { - this.picUrl = picUrl; - } + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class MsgMenu implements Serializable { + private static final long serialVersionUID = 7020769047598378839L; + private String id; + private String content; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfAccountRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfAccountRequest.java index b1f0fac8f0..01633192d2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfAccountRequest.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfAccountRequest.java @@ -5,101 +5,45 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import com.google.gson.annotations.SerializedName; - +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +@Data +@Builder +@AllArgsConstructor public class WxMpKfAccountRequest implements Serializable { private static final long serialVersionUID = -5451863610674856927L; /** - * kf_account 完整客服账号,格式为:账号前缀@公众号微信号 + * kf_account. + * 完整客服账号,格式为:账号前缀@公众号微信号 */ @SerializedName("kf_account") private String kfAccount; - + /** - * nickname 客服昵称,最长6个汉字或12个英文字符 + * nickname. + * 客服昵称,最长6个汉字或12个英文字符 */ @SerializedName("nickname") private String nickName; /** - * invite_wx 接收绑定邀请的客服微信号 + * invite_wx. + * 接收绑定邀请的客服微信号 */ @SerializedName("invite_wx") private String inviteWx; - + @Override public String toString() { return ToStringBuilder.reflectionToString(this); } - - public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); - } - - public String getKfAccount() { - return this.kfAccount; - } - - public void setKfAccount(String kfAccount) { - this.kfAccount = kfAccount; - } - - public String getNickName() { - return this.nickName; - } - - public void setNickName(String nickName) { - this.nickName = nickName; - } - public static Builder builder() { - return new Builder(); - } - - public String getInviteWx() { - return this.inviteWx; - } - - public void setInviteWx(String inviteWx) { - this.inviteWx = inviteWx; - } - - public static class Builder { - private String kfAccount; - private String nickName; - private String inviteWx; - - public Builder kfAccount(String kfAccount) { - this.kfAccount = kfAccount; - return this; - } - - public Builder nickName(String nickName) { - this.nickName = nickName; - return this; - } - - public Builder inviteWx(String inviteWx) { - this.inviteWx = inviteWx; - return this; - } - - public Builder from(WxMpKfAccountRequest origin) { - this.kfAccount(origin.kfAccount); - this.nickName(origin.nickName); - this.inviteWx(origin.inviteWx); - return this; - } - - public WxMpKfAccountRequest build() { - WxMpKfAccountRequest m = new WxMpKfAccountRequest(); - m.kfAccount = this.kfAccount; - m.nickName = this.nickName; - m.inviteWx = this.inviteWx; - return m; - } + public String toJson() { + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfSessionRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfSessionRequest.java index ac59e3238e..38b25461e1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfSessionRequest.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/request/WxMpKfSessionRequest.java @@ -1,11 +1,12 @@ package me.chanjar.weixin.mp.bean.kefu.request; +import java.io.Serializable; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.io.Serializable; - +@Data public class WxMpKfSessionRequest implements Serializable { private static final long serialVersionUID = -5451863610674856927L; @@ -28,19 +29,11 @@ public WxMpKfSessionRequest(String kfAccount, String openid) { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return this.toJson(); } public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); - } - - public String getKfAccount() { - return this.kfAccount; - } - - public void setKfAccount(String kfAccount) { - this.kfAccount = kfAccount; + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfInfo.java index 868bf51952..f14feb29da 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfInfo.java @@ -1,16 +1,18 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; + import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; - -import java.io.Serializable; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** * 客服基本信息以及客服在线状态信息 - * @author Binary Wang * + * @author Binary Wang */ +@Data public class WxMpKfInfo implements Serializable { private static final long serialVersionUID = -5877300750666022290L; @@ -63,7 +65,7 @@ public class WxMpKfInfo implements Serializable { private String inviteStatus; /** - * status 客服在线状态,目前为:1、web 在线 + * status 客服在线状态,目前为:1、web 在线 */ @SerializedName("status") private Integer status; @@ -75,88 +77,9 @@ public class WxMpKfInfo implements Serializable { @SerializedName("accepted_case") private Integer acceptedCase; - public Integer getStatus() { - return this.status; - } - - public void setStatus(Integer status) { - this.status = status; - } - - public Integer getAcceptedCase() { - return this.acceptedCase; - } - - public void setAcceptedCase(Integer acceptedCase) { - this.acceptedCase = acceptedCase; - } - - public String getAccount() { - return this.account; - } - - public void setAccount(String account) { - this.account = account; - } - - public String getHeadImgUrl() { - return this.headImgUrl; - } - - public void setHeadImgUrl(String headImgUrl) { - this.headImgUrl = headImgUrl; - } - - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - public String getNick() { - return this.nick; - } - - public void setNick(String nick) { - this.nick = nick; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getWxAccount() { - return this.wxAccount; - } - - public void setWxAccount(String wxAccount) { - this.wxAccount = wxAccount; + return WxMpGsonBuilder.create().toJson(this); } - public String getInviteWx() { - return this.inviteWx; - } - - public void setInviteWx(String inviteWx) { - this.inviteWx = inviteWx; - } - - public Long getInviteExpireTime() { - return this.inviteExpireTime; - } - - public void setInviteExpireTime(Long inviteExpireTime) { - this.inviteExpireTime = inviteExpireTime; - } - - public String getInviteStatus() { - return this.inviteStatus; - } - - public void setInviteStatus(String inviteStatus) { - this.inviteStatus = inviteStatus; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfList.java index 9ec9eeff5b..cadfc8c7d5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfList.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfList.java @@ -1,33 +1,29 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; /** - * * @author Binary Wang - * */ -public class WxMpKfList { +@Data +public class WxMpKfList implements Serializable { + private static final long serialVersionUID = -8194193505173564894L; + @SerializedName("kf_list") private List kfList; - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public List getKfList() { - return this.kfList; + public static WxMpKfList fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpKfList.class); } - public void setKfList(List kfList) { - this.kfList = kfList; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } - public static WxMpKfList fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpKfList.class); - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgList.java index 7c04acb34a..af5559ea42 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgList.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgList.java @@ -1,15 +1,21 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** - * Created by Binary Wang on 2016/7/15. + * + * @author Binary Wang + * @date 2016/7/15 */ -public class WxMpKfMsgList { +@Data +public class WxMpKfMsgList implements Serializable { + private static final long serialVersionUID = 4524296707435188202L; + @SerializedName("recordlist") private List records; @@ -19,36 +25,12 @@ public class WxMpKfMsgList { @SerializedName("msgid") private Long msgId; - public List getRecords() { - return this.records; - } - - public void setRecords(List records) { - this.records = records; - } - - public Integer getNumber() { - return this.number; - } - - public void setNumber(Integer number) { - this.number = number; - } - - public Long getMsgId() { - return this.msgId; - } - - public void setMsgId(Long msgId) { - this.msgId = msgId; + public static WxMpKfMsgList fromJson(String responseContent) { + return WxMpGsonBuilder.create().fromJson(responseContent, WxMpKfMsgList.class); } @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public static WxMpKfMsgList fromJson(String responseContent) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(responseContent, WxMpKfMsgList.class); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgRecord.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgRecord.java index 0ed9c0b24a..325c66aa95 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgRecord.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfMsgRecord.java @@ -1,12 +1,20 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * Created by Binary Wang on 2016/7/18. + * + * @author Binary Wang + * @date 2016/7/18 */ -public class WxMpKfMsgRecord { +@Data +public class WxMpKfMsgRecord implements Serializable { + private static final long serialVersionUID = -280692188908528688L; + /** * worker 完整客服帐号,格式为:帐号前缀@公众号微信号 */ @@ -31,7 +39,7 @@ public class WxMpKfMsgRecord { @SerializedName("text") private String text; - /** + /** * time 操作时间,unix时间戳 */ @SerializedName("time") @@ -39,7 +47,7 @@ public class WxMpKfMsgRecord { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } public String getWorker() { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfOnlineList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfOnlineList.java index 9750009583..555ee9dba2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfOnlineList.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfOnlineList.java @@ -1,33 +1,29 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; /** - * * @author Binary Wang - * */ -public class WxMpKfOnlineList { +@Data +public class WxMpKfOnlineList implements Serializable { + private static final long serialVersionUID = -6154705288500854617L; + @SerializedName("kf_online_list") private List kfOnlineList; - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public List getKfOnlineList() { - return this.kfOnlineList; + public static WxMpKfOnlineList fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpKfOnlineList.class); } - public void setKfOnlineList(List kfOnlineList) { - this.kfOnlineList = kfOnlineList; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } - public static WxMpKfOnlineList fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpKfOnlineList.class); - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSession.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSession.java index da29b4aec9..3f4d49d2e8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSession.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSession.java @@ -1,14 +1,18 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * * @author Binary Wang - * */ -public class WxMpKfSession { +@Data +public class WxMpKfSession implements Serializable { + private static final long serialVersionUID = 7804332813164994062L; + /** * kf_account 正在接待的客服,为空表示没有人在接待 */ @@ -37,38 +41,7 @@ public class WxMpKfSession { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } - public String getKfAccount() { - return this.kfAccount; - } - - public void setKfAccount(String kfAccount) { - this.kfAccount = kfAccount; - } - - public long getCreateTime() { - return this.createTime; - } - - public void setCreateTime(long createTime) { - this.createTime = createTime; - } - - public String getOpenid() { - return this.openid; - } - - public void setOpenid(String openid) { - this.openid = openid; - } - - public long getLatestTime() { - return this.latestTime; - } - - public void setLatestTime(long latestTime) { - this.latestTime = latestTime; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionGetResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionGetResult.java index 2eb7314325..785e2126d4 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionGetResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionGetResult.java @@ -1,15 +1,18 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * * @author Binary Wang - * */ -public class WxMpKfSessionGetResult { +@Data +public class WxMpKfSessionGetResult implements Serializable { + private static final long serialVersionUID = 8474846575200033152L; + /** * kf_account 正在接待的客服,为空表示没有人在接待 */ @@ -22,29 +25,13 @@ public class WxMpKfSessionGetResult { @SerializedName("createtime") private long createTime; - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - public static WxMpKfSessionGetResult fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpKfSessionGetResult.class); - } - - public String getKfAccount() { - return this.kfAccount; - } - - public void setKfAccount(String kfAccount) { - this.kfAccount = kfAccount; + return WxMpGsonBuilder.create().fromJson(json, WxMpKfSessionGetResult.class); } - public long getCreateTime() { - return this.createTime; - } - - public void setCreateTime(long createTime) { - this.createTime = createTime; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionList.java index a9a0640e6d..37708a2b49 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionList.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionList.java @@ -1,38 +1,33 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** - * * @author Binary Wang - * */ -public class WxMpKfSessionList { +@Data +public class WxMpKfSessionList implements Serializable { + private static final long serialVersionUID = -7680371346226640206L; + /** * 会话列表 */ @SerializedName("sessionlist") private List kfSessionList; - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - public static WxMpKfSessionList fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, - WxMpKfSessionList.class); + return WxMpGsonBuilder.create().fromJson(json, + WxMpKfSessionList.class); } - public List getKfSessionList() { - return this.kfSessionList; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } - public void setKfSessionList(List kfSessionList) { - this.kfSessionList = kfSessionList; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionWaitCaseList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionWaitCaseList.java index 22bcc4da67..2fb0ea560b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionWaitCaseList.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/result/WxMpKfSessionWaitCaseList.java @@ -1,17 +1,19 @@ package me.chanjar.weixin.mp.bean.kefu.result; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** - * * @author Binary Wang - * */ -public class WxMpKfSessionWaitCaseList { +@Data +public class WxMpKfSessionWaitCaseList implements Serializable { + private static final long serialVersionUID = 2432132626631361922L; + /** * count 未接入会话数量 */ @@ -24,22 +26,14 @@ public class WxMpKfSessionWaitCaseList { @SerializedName("waitcaselist") private List kfSessionWaitCaseList; - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - public static WxMpKfSessionWaitCaseList fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, - WxMpKfSessionWaitCaseList.class); - } - - public List getKfSessionWaitCaseList() { - return this.kfSessionWaitCaseList; + return WxMpGsonBuilder.create().fromJson(json, + WxMpKfSessionWaitCaseList.class); } - public void setKfSessionWaitCaseList(List kfSessionWaitCaseList) { - this.kfSessionWaitCaseList = kfSessionWaitCaseList; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLead.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLead.java new file mode 100644 index 0000000000..868d22a96b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLead.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ +@Data +public class WxMpAdLead implements Serializable { + private static final long serialVersionUID = -8889087268596440407L; + /** + * 点击ID + */ + @SerializedName("click_id") + private String click_id; + /** + * 广告组ID + */ + @SerializedName("adgroup_id") + private Long adgroup_id; + /** + * 广告组名称 + */ + @SerializedName("adgroup_name") + private String adgroup_name; + /** + * 推广计划ID + */ + @SerializedName("campaign_id") + private Long campaign_id; + /** + * 推广计划名称 + */ + @SerializedName("campaign_name") + private String campaign_name; + /** + * 代理ID + */ + @SerializedName("agency_id") + private String agency_id; + /** + * 代理名称 + */ + @SerializedName("agency_name") + private String agency_name; + /** + * 销售线索信息 + */ + @SerializedName("leads_info") + private List leads_info; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadFilter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadFilter.java new file mode 100644 index 0000000000..3923025336 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadFilter.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadFilter implements Serializable { + private static final long serialVersionUID = -1469998986497327439L; + private String field; + private String operator; + private List values; + + public JsonObject toJsonObject() { + JsonObject json = new JsonObject(); + json.addProperty("field", field); + json.addProperty("operator", operator); + if (values != null) { + JsonArray vs = new JsonArray(); + for (String value : values) { + vs.add(value); + } + } + return json; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadInfo.java new file mode 100644 index 0000000000..859a0dca5e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadInfo.java @@ -0,0 +1,18 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadInfo implements Serializable { + private static final long serialVersionUID = -6462312242780350479L; + @SerializedName("key") + private String key; + @SerializedName("value") + private String value; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadPageInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadPageInfo.java new file mode 100644 index 0000000000..296a3fef82 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadPageInfo.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadPageInfo implements Serializable { + private static final long serialVersionUID = -896765006445604780L; + @SerializedName("page") + private Integer page; + @SerializedName("page_size") + private Integer pageSize; + @SerializedName("total_page") + private Integer totalPage; + @SerializedName("total_number") + private Integer totalNumber; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadResult.java new file mode 100644 index 0000000000..7ce44ddbe2 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadResult.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadResult implements Serializable { + private static final long serialVersionUID = -1526796632563660821L; + + @SerializedName("page_info") + private WxMpAdLeadPageInfo pageInfo; + @SerializedName("list") + private List adLeads; + + public static WxMpAdLeadResult fromJson(String json) { + + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("data"), + new TypeToken() { + }.getType()); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserAction.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserAction.java new file mode 100644 index 0000000000..ad5f723e53 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserAction.java @@ -0,0 +1,71 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class WxMpUserAction implements Serializable { + private static final long serialVersionUID = 7042393762652152209L; + + private Long userActionSetId; + private String url; + private Integer actionTime; + private String actionType; + private String leadsType; + private String clickId; + private Integer actionParam; + + private JsonObject toJsonObject() { + JsonObject json = new JsonObject(); + json.addProperty("user_action_set_id", this.userActionSetId); + json.addProperty("url", this.url); + json.addProperty("action_time", this.actionTime); + json.addProperty("action_type", this.actionType); + + if (this.clickId != null) { + JsonObject traceJson = new JsonObject(); + traceJson.addProperty("click_id", this.clickId); + json.add("trace", traceJson); + } + + if (this.actionParam != null) { + JsonObject actionParamJson = new JsonObject(); + actionParamJson.addProperty("value", actionParam); + actionParamJson.addProperty("leads_type", leadsType); + json.add("action_param", actionParamJson); + } + + return json; + } + + /** + * list对象转换为json字符串 + * + * @param actions . + * @return . + */ + public static String listToJson(List actions) { + JsonArray array = new JsonArray(); + for (WxMpUserAction action : actions) { + array.add(action.toJsonObject()); + } + + JsonObject result = new JsonObject(); + result.add("actions", array); + return result.toString(); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionSet.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionSet.java new file mode 100644 index 0000000000..1bf41b4871 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionSet.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ + +@Data +public class WxMpUserActionSet implements Serializable { + private static final long serialVersionUID = 1979861770645159905L; + + /** + * user_action_set_id + * 用户行为源名称 + */ + @SerializedName("user_action_set_id") + private Long userActionSetId; + + /** + * title. + * 用户行为源描述 + */ + @SerializedName("description") + private String description; + + /** + * activate_status. + * 数据接入状态, true 表示已接入, false 表示未接入 + */ + @SerializedName("activate_status") + private Boolean activate_status; + + /** + * created_time. + * 创建时间 + */ + @SerializedName("created_time") + private String createdTime; + + public static List fromJson(String json) { + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("data").getAsJsonObject().get("list"), + new TypeToken>() { + }.getType()); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMediaImgUploadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMediaImgUploadResult.java index 387a791ac6..b60b2791c8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMediaImgUploadResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMediaImgUploadResult.java @@ -1,5 +1,6 @@ package me.chanjar.weixin.mp.bean.material; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; @@ -7,10 +8,8 @@ /** * @author miller */ +@Data public class WxMediaImgUploadResult implements Serializable { - /** - * - */ private static final long serialVersionUID = 1996392453428768829L; private String url; @@ -18,11 +17,4 @@ public static WxMediaImgUploadResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMediaImgUploadResult.class); } - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterial.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterial.java index 8725510a84..a554834d01 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterial.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterial.java @@ -1,10 +1,15 @@ package me.chanjar.weixin.mp.bean.material; +import lombok.Data; + import java.io.File; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; -public class WxMpMaterial { +@Data +public class WxMpMaterial implements Serializable { + private static final long serialVersionUID = -1651816949780969485L; private String name; private File file; @@ -27,41 +32,4 @@ public Map getForm() { form.put("introduction", this.videoIntroduction); return form; } - - public String getVideoTitle() { - return this.videoTitle; - } - - public void setVideoTitle(String videoTitle) { - this.videoTitle = videoTitle; - } - - public String getVideoIntroduction() { - return this.videoIntroduction; - } - - public void setVideoIntroduction(String videoIntroduction) { - this.videoIntroduction = videoIntroduction; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public File getFile() { - return this.file; - } - - public void setFile(File file) { - this.file = file; - } - - @Override - public String toString() { - return "WxMpMaterial [" + "name=" + this.name + ", file=" + this.file + ", videoTitle=" + this.videoTitle + ", videoIntroduction=" + this.videoIntroduction + "]"; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialArticleUpdate.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialArticleUpdate.java index 371b7e0d30..04bfc919fa 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialArticleUpdate.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialArticleUpdate.java @@ -1,49 +1,19 @@ package me.chanjar.weixin.mp.bean.material; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; +@Data public class WxMpMaterialArticleUpdate implements Serializable { - - /** - * - */ private static final long serialVersionUID = -7611963949517780270L; + private String mediaId; private int index; - private WxMpMaterialNews.WxMpMaterialNewsArticle articles; - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public int getIndex() { - return this.index; - } - - public void setIndex(int index) { - this.index = index; - } - - public WxMpMaterialNews.WxMpMaterialNewsArticle getArticles() { - return this.articles; - } - - public void setArticles(WxMpMaterialNews.WxMpMaterialNewsArticle articles) { - this.articles = articles; - } + private WxMpNewsArticle articles; public String toJson() { return WxMpGsonBuilder.create().toJson(this); } - - @Override - public String toString() { - return "WxMpMaterialArticleUpdate [" + "mediaId=" + this.mediaId + ", index=" + this.index + ", articles=" + this.articles + "]"; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialCountResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialCountResult.java index 6dd74889ca..721b31fc9f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialCountResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialCountResult.java @@ -1,51 +1,25 @@ package me.chanjar.weixin.mp.bean.material; -import me.chanjar.weixin.common.util.ToStringUtils; - import java.io.Serializable; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * @author codepiano + */ +@Data public class WxMpMaterialCountResult implements Serializable { private static final long serialVersionUID = -5568772662085874138L; + private int voiceCount; private int videoCount; private int imageCount; private int newsCount; - public int getVoiceCount() { - return this.voiceCount; - } - - public void setVoiceCount(int voiceCount) { - this.voiceCount = voiceCount; - } - - public int getVideoCount() { - return this.videoCount; - } - - public void setVideoCount(int videoCount) { - this.videoCount = videoCount; - } - - public int getImageCount() { - return this.imageCount; - } - - public void setImageCount(int imageCount) { - this.imageCount = imageCount; - } - - public int getNewsCount() { - return this.newsCount; - } - - public void setNewsCount(int newsCount) { - this.newsCount = newsCount; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialFileBatchGetResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialFileBatchGetResult.java index 5df9f4546c..ebad06bfec 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialFileBatchGetResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialFileBatchGetResult.java @@ -1,87 +1,38 @@ package me.chanjar.weixin.mp.bean.material; -import me.chanjar.weixin.common.util.ToStringUtils; - import java.io.Serializable; import java.util.Date; import java.util.List; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * @author codepiano + */ +@Data public class WxMpMaterialFileBatchGetResult implements Serializable { private static final long serialVersionUID = -560388368297267884L; + private int totalCount; private int itemCount; private List items; - public int getTotalCount() { - return this.totalCount; - } - - public void setTotalCount(int totalCount) { - this.totalCount = totalCount; - } - - public int getItemCount() { - return this.itemCount; - } - - public void setItemCount(int itemCount) { - this.itemCount = itemCount; - } - - public List getItems() { - return this.items; - } - - public void setItems(List items) { - this.items = items; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } + @Data public static class WxMaterialFileBatchGetNewsItem { private String mediaId; private Date updateTime; private String name; private String url; - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public Date getUpdateTime() { - return this.updateTime; - } - - public void setUpdateTime(Date updateTime) { - this.updateTime = updateTime; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java index f67a8cb4fa..dbb855a582 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java @@ -1,167 +1,46 @@ package me.chanjar.weixin.mp.bean.material; -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 图文素材. + * + * @author codepiano + */ +@Data public class WxMpMaterialNews implements Serializable { private static final long serialVersionUID = -3283203652013494976L; - private List articles = new ArrayList<>(); + private Date createTime; + private Date updateTime; - public List getArticles() { + private List articles = new ArrayList<>(); + + public List getArticles() { return this.articles; } - public void addArticle(WxMpMaterialNewsArticle article) { + public void addArticle(WxMpNewsArticle article) { this.articles.add(article); } public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } public boolean isEmpty() { return this.articles == null || this.articles.isEmpty(); } - /** - *
    -   * 群发图文消息article
    -   * 1. thumbMediaId  (必填) 图文消息的封面图片素材id(必须是永久mediaID)
    -   * 2. author          图文消息的作者
    -   * 3. title           (必填) 图文消息的标题
    -   * 4. contentSourceUrl 在图文消息页面点击“阅读原文”后的页面链接
    -   * 5. content (必填)  图文消息页面的内容,支持HTML标签
    -   * 6. digest          图文消息的描述
    -   * 7. showCoverPic  是否显示封面,true为显示,false为不显示
    -   * 8. url           点击图文消息跳转链接
    -   * 
    - * - * @author chanjarster - */ - public static class WxMpMaterialNewsArticle { - /** - * (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得 - */ - private String thumbMediaId; - /** - * 图文消息的封面url - */ - private String thumbUrl; - /** - * 图文消息的作者 - */ - private String author; - /** - * (必填) 图文消息的标题 - */ - private String title; - /** - * 在图文消息页面点击“阅读原文”后的页面链接 - */ - private String contentSourceUrl; - /** - * (必填) 图文消息页面的内容,支持HTML标签 - */ - private String content; - /** - * 图文消息的描述 - */ - private String digest; - /** - * 是否显示封面,true为显示,false为不显示 - */ - private boolean showCoverPic; - - /** - * 点击图文消息跳转链接 - */ - private String url; - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public String getAuthor() { - return this.author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getContentSourceUrl() { - return this.contentSourceUrl; - } - - public void setContentSourceUrl(String contentSourceUrl) { - this.contentSourceUrl = contentSourceUrl; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getDigest() { - return this.digest; - } - - public void setDigest(String digest) { - this.digest = digest; - } - - public boolean isShowCoverPic() { - return this.showCoverPic; - } - - public void setShowCoverPic(boolean showCoverPic) { - this.showCoverPic = showCoverPic; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getThumbUrl() { - return this.thumbUrl; - } - - public void setThumbUrl(String thumbUrl) { - this.thumbUrl = thumbUrl; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return this.toJson(); } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNewsBatchGetResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNewsBatchGetResult.java index 03f0a6d854..894709ac8f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNewsBatchGetResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNewsBatchGetResult.java @@ -1,11 +1,13 @@ package me.chanjar.weixin.mp.bean.material; -import me.chanjar.weixin.common.util.ToStringUtils; - import java.io.Serializable; import java.util.Date; import java.util.List; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +@Data public class WxMpMaterialNewsBatchGetResult implements Serializable { private static final long serialVersionUID = -1617952797921001666L; @@ -13,67 +15,20 @@ public class WxMpMaterialNewsBatchGetResult implements Serializable { private int itemCount; private List items; - public int getTotalCount() { - return this.totalCount; - } - - public void setTotalCount(int totalCount) { - this.totalCount = totalCount; - } - - public int getItemCount() { - return this.itemCount; - } - - public void setItemCount(int itemCount) { - this.itemCount = itemCount; - } - - public List getItems() { - return this.items; - } - - public void setItems(List items) { - this.items = items; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } + @Data public static class WxMaterialNewsBatchGetNewsItem { private String mediaId; private Date updateTime; private WxMpMaterialNews content; - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public Date getUpdateTime() { - return this.updateTime; - } - - public void setUpdateTime(Date updateTime) { - this.updateTime = updateTime; - } - - public WxMpMaterialNews getContent() { - return this.content; - } - - public void setContent(WxMpMaterialNews content) { - this.content = content; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java index 2b3a1e5785..0d133a763e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java @@ -1,33 +1,17 @@ package me.chanjar.weixin.mp.bean.material; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; -public class WxMpMaterialUploadResult implements Serializable { +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - /** - * - */ +@Data +public class WxMpMaterialUploadResult implements Serializable { private static final long serialVersionUID = -128818731449449537L; private String mediaId; private String url; - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } + private Integer errCode; + private String errMsg; public static WxMpMaterialUploadResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMpMaterialUploadResult.class); @@ -35,7 +19,7 @@ public static WxMpMaterialUploadResult fromJson(String json) { @Override public String toString() { - return "WxMpMaterialUploadResult [media_id=" + this.mediaId + ", url=" + this.url + "]"; + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialVideoInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialVideoInfoResult.java index 06b85b7e9b..8a783e6809 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialVideoInfoResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialVideoInfoResult.java @@ -1,50 +1,20 @@ package me.chanjar.weixin.mp.bean.material; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; +@Data public class WxMpMaterialVideoInfoResult implements Serializable { - - /** - * - */ private static final long serialVersionUID = 1269131745333792202L; + private String title; private String description; private String downUrl; - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getDownUrl() { - return this.downUrl; - } - - public void setDownUrl(String downUrl) { - this.downUrl = downUrl; - } - public static WxMpMaterialVideoInfoResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMpMaterialVideoInfoResult.class); } - @Override - public String toString() { - return "WxMpMaterialVideoInfoResult [title=" + this.title + ", description=" + this.description + ", downUrl=" + this.downUrl + "]"; - } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpNewsArticle.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpNewsArticle.java new file mode 100644 index 0000000000..3e08a94b91 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpNewsArticle.java @@ -0,0 +1,82 @@ +package me.chanjar.weixin.mp.bean.material; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + *
    + * 图文消息article.
    + * 1. thumbMediaId  (必填) 图文消息的封面图片素材id(必须是永久mediaID)
    + * 2. author          图文消息的作者
    + * 3. title           (必填) 图文消息的标题
    + * 4. contentSourceUrl 在图文消息页面点击“阅读原文”后的页面链接
    + * 5. content (必填)  图文消息页面的内容,支持HTML标签
    + * 6. digest          图文消息的描述
    + * 7. showCoverPic  是否显示封面,true为显示,false为不显示
    + * 8. url           点击图文消息跳转链接
    + * 9. need_open_comment(新增字段) 否 Uint32 是否打开评论,0不打开,1打开
    + * 10. only_fans_can_comment(新增字段) 否 Uint32 是否粉丝才可评论,0所有人可评论,1粉丝才可评论
    + * 
    + * + * @author chanjarster + */ +@Data +public class WxMpNewsArticle implements Serializable { + private static final long serialVersionUID = -635384661692321171L; + /** + * (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得. + */ + private String thumbMediaId; + /** + * 图文消息的封面url. + */ + private String thumbUrl; + /** + * 图文消息的作者. + */ + private String author; + /** + * (必填) 图文消息的标题. + */ + private String title; + /** + * 在图文消息页面点击“阅读原文”后的页面链接. + */ + private String contentSourceUrl; + /** + * (必填) 图文消息页面的内容,支持HTML标签. + */ + private String content; + /** + * 图文消息的描述. + */ + private String digest; + /** + * 是否显示封面,true为显示,false为不显示. + */ + private boolean showCoverPic; + + /** + * 点击图文消息跳转链接. + */ + private String url; + + /** + * need_open_comment + * 是否打开评论,0不打开,1打开. + */ + private Boolean needOpenComment; + + /** + * only_fans_can_comment + * 是否粉丝才可评论,0所有人可评论,1粉丝才可评论. + */ + private Boolean onlyFansCanComment; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java index b78d2dc378..8fb937ce9b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java @@ -1,16 +1,23 @@ package me.chanjar.weixin.mp.bean.menu; +import java.io.Serializable; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** *
      * Created by Binary Wang on 2016-11-25.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ -public class WxMpGetSelfMenuInfoResult { +@Data +public class WxMpGetSelfMenuInfoResult implements Serializable { + private static final long serialVersionUID = -5612495636936835166L; + @SerializedName("selfmenu_info") private WxMpSelfMenuInfo selfMenuInfo; @@ -23,22 +30,7 @@ public static WxMpGetSelfMenuInfoResult fromJson(String json) { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } - public WxMpSelfMenuInfo getSelfMenuInfo() { - return selfMenuInfo; - } - - public void setSelfMenuInfo(WxMpSelfMenuInfo selfMenuInfo) { - this.selfMenuInfo = selfMenuInfo; - } - - public Integer getIsMenuOpen() { - return isMenuOpen; - } - - public void setIsMenuOpen(Integer isMenuOpen) { - this.isMenuOpen = isMenuOpen; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java index 366ca2f1ba..f94c59e29d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java @@ -1,21 +1,27 @@ package me.chanjar.weixin.mp.bean.menu; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; +import lombok.Data; import me.chanjar.weixin.common.bean.menu.WxMenuButton; import me.chanjar.weixin.common.bean.menu.WxMenuRule; -import me.chanjar.weixin.common.util.ToStringUtils; import me.chanjar.weixin.common.util.json.WxGsonBuilder; - -import java.util.List; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** *
      *   公众号专用的菜单类,可能包含个性化菜单
      * Created by Binary Wang on 2017-1-17.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ -public class WxMpMenu { +@Data +public class WxMpMenu implements Serializable { + private static final long serialVersionUID = -5794350513426702252L; + @SerializedName("menu") private WxMpConditionalMenu menu; @@ -26,32 +32,19 @@ public static WxMpMenu fromJson(String json) { return WxGsonBuilder.create().fromJson(json, WxMpMenu.class); } - public WxMpConditionalMenu getMenu() { - return menu; - } - - public void setMenu(WxMpConditionalMenu menu) { - this.menu = menu; - } - - public List getConditionalMenu() { - return conditionalMenu; - } - - public void setConditionalMenu(List conditionalMenu) { - this.conditionalMenu = conditionalMenu; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return this.toJson(); } public String toJson() { return WxGsonBuilder.create().toJson(this); } - public static class WxMpConditionalMenu { + @Data + public static class WxMpConditionalMenu implements Serializable { + private static final long serialVersionUID = -2279946921755382289L; + @SerializedName("button") private List buttons; @SerializedName("matchrule") @@ -61,32 +54,9 @@ public static class WxMpConditionalMenu { @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public List getButtons() { - return buttons; - } - - public void setButtons(List buttons) { - this.buttons = buttons; - } - - public WxMenuRule getRule() { - return rule; - } - - public void setRule(WxMenuRule rule) { - this.rule = rule; + return WxMpGsonBuilder.create().toJson(this); } - public String getMenuId() { - return menuId; - } - - public void setMenuId(String menuId) { - this.menuId = menuId; - } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java index f2c0c4ca51..f0e0a1049a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java @@ -1,18 +1,24 @@ package me.chanjar.weixin.mp.bean.menu; -import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; - +import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + /** *
      * Created by Binary Wang on 2016-11-25.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ -public class WxMpSelfMenuInfo { +@Data +public class WxMpSelfMenuInfo implements Serializable { + private static final long serialVersionUID = -81203094124202901L; + /** * 菜单按钮 */ @@ -21,14 +27,12 @@ public class WxMpSelfMenuInfo { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } - public static class WxMpSelfMenuButton { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } + @Data + public static class WxMpSelfMenuButton implements Serializable { + private static final long serialVersionUID = -4426602953309048341L; /** *
    @@ -38,16 +42,14 @@ public String toString() {
          */
         @SerializedName("type")
         private String type;
    -
         /**
    -     * 菜单名称
    +     * 菜单名称.
          */
         @SerializedName("name")
         private String name;
    -
         /**
          * 
    -     * 对于不同的菜单类型,value的值意义不同。
    +     * 对于不同的菜单类型,value的值意义不同.
          * 官网上设置的自定义菜单:
          *  
  1. Text:保存文字到value; *
  2. Img、voice:保存mediaID到value; @@ -56,231 +58,129 @@ public String toString() { *
  3. View:保存链接到url。
  4. * * 使用API设置的自定义菜单: - *
  5. click、scancode_push、scancode_waitmsg、pic_sysphoto、pic_photo_or_album、 pic_weixin、location_select:保存值到key; + *
  6. click、scancode_push、scancode_waitmsg、pic_sysphoto、pic_photo_or_album、pic_weixin、location_select:保存值到key; *
  7. view:保存链接到url *
  8. */ @SerializedName("key") private String key; - /** + * . + * * @see #key */ @SerializedName("url") private String url; /** + * . + * * @see #key */ @SerializedName("value") private String value; /** - * 子菜单信息 + *
    +     * 小程序的appid.
    +     * miniprogram类型必须
    +     * 
    + */ + @SerializedName("appid") + private String appId; + + /** + *
    +     * 小程序的页面路径.
    +     * miniprogram类型必须
    +     * 
    + */ + @SerializedName("pagepath") + private String pagePath; + /** + * 子菜单信息. */ @SerializedName("sub_button") private SubButtons subButtons; - - public SubButtons getSubButtons() { - return subButtons; - } - - public void setSubButtons(SubButtons subButtons) { - this.subButtons = subButtons; - } - - public static class SubButtons { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - @SerializedName("list") - private List subButtons = new ArrayList<>(); - - public List getSubButtons() { - return subButtons; - } - - public void setSubButtons(List subButtons) { - this.subButtons = subButtons; - } - } - /** - * 图文消息的信息 + * 图文消息的信息. */ @SerializedName("news_info") private NewsInfo newsInfo; - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } - public NewsInfo getNewsInfo() { - return newsInfo; - } + @Data + public static class SubButtons implements Serializable { + private static final long serialVersionUID = 1763350658575521079L; - public void setNewsInfo(NewsInfo newsInfo) { - this.newsInfo = newsInfo; - } + @SerializedName("list") + private List subButtons = new ArrayList<>(); - public static class NewsInfo { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } + } + + @Data + public static class NewsInfo implements Serializable { + private static final long serialVersionUID = 3449813746347818457L; @SerializedName("list") private List news = new ArrayList<>(); - public List getNews() { - return news; - } - - public void setNews(List news) { - this.news = news; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } - public static class NewsInButton { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } + @Data + public static class NewsInButton implements Serializable { + private static final long serialVersionUID = 8701455967664912972L; /** - * 图文消息的标题 + * 图文消息的标题. */ @SerializedName("title") private String title; - /** - * 摘要 + * 摘要. */ @SerializedName("digest") private String digest; - /** - * 作者 + * 作者. */ @SerializedName("author") private String author; - /** - * show_cover - * 是否显示封面,0为不显示,1为显示 + * 是否显示封面,0为不显示,1为显示. */ @SerializedName("show_cover") private Integer showCover; - /** - * 封面图片的URL + * 封面图片的URL. */ @SerializedName("cover_url") private String coverUrl; - /** - * 正文的URL + * 正文的URL. */ @SerializedName("content_url") private String contentUrl; - /** - * 原文的URL,若置空则无查看原文入口 + * 原文的URL,若置空则无查看原文入口. */ @SerializedName("source_url") private String sourceUrl; - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDigest() { - return digest; - } - - public void setDigest(String digest) { - this.digest = digest; - } - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public Integer getShowCover() { - return showCover; - } - - public void setShowCover(Integer showCover) { - this.showCover = showCover; - } - - public String getCoverUrl() { - return coverUrl; - } - - public void setCoverUrl(String coverUrl) { - this.coverUrl = coverUrl; - } - - public String getContentUrl() { - return contentUrl; - } - - public void setContentUrl(String contentUrl) { - this.contentUrl = contentUrl; - } - - public String getSourceUrl() { - return sourceUrl; - } - - public void setSourceUrl(String sourceUrl) { - this.sourceUrl = sourceUrl; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java new file mode 100644 index 0000000000..a75c98e02c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.mp.bean.message; + +import java.io.Serializable; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + *
    + *  Created by BinaryWang on 2017/5/4.
    + * 
    + * + * @author Binary Wang + */ +@XStreamAlias("HardWare") +@Data +public class HardWare implements Serializable { + private static final long serialVersionUID = -1295785297354896461L; + + /** + * 消息展示,目前支持myrank(排行榜) + */ + @XStreamAlias("MessageView") + @XStreamConverter(value = XStreamCDataConverter.class) + private String messageView; + /** + * 消息点击动作,目前支持ranklist(点击跳转排行榜) + */ + @XStreamAlias("MessageAction") + @XStreamConverter(value = XStreamCDataConverter.class) + private String messageAction; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java new file mode 100644 index 0000000000..f53b44c41d --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.mp.bean.message; + +import java.io.Serializable; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + *
    + *  Created by BinaryWang on 2017/5/4.
    + * 
    + * + * @author Binary Wang + */ +@XStreamAlias("ScanCodeInfo") +@Data +public class ScanCodeInfo implements Serializable { + private static final long serialVersionUID = 4745181270645050122L; + + /** + * 扫描类型,一般是qrcode. + */ + @XStreamAlias("ScanType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String scanType; + + /** + * 扫描结果,即二维码对应的字符串信息. + */ + @XStreamAlias("ScanResult") + @XStreamConverter(value = XStreamCDataConverter.class) + private String scanResult; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java new file mode 100644 index 0000000000..09f1776bb5 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.mp.bean.message; + +import java.io.Serializable; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + *
    + *  Created by BinaryWang on 2017/5/4.
    + * 
    + * + * @author Binary Wang + */ +@XStreamAlias("SendLocationInfo") +@Data +public class SendLocationInfo implements Serializable { + private static final long serialVersionUID = 6633214140499161130L; + + @XStreamAlias("Location_X") + @XStreamConverter(value = XStreamCDataConverter.class) + private String locationX; + + @XStreamAlias("Location_Y") + @XStreamConverter(value = XStreamCDataConverter.class) + private String locationY; + + @XStreamAlias("Scale") + @XStreamConverter(value = XStreamCDataConverter.class) + private String scale; + + @XStreamAlias("Label") + @XStreamConverter(value = XStreamCDataConverter.class) + private String label; + + @XStreamAlias("Poiname") + @XStreamConverter(value = XStreamCDataConverter.class) + private String poiName; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java new file mode 100644 index 0000000000..a01e66596c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.mp.bean.message; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + *
    + *  Created by BinaryWang on 2017/5/4.
    + * 
    + * + * @author Binary Wang + */ +@XStreamAlias("SendPicsInfo") +@Data +public class SendPicsInfo implements Serializable { + private static final long serialVersionUID = -4572837013294199227L; + + @XStreamAlias("PicList") + protected final List picList = new ArrayList<>(); + + @XStreamAlias("Count") + private Long count; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + @XStreamAlias("item") + @Data + public static class Item implements Serializable { + private static final long serialVersionUID = 7706235740094081194L; + + @XStreamAlias("PicMd5Sum") + @XStreamConverter(value = XStreamCDataConverter.class) + private String picMd5Sum; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java index 0f1a3ffe62..f6b54a20b2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java @@ -2,22 +2,26 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.util.XmlUtils; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import me.chanjar.weixin.mp.util.xml.XStreamTransformer; import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; +import java.nio.charset.StandardCharsets; +import java.util.Map; /** *
    - * 微信推送过来的消息,xml格式
    + * 微信推送过来的消息,xml格式.
      * 部分未注释的字段的解释请查阅相关微信开发文档:
      * 接收普通消息
      * 接收事件推送
    @@ -25,11 +29,17 @@
      *
      * @author chanjarster
      */
    +@Data
    +@Slf4j
     @XStreamAlias("xml")
     public class WxMpXmlMessage implements Serializable {
    -
       private static final long serialVersionUID = -3586245291677274914L;
     
    +  /**
    +   * 使用dom4j解析的存放所有xml属性和值的map.
    +   */
    +  private Map allFieldsMap;
    +
       ///////////////////////
       // 以下都是微信推送过来的消息的xml的element所对应的属性
       ///////////////////////
    @@ -125,32 +135,37 @@ public class WxMpXmlMessage implements Serializable {
       @XStreamConverter(value = XStreamCDataConverter.class)
       private String recognition;
     
    +  @XStreamAlias("UnionId")
    +  @XStreamConverter(value = XStreamCDataConverter.class)
    +  private String unionId;
    +
       ///////////////////////////////////////
       // 群发消息返回的结果
       ///////////////////////////////////////
       /**
    -   * 群发的结果
    +   * 群发的结果.
        */
       @XStreamAlias("Status")
       @XStreamConverter(value = XStreamCDataConverter.class)
       private String status;
       /**
    -   * group_id下粉丝数;或者openid_list中的粉丝数
    +   * group_id下粉丝数;或者openid_list中的粉丝数.
        */
       @XStreamAlias("TotalCount")
       private Integer totalCount;
       /**
    -   * 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount
    +   * 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数.
    +   * 原则上,filterCount = sentCount + errorCount
        */
       @XStreamAlias("FilterCount")
       private Integer filterCount;
       /**
    -   * 发送成功的粉丝数
    +   * 发送成功的粉丝数.
        */
       @XStreamAlias("SentCount")
       private Integer sentCount;
       /**
    -   * 发送失败的粉丝数
    +   * 发送失败的粉丝数.
        */
       @XStreamAlias("ErrorCount")
       private Integer errorCount;
    @@ -159,17 +174,17 @@ public class WxMpXmlMessage implements Serializable {
       // 客服会话管理相关事件推送
       ///////////////////////////////////////
       /**
    -   * 创建或关闭客服会话时的客服帐号
    +   * 创建或关闭客服会话时的客服帐号.
        */
       @XStreamAlias("KfAccount")
       private String kfAccount;
       /**
    -   * 转接客服会话时的转入客服帐号
    +   * 转接客服会话时的转入客服帐号.
        */
       @XStreamAlias("ToKfAccount")
       private String toKfAccount;
       /**
    -   * 转接客服会话时的转出客服帐号
    +   * 转接客服会话时的转出客服帐号.
        */
       @XStreamAlias("FromKfAccount")
       private String fromKfAccount;
    @@ -177,6 +192,7 @@ public class WxMpXmlMessage implements Serializable {
       ///////////////////////////////////////
       // 卡券相关事件推送
       ///////////////////////////////////////
    +
       @XStreamAlias("CardId")
       @XStreamConverter(value = XStreamCDataConverter.class)
       private String cardId;
    @@ -185,8 +201,11 @@ public class WxMpXmlMessage implements Serializable {
       @XStreamConverter(value = XStreamCDataConverter.class)
       private String friendUserName;
     
    +  /**
    +   * 是否为转赠,1代表是,0代表否.
    +   */
       @XStreamAlias("IsGiveByFriend")
    -  private Integer isGiveByFriend; // 是否为转赠,1代表是,0代表否
    +  private Integer isGiveByFriend;
     
       @XStreamAlias("UserCardCode")
       @XStreamConverter(value = XStreamCDataConverter.class)
    @@ -199,6 +218,131 @@ public class WxMpXmlMessage implements Serializable {
       @XStreamAlias("OuterId")
       private Integer outerId;
     
    +  /**
    +   * 用户删除会员卡后可重新找回,当用户本次操作为找回时,该值为1,否则为0.
    +   */
    +  @XStreamAlias("IsRestoreMemberCard")
    +  private String isRestoreMemberCard;
    +
    +  /**
    +   * 
    +   * 领取场景值,用于领取渠道数据统计。可在生成二维码接口及添加Addcard接口中自定义该字段的字符串值.
    +   * 核销卡券时:开发者发起核销时传入的自定义参数,用于进行核销渠道统计
    +   * 另外:
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.7 进入会员卡事件推送 user_view_card
    +   * OuterStr:商户自定义二维码渠道参数,用于标识本次扫码打开会员卡来源来自于某个渠道值的二维码
    +   * 
    + */ + @XStreamAlias("OuterStr") + private String outerStr; + + /** + * 是否转赠退回,0代表不是,1代表是. + */ + @XStreamAlias("IsReturnBack") + private String isReturnBack; + + /** + * 是否是群转赠,0代表不是,1代表是. + */ + @XStreamAlias("IsChatRoom") + private String isChatRoom; + + /** + * 核销来源. + * 支持开发者统计API核销(FROM_API)、公众平台核销(FROM_MP)、卡券商户助手核销(FROM_MOBILE_HELPER)(核销员微信号) + */ + @XStreamAlias("ConsumeSource") + private String consumeSource; + + /** + * 门店名称. + * 当前卡券核销的门店名称(只有通过自助核销和买单核销时才会出现该字段) + */ + @XStreamAlias("LocationName") + private String locationName; + + /** + * 核销该卡券核销员的openid(只有通过卡券商户助手核销时才会出现). + */ + @XStreamAlias("StaffOpenId") + private String staffOpenId; + + /** + * 自助核销时,用户输入的验证码. + */ + @XStreamAlias("VerifyCode") + private String verifyCode; + + /** + * 自助核销时,用户输入的备注金额. + */ + @XStreamAlias("RemarkAmount") + private String remarkAmount; + + /** + *
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.10 库存报警事件card_sku_remind
    +   * Detail:报警详细信息.
    +   * 
    + */ + @XStreamAlias("Detail") + private String detail; + + /** + *
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.9 会员卡内容更新事件 update_member_card
    +   * ModifyBonus:变动的积分值.
    +   * 
    + */ + @XStreamAlias("ModifyBonus") + private String modifyBonus; + + /** + *
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.9 会员卡内容更新事件 update_member_card
    +   * ModifyBalance:变动的余额值.
    +   * 
    + */ + @XStreamAlias("ModifyBalance") + private String modifyBalance; + + /** + *
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
    +   * TransId:微信支付交易订单号(只有使用买单功能核销的卡券才会出现).
    +   * 
    + */ + @XStreamAlias("TransId") + private String transId; + + /** + *
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
    +   * LocationId:门店ID,当前卡券核销的门店ID(只有通过卡券商户助手和买单核销时才会出现)
    +   * 
    + */ + @XStreamAlias("LocationId") + private String locationId; + + /** + *
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
    +   * Fee:实付金额,单位为分
    +   * 
    + */ + @XStreamAlias("Fee") + private String fee; + + /** + *
    +   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
    +   * OriginalFee:应付金额,单位为分
    +   * 
    + */ + @XStreamAlias("OriginalFee") + private String originalFee; + @XStreamAlias("ScanCodeInfo") private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); @@ -208,33 +352,107 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("SendLocationInfo") private SendLocationInfo sendLocationInfo = new SendLocationInfo(); + /** + * 审核不通过原因 + */ + @XStreamAlias("RefuseReason") + private String refuseReason; + + /** + * 是否为朋友推荐,0代表否,1代表是 + */ + @XStreamAlias("IsRecommendByFriend") + private String isRecommendByFriend; + + /** + * 购买券点时,实际支付成功的时间 + */ + @XStreamAlias("PayFinishTime") + private String payFinishTime; + + /** + * 购买券点时,支付二维码的生成时间 + */ + @XStreamAlias("CreateOrderTime") + private String createOrderTime; + + /** + * 购买券点时,支付二维码的生成时间 + */ + @XStreamAlias("Desc") + private String desc; + + /** + * 剩余免费券点数量 + */ + @XStreamAlias("FreeCoinCount") + private String freeCoinCount; + + /** + * 剩余付费券点数量 + */ + @XStreamAlias("PayCoinCount") + private String payCoinCount; + + /** + * 本次变动的免费券点数量 + */ + @XStreamAlias("RefundFreeCoinCount") + private String refundFreeCoinCount; + + /** + * 本次变动的付费券点数量 + */ + @XStreamAlias("RefundPayCoinCount") + private String refundPayCoinCount; + + /** + *
    +   *    所要拉取的订单类型 ORDER_TYPE_SYS_ADD 平台赠送券点 ORDER_TYPE_WXPAY 充值券点 ORDER_TYPE_REFUND 库存未使用回退券点
    +   *    ORDER_TYPE_REDUCE 券点兑换库存 ORDER_TYPE_SYS_REDUCE 平台扣减
    +   * 
    + */ + @XStreamAlias("OrderType") + private String orderType; + + /** + * 系统备注,说明此次变动的缘由,如开通账户奖励、门店奖励、核销奖励以及充值、扣减。 + */ + @XStreamAlias("Memo") + private String memo; + + /** + * 所开发票的详情 + */ + @XStreamAlias("ReceiptInfo") + private String receiptInfo; + + /////////////////////////////////////// // 门店审核事件推送 /////////////////////////////////////// /** - * UniqId - * 商户自己内部ID,即字段中的sid + * 商户自己内部ID,即字段中的sid. */ @XStreamAlias("UniqId") private String storeUniqId; /** - * PoiId - * 微信的门店ID,微信内门店唯一标示ID + * 微信的门店ID,微信内门店唯一标示ID. */ @XStreamAlias("PoiId") private String poiId; /** - * Result - * 审核结果,成功succ 或失败fail + * 审核结果,成功succ 或失败fail. + * + * 在商品审核结果推送时,verify_ok表示审核通过,verify_not_pass表示审核未通过。 */ @XStreamAlias("Result") private String result; /** - * msg - * 成功的通知信息,或审核失败的驳回理由 + * 成功的通知信息,或审核失败的驳回理由. */ @XStreamAlias("msg") private String msg; @@ -243,46 +461,82 @@ public class WxMpXmlMessage implements Serializable { // 微信认证事件推送 /////////////////////////////////////// /** - * ExpiredTime - * 资质认证成功/名称认证成功: 有效期 (整形),指的是时间戳,将于该时间戳认证过期 + * 资质认证成功/名称认证成功: 有效期 (整形),指的是时间戳,将于该时间戳认证过期. * 年审通知: 有效期 (整形),指的是时间戳,将于该时间戳认证过期,需尽快年审 * 认证过期失效通知: 有效期 (整形),指的是时间戳,表示已于该时间戳认证过期,需要重新发起微信认证 */ @XStreamAlias("ExpiredTime") private Long expiredTime; /** - * FailTime - * 失败发生时间 (整形),时间戳 + * 失败发生时间 (整形),时间戳. */ @XStreamAlias("FailTime") private Long failTime; /** - * FailReason - * 认证失败的原因 + * 认证失败的原因. */ @XStreamAlias("FailReason") private String failReason; + /////////////////////////////////////// + // 微信小店 6.1订单付款通知 + /////////////////////////////////////// + /** + * 订单ID. + */ + @XStreamAlias("OrderId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String orderId; + + /** + * 订单状态. + */ + @XStreamAlias("OrderStatus") + private String orderStatus; + + /** + * 商品ID. + */ + @XStreamAlias("ProductId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String productId; + + /** + * 商品SKU信息. + */ + @XStreamAlias("SkuInfo") + @XStreamConverter(value = XStreamCDataConverter.class) + private String skuInfo; /////////////////////////////////////// // 微信硬件平台相关事件推送 /////////////////////////////////////// /** - * 设备类型,目前为"公众账号原始ID" + * 设备类型. + * 目前为"公众账号原始ID" */ @XStreamAlias("DeviceType") @XStreamConverter(value = XStreamCDataConverter.class) private String deviceType; /** - * 设备ID,第三方提供 + * 设备ID. + * 第三方提供 */ @XStreamAlias("DeviceID") @XStreamConverter(value = XStreamCDataConverter.class) private String deviceId; /** - * 微信用户账号的OpenID + * 微信客户端生成的session id,用于request和response对应, + * 因此响应中该字段第三方需要原封不变的带回 + */ + @XStreamAlias("SessionID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String sessionId; + + /** + * 微信用户账号的OpenID. */ @XStreamAlias("OpenID") @XStreamConverter(value = XStreamCDataConverter.class) @@ -292,185 +546,190 @@ public class WxMpXmlMessage implements Serializable { private HardWare hardWare = new HardWare(); /** - * 请求类型:0:退订设备状态;1:心跳;(心跳的处理方式跟订阅一样)2:订阅设备状态 + * 请求类型. + * 0:退订设备状态; + * 1:心跳;(心跳的处理方式跟订阅一样) + * 2:订阅设备状态 */ @XStreamAlias("OpType") private Integer opType; /** - * 设备状态:0:未连接;1:已连接 + * 设备状态. + * 0:未连接;1:已连接 */ @XStreamAlias("DeviceStatus") private Integer deviceStatus; - public static WxMpXmlMessage fromXml(String xml) { - return XStreamTransformer.fromXml(WxMpXmlMessage.class, xml); - } - - public static WxMpXmlMessage fromXml(InputStream is) { - return XStreamTransformer.fromXml(WxMpXmlMessage.class, is); - } - + /////////////////////////////////////// + // 小程序 审核事件 + /////////////////////////////////////// /** - * 从加密字符串转换 - * - * @param encryptedXml - * @param wxMpConfigStorage - * @param timestamp - * @param nonce - * @param msgSignature - */ - public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, - WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, - String msgSignature) { - WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); - String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, - encryptedXml); - return fromXml(plainText); - } - - public static WxMpXmlMessage fromEncryptedXml(InputStream is, - WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, - String msgSignature) { - try { - return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, - timestamp, nonce, msgSignature); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public Integer getOpType() { - return opType; - } - - public void setOpType(Integer opType) { - this.opType = opType; - } - - public Integer getDeviceStatus() { - - return deviceStatus; - } - - public void setDeviceStatus(Integer deviceStatus) { - this.deviceStatus = deviceStatus; - } - - public HardWare getHardWare() { - return hardWare; - } - - public void setHardWare(HardWare hardWare) { - this.hardWare = hardWare; - } - - public String getDeviceType() { - return deviceType; - } - - public void setDeviceType(String deviceType) { - this.deviceType = deviceType; - } + * 审核成功时的时间(整形),时间戳 + */ + @XStreamAlias("SuccTime") + private Long succTime; - public String getDeviceId() { - return deviceId; - } + /** + * 审核失败的原因 + */ + @XStreamAlias("Reason") + private String reason; - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } + /////////////////////////////////////// + // 扫一扫事件推送 + /////////////////////////////////////// + /** + * 商品编码标准 + */ + @XStreamAlias("KeyStandard") + private String keyStandard; + /** + * 商品编码内容 + */ + @XStreamAlias("KeyStr") + private String keyStr; - public String getOpenId() { - return openId; - } + /** + * 用户在微信内设置的国家 + */ + @XStreamAlias("Country") + private String country; - public void setOpenId(String openId) { - this.openId = openId; - } + /** + * 用户在微信内设置的省份 + */ + @XStreamAlias("Province") + private String province; - public Long getExpiredTime() { - return this.expiredTime; - } + /** + * 用户在微信内设置的城市 + */ + @XStreamAlias("City") + private String city; - public void setExpiredTime(Long expiredTime) { - this.expiredTime = expiredTime; - } + /** + * 用户的性别,1为男性,2为女性,0代表未知 + */ + @XStreamAlias("Sex") + private String sex; - public Long getFailTime() { - return this.failTime; - } + /** + * 打开商品主页的场景,1为扫码,2为其他打开场景(如会话、收藏或朋友圈) + */ + @XStreamAlias("Scene") + private String scene; - public void setFailTime(Long failTime) { - this.failTime = failTime; - } + /** + * 调用“获取商品二维码接口”时传入的extinfo,为标识参数 + */ + @XStreamAlias("ExtInfo") + private String extInfo; - public String getFailReason() { - return this.failReason; - } + /** + * 用户的实时地理位置信息(目前只精确到省一级),可在国家统计局网站查到对应明细: http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201504/t20150415_712722.html + */ + @XStreamAlias("RegionCode") + private String regionCode; - public void setFailReason(String failReason) { - this.failReason = failReason; - } + /** + * 审核未通过的原因. + */ + @XStreamAlias("ReasonMsg") + private String reasonMsg; - public String getStoreUniqId() { - return this.storeUniqId; - } + /** + * 给用户发菜单消息类型的客服消息后,用户所点击的菜单ID. + */ + @XStreamAlias("bizmsgmenuid") + private String bizMsgMenuId; - public void setStoreUniqId(String storeUniqId) { - this.storeUniqId = storeUniqId; - } + /*------------------ 电子发票 ------------------*/ + /** + * 授权成功的订单号,与失败订单号两者必显示其一 + */ + @XStreamAlias("SuccOrderId") + private String succOrderId; - public String getPoiId() { - return this.poiId; - } + /** + * 授权失败的订单号,与成功订单号两者必显示其一 + */ + @XStreamAlias("FailOrderId") + private String failOrderId; - public void setPoiId(String poiId) { - this.poiId = poiId; - } + /** + * 获取授权页链接的AppId + */ + @XStreamAlias("AuthorizeAppId") + private String authorizeAppId; - public String getResult() { - return this.result; - } + /** + * 授权来源,web:公众号开票,app:app开票,wxa:小程序开票,wap:h5开票 + */ + @XStreamAlias("source") + private String source; - public void setResult(String result) { - this.result = result; - } + /** + * 发票请求流水号,唯一识别发票请求的流水号 + */ + @XStreamAlias("fpqqlsh") + private String fpqqlsh; - public String getMsg() { - return this.msg; - } + /** + * 纳税人识别码 + */ + @XStreamAlias("nsrsbh") + private String nsrsbh; - public void setMsg(String msg) { - this.msg = msg; - } - public String getToUser() { - return this.toUser; + public static WxMpXmlMessage fromXml(String xml) { + //修改微信变态的消息内容格式,方便解析 + xml = xml.replace("", ""); + final WxMpXmlMessage xmlMessage = XStreamTransformer.fromXml(WxMpXmlMessage.class, xml); + xmlMessage.setAllFieldsMap(XmlUtils.xml2Map(xml)); + return xmlMessage; } - public void setToUser(String toUser) { - this.toUser = toUser; + public static WxMpXmlMessage fromXml(InputStream is) { + return XStreamTransformer.fromXml(WxMpXmlMessage.class, is); } - public Long getCreateTime() { - return this.createTime; + /** + * 从加密字符串转换. + * + * @param encryptedXml 密文 + * @param wxMpConfigStorage 配置存储器对象 + * @param timestamp 时间戳 + * @param nonce 随机串 + * @param msgSignature 签名串 + */ + public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, WxMpConfigStorage wxMpConfigStorage, + String timestamp, String nonce, String msgSignature) { + WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); + String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); + log.debug("解密后的原始xml消息内容:{}", plainText); + return fromXml(plainText); } - public void setCreateTime(Long createTime) { - this.createTime = createTime; + public static WxMpXmlMessage fromEncryptedXml(InputStream is, WxMpConfigStorage wxMpConfigStorage, String timestamp, + String nonce, String msgSignature) { + try { + return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxMpConfigStorage, timestamp, nonce, msgSignature); + } catch (IOException e) { + throw new RuntimeException(e); + } } /** *
        * 当接受用户消息时,可能会获得以下值:
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VOICE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LOCATION}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LINK}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT}
    +   * {@link WxConsts.XmlMsgType#TEXT}
    +   * {@link WxConsts.XmlMsgType#IMAGE}
    +   * {@link WxConsts.XmlMsgType#VOICE}
    +   * {@link WxConsts.XmlMsgType#VIDEO}
    +   * {@link WxConsts.XmlMsgType#LOCATION}
    +   * {@link WxConsts.XmlMsgType#LINK}
    +   * {@link WxConsts.XmlMsgType#EVENT}
        * 
    */ public String getMsgType() { @@ -480,525 +739,21 @@ public String getMsgType() { /** *
        * 当发送消息的时候使用:
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_TEXT}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_IMAGE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VOICE}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_VIDEO}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_NEWS}
    -   * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_MUSIC}
    +   * {@link WxConsts.XmlMsgType#TEXT}
    +   * {@link WxConsts.XmlMsgType#IMAGE}
    +   * {@link WxConsts.XmlMsgType#VOICE}
    +   * {@link WxConsts.XmlMsgType#VIDEO}
    +   * {@link WxConsts.XmlMsgType#NEWS}
    +   * {@link WxConsts.XmlMsgType#MUSIC}
        * 
    - * - * @param msgType */ public void setMsgType(String msgType) { this.msgType = msgType; } - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public Long getMsgId() { - return this.msgId; - } - - public void setMsgId(Long msgId) { - this.msgId = msgId; - } - - public String getPicUrl() { - return this.picUrl; - } - - public void setPicUrl(String picUrl) { - this.picUrl = picUrl; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public String getFormat() { - return this.format; - } - - public void setFormat(String format) { - this.format = format; - } - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public Double getLocationX() { - return this.locationX; - } - - public void setLocationX(Double locationX) { - this.locationX = locationX; - } - - public Double getLocationY() { - return this.locationY; - } - - public void setLocationY(Double locationY) { - this.locationY = locationY; - } - - public Double getScale() { - return this.scale; - } - - public void setScale(Double scale) { - this.scale = scale; - } - - public String getLabel() { - return this.label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getEvent() { - return this.event; - } - - public void setEvent(String event) { - this.event = event; - } - - public String getEventKey() { - return this.eventKey; - } - - public void setEventKey(String eventKey) { - this.eventKey = eventKey; - } - - public String getTicket() { - return this.ticket; - } - - public void setTicket(String ticket) { - this.ticket = ticket; - } - - public Double getLatitude() { - return this.latitude; - } - - public void setLatitude(Double latitude) { - this.latitude = latitude; - } - - public Double getLongitude() { - return this.longitude; - } - - public void setLongitude(Double longitude) { - this.longitude = longitude; - } - - public Double getPrecision() { - return this.precision; - } - - public void setPrecision(Double precision) { - this.precision = precision; - } - - public String getRecognition() { - return this.recognition; - } - - public void setRecognition(String recognition) { - this.recognition = recognition; - } - - public String getFromUser() { - return this.fromUser; - } - - public void setFromUser(String fromUser) { - this.fromUser = fromUser; - } - - public String getStatus() { - return this.status; - } - - public void setStatus(String status) { - this.status = status; - } - - public Integer getTotalCount() { - return this.totalCount; - } - - public void setTotalCount(Integer totalCount) { - this.totalCount = totalCount; - } - - public Integer getFilterCount() { - return this.filterCount; - } - - public void setFilterCount(Integer filterCount) { - this.filterCount = filterCount; - } - - public Integer getSentCount() { - return this.sentCount; - } - - public void setSentCount(Integer sentCount) { - this.sentCount = sentCount; - } - - public Integer getErrorCount() { - return this.errorCount; - } - - public void setErrorCount(Integer errorCount) { - this.errorCount = errorCount; - } - - public String getCardId() { - return this.cardId; - } - - public void setCardId(String cardId) { - this.cardId = cardId; - } - - public String getFriendUserName() { - return this.friendUserName; - } - - public void setFriendUserName(String friendUserName) { - this.friendUserName = friendUserName; - } - - public Integer getIsGiveByFriend() { - return this.isGiveByFriend; - } - - public void setIsGiveByFriend(Integer isGiveByFriend) { - this.isGiveByFriend = isGiveByFriend; - } - - public String getUserCardCode() { - return this.userCardCode; - } - - public void setUserCardCode(String userCardCode) { - this.userCardCode = userCardCode; - } - - public String getOldUserCardCode() { - return this.oldUserCardCode; - } - - public void setOldUserCardCode(String oldUserCardCode) { - this.oldUserCardCode = oldUserCardCode; - } - - public Integer getOuterId() { - return this.outerId; - } - - public void setOuterId(Integer outerId) { - this.outerId = outerId; - } - - public WxMpXmlMessage.ScanCodeInfo getScanCodeInfo() { - return this.scanCodeInfo; - } - - public void setScanCodeInfo(WxMpXmlMessage.ScanCodeInfo scanCodeInfo) { - this.scanCodeInfo = scanCodeInfo; - } - - public WxMpXmlMessage.SendPicsInfo getSendPicsInfo() { - return this.sendPicsInfo; - } - - public void setSendPicsInfo(WxMpXmlMessage.SendPicsInfo sendPicsInfo) { - this.sendPicsInfo = sendPicsInfo; - } - - public WxMpXmlMessage.SendLocationInfo getSendLocationInfo() { - return this.sendLocationInfo; - } - - public void setSendLocationInfo( - WxMpXmlMessage.SendLocationInfo sendLocationInfo) { - this.sendLocationInfo = sendLocationInfo; - } - - public Long getMenuId() { - return this.menuId; - } - - public void setMenuId(Long menuId) { - this.menuId = menuId; - } - - public String getKfAccount() { - return this.kfAccount; - } - - public void setKfAccount(String kfAccount) { - this.kfAccount = kfAccount; - } - - public String getToKfAccount() { - return this.toKfAccount; - } - - public void setToKfAccount(String toKfAccount) { - this.toKfAccount = toKfAccount; - } - - public String getFromKfAccount() { - return this.fromKfAccount; - } - - public void setFromKfAccount(String fromKfAccount) { - this.fromKfAccount = fromKfAccount; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - @XStreamAlias("HardWare") - public static class HardWare { - /** - * 消息展示,目前支持myrank(排行榜) - */ - @XStreamAlias("MessageView") - @XStreamConverter(value = XStreamCDataConverter.class) - private String messageView; - /** - * 消息点击动作,目前支持ranklist(点击跳转排行榜) - */ - @XStreamAlias("MessageAction") - @XStreamConverter(value = XStreamCDataConverter.class) - private String messageAction; - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getMessageView() { - return messageView; - } - - public void setMessageView(String messageView) { - this.messageView = messageView; - } - - public String getMessageAction() { - return messageAction; - } - - public void setMessageAction(String messageAction) { - this.messageAction = messageAction; - } + return WxMpGsonBuilder.create().toJson(this); } - @XStreamAlias("ScanCodeInfo") - public static class ScanCodeInfo { - @XStreamAlias("ScanType") - @XStreamConverter(value = XStreamCDataConverter.class) - private String scanType; - @XStreamAlias("ScanResult") - @XStreamConverter(value = XStreamCDataConverter.class) - private String scanResult; - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - /** - * 扫描类型,一般是qrcode - */ - public String getScanType() { - - return this.scanType; - } - - public void setScanType(String scanType) { - this.scanType = scanType; - } - - /** - * 扫描结果,即二维码对应的字符串信息 - */ - public String getScanResult() { - return this.scanResult; - } - - public void setScanResult(String scanResult) { - this.scanResult = scanResult; - } - - } - - @XStreamAlias("SendPicsInfo") - public static class SendPicsInfo { - @XStreamAlias("PicList") - protected final List picList = new ArrayList<>(); - @XStreamAlias("Count") - private Long count; - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public Long getCount() { - return this.count; - } - - public void setCount(Long count) { - this.count = count; - } - - public List getPicList() { - return this.picList; - } - - @XStreamAlias("item") - public static class Item { - @XStreamAlias("PicMd5Sum") - @XStreamConverter(value = XStreamCDataConverter.class) - private String picMd5Sum; - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getPicMd5Sum() { - return this.picMd5Sum; - } - - public void setPicMd5Sum(String picMd5Sum) { - this.picMd5Sum = picMd5Sum; - } - } - } - - @XStreamAlias("SendLocationInfo") - public static class SendLocationInfo { - - @XStreamAlias("Location_X") - @XStreamConverter(value = XStreamCDataConverter.class) - private String locationX; - - @XStreamAlias("Location_Y") - @XStreamConverter(value = XStreamCDataConverter.class) - private String locationY; - - @XStreamAlias("Scale") - @XStreamConverter(value = XStreamCDataConverter.class) - private String scale; - - @XStreamAlias("Label") - @XStreamConverter(value = XStreamCDataConverter.class) - private String label; - - @XStreamAlias("Poiname") - @XStreamConverter(value = XStreamCDataConverter.class) - private String poiname; - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getLocationX() { - return this.locationX; - } - - public void setLocationX(String locationX) { - this.locationX = locationX; - } - - public String getLocationY() { - return this.locationY; - } - - public void setLocationY(String locationY) { - this.locationY = locationY; - } - - public String getScale() { - return this.scale; - } - - public void setScale(String scale) { - this.scale = scale; - } - - public String getLabel() { - return this.label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getPoiname() { - return this.poiname; - } - - public void setPoiname(String poiname) { - this.poiname = poiname; - } - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutDeviceMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutDeviceMessage.java new file mode 100644 index 0000000000..b32935a106 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutDeviceMessage.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.mp.bean.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; + +@XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) +public class WxMpXmlOutDeviceMessage extends WxMpXmlOutMessage { + private static final long serialVersionUID = -3093843149649157587L; + + @XStreamAlias("DeviceType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String deviceType; + + @XStreamAlias("DeviceID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String deviceId; + + @XStreamAlias("Content") + @XStreamConverter(value = XStreamCDataConverter.class) + private String content; + + @XStreamAlias("SessionID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String sessionId; + + public WxMpXmlOutDeviceMessage() { + this.msgType = WxConsts.XmlMsgType.DEVICE_TEXT; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java index 62852ca8f1..dbb0ab90f9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java @@ -2,30 +2,23 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxMpXmlOutImageMessage extends WxMpXmlOutMessage { - - /** - * - */ private static final long serialVersionUID = -2684778597067990723L; + @XStreamAlias("Image") @XStreamConverter(value = XStreamMediaIdConverter.class) private String mediaId; - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - public WxMpXmlOutImageMessage() { - this.msgType = WxConsts.XML_MSG_IMAGE; + this.msgType = WxConsts.XmlMsgType.IMAGE; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java index c0b53d46a3..40e0b41a2b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java @@ -2,8 +2,9 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.builder.outxml.*; import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; import me.chanjar.weixin.mp.util.xml.XStreamTransformer; @@ -11,71 +12,25 @@ import java.io.Serializable; @XStreamAlias("xml") +@Data public abstract class WxMpXmlOutMessage implements Serializable { - private static final long serialVersionUID = -381382011286216263L; @XStreamAlias("ToUserName") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) protected String toUserName; @XStreamAlias("FromUserName") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) protected String fromUserName; @XStreamAlias("CreateTime") protected Long createTime; @XStreamAlias("MsgType") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) protected String msgType; - public String getToUserName() { - return this.toUserName; - } - - public void setToUserName(String toUserName) { - this.toUserName = toUserName; - } - - public String getFromUserName() { - return this.fromUserName; - } - - public void setFromUserName(String fromUserName) { - this.fromUserName = fromUserName; - } - - public Long getCreateTime() { - return this.createTime; - } - - public void setCreateTime(Long createTime) { - this.createTime = createTime; - } - - public String getMsgType() { - return this.msgType; - } - - public void setMsgType(String msgType) { - this.msgType = msgType; - } - - @SuppressWarnings("unchecked") - public String toXml() { - return XStreamTransformer.toXml((Class) this.getClass(), this); - } - - /** - * 转换成加密的xml格式 - */ - public String toEncryptedXml(WxMpConfigStorage wxMpConfigStorage) { - String plainXml = toXml(); - WxMpCryptUtil pc = new WxMpCryptUtil(wxMpConfigStorage); - return pc.encrypt(plainXml); - } - /** * 获得文本消息builder */ @@ -124,4 +79,25 @@ public static NewsBuilder NEWS() { public static TransferCustomerServiceBuilder TRANSFER_CUSTOMER_SERVICE() { return new TransferCustomerServiceBuilder(); } + + /** + * 获得设备消息builder + */ + public static DeviceBuilder DEVICE() { + return new DeviceBuilder(); + } + + @SuppressWarnings("unchecked") + public String toXml() { + return XStreamTransformer.toXml((Class) this.getClass(), this); + } + + /** + * 转换成加密的xml格式 + */ + public String toEncryptedXml(WxMpConfigStorage wxMpConfigStorage) { + String plainXml = toXml(); + WxMpCryptUtil pc = new WxMpCryptUtil(wxMpConfigStorage); + return pc.encrypt(plainXml); + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java index a5b48619a5..1124f45857 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java @@ -2,126 +2,90 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import java.io.Serializable; + @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage { - - /** - * - */ private static final long serialVersionUID = -4159937804975448945L; + @XStreamAlias("Music") protected final Music music = new Music(); public WxMpXmlOutMusicMessage() { - this.msgType = WxConsts.XML_MSG_MUSIC; + this.msgType = WxConsts.XmlMsgType.MUSIC; + } + + @XStreamAlias("Music") + @Data + public static class Music implements Serializable { + private static final long serialVersionUID = -5492592401691895334L; + + @XStreamAlias("Title") + @XStreamConverter(value = XStreamCDataConverter.class) + private String title; + + @XStreamAlias("Description") + @XStreamConverter(value = XStreamCDataConverter.class) + private String description; + + @XStreamAlias("ThumbMediaId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String thumbMediaId; + + @XStreamAlias("MusicUrl") + @XStreamConverter(value = XStreamCDataConverter.class) + private String musicUrl; + + @XStreamAlias("HQMusicUrl") + @XStreamConverter(value = XStreamCDataConverter.class) + private String hqMusicUrl; } public String getTitle() { - return this.music.getTitle(); + return this.music.title; } public void setTitle(String title) { - this.music.setTitle(title); + this.music.title = title; } public String getDescription() { - return this.music.getDescription(); + return this.music.description; } public void setDescription(String description) { - this.music.setDescription(description); + this.music.description = description; } public String getThumbMediaId() { - return this.music.getThumbMediaId(); + return this.music.thumbMediaId; } public void setThumbMediaId(String thumbMediaId) { - this.music.setThumbMediaId(thumbMediaId); + this.music.thumbMediaId = thumbMediaId; } public String getMusicUrl() { - return this.music.getMusicUrl(); + return this.music.musicUrl; } public void setMusicUrl(String musicUrl) { - this.music.setMusicUrl(musicUrl); + this.music.musicUrl = musicUrl; } public String getHqMusicUrl() { - return this.music.getHqMusicUrl(); + return this.music.hqMusicUrl; } public void setHqMusicUrl(String hqMusicUrl) { - this.music.setHqMusicUrl(hqMusicUrl); - } - - @XStreamAlias("Music") - public static class Music { - - @XStreamAlias("Title") - @XStreamConverter(value=XStreamCDataConverter.class) - private String title; - - @XStreamAlias("Description") - @XStreamConverter(value=XStreamCDataConverter.class) - private String description; - - @XStreamAlias("ThumbMediaId") - @XStreamConverter(value=XStreamCDataConverter.class) - private String thumbMediaId; - - @XStreamAlias("MusicUrl") - @XStreamConverter(value=XStreamCDataConverter.class) - private String musicUrl; - - @XStreamAlias("HQMusicUrl") - @XStreamConverter(value=XStreamCDataConverter.class) - private String hqMusicUrl; - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getThumbMediaId() { - return this.thumbMediaId; - } - - public void setThumbMediaId(String thumbMediaId) { - this.thumbMediaId = thumbMediaId; - } - - public String getMusicUrl() { - return this.musicUrl; - } - - public void setMusicUrl(String musicUrl) { - this.musicUrl = musicUrl; - } - - public String getHqMusicUrl() { - return this.hqMusicUrl; - } - - public void setHqMusicUrl(String hqMusicUrl) { - this.hqMusicUrl = hqMusicUrl; - } - + this.music.hqMusicUrl = hqMusicUrl; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java index 413a6d7c09..00f8d70c88 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java @@ -1,33 +1,41 @@ package me.chanjar.weixin.mp.bean.message; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import java.util.ArrayList; -import java.util.List; - +/** + * 被动回复的图文消息xml. + * @author chanjarster + */ @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { + private static final long serialVersionUID = -4604402850905714772L; /** - * + * 图文消息信息. + * 注意,如果图文数超过限制,则将只发限制内的条数 */ - private static final long serialVersionUID = -4604402850905714772L; - - @XStreamAlias("ArticleCount") - protected int articleCount; - @XStreamAlias("Articles") protected final List articles = new ArrayList<>(); + /** + * 图文消息个数. + * 当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息 + */ + @XStreamAlias("ArticleCount") + protected int articleCount; public WxMpXmlOutNewsMessage() { - this.msgType = WxConsts.XML_MSG_NEWS; - } - - public int getArticleCount() { - return this.articleCount; + this.msgType = WxConsts.XmlMsgType.NEWS; } public void addArticle(Item item) { @@ -35,63 +43,40 @@ public void addArticle(Item item) { this.articleCount = this.articles.size(); } - public List getArticles() { - return this.articles; - } - - @XStreamAlias("item") - public static class Item { + @Data + public static class Item implements Serializable { + private static final long serialVersionUID = -4971456355028904754L; + /** + * 图文消息标题. + */ @XStreamAlias("Title") - @XStreamConverter(value=XStreamCDataConverter.class) - private String Title; + @XStreamConverter(value = XStreamCDataConverter.class) + private String title; + /** + * 图文消息描述. + */ @XStreamAlias("Description") - @XStreamConverter(value=XStreamCDataConverter.class) - private String Description; + @XStreamConverter(value = XStreamCDataConverter.class) + private String description; + /** + * 图片链接. + * 支持JPG、PNG格式,较好的效果为大图360*200,小图200*200 + */ @XStreamAlias("PicUrl") - @XStreamConverter(value=XStreamCDataConverter.class) - private String PicUrl; + @XStreamConverter(value = XStreamCDataConverter.class) + private String picUrl; + /** + * 点击图文消息跳转链接. + */ @XStreamAlias("Url") - @XStreamConverter(value=XStreamCDataConverter.class) - private String Url; - - public String getTitle() { - return this.Title; - } - - public void setTitle(String title) { - this.Title = title; - } - - public String getDescription() { - return this.Description; - } - - public void setDescription(String description) { - this.Description = description; - } - - public String getPicUrl() { - return this.PicUrl; - } - - public void setPicUrl(String picUrl) { - this.PicUrl = picUrl; - } - - public String getUrl() { - return this.Url; - } - - public void setUrl(String url) { - this.Url = url; - } + @XStreamConverter(value = XStreamCDataConverter.class) + private String url; } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java index 0719b1edef..cbaa05abc3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java @@ -2,31 +2,23 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage { - - /** - * - */ private static final long serialVersionUID = -3972786455288763361L; + @XStreamAlias("Content") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) private String content; public WxMpXmlOutTextMessage() { - this.msgType = WxConsts.XML_MSG_TEXT; - } - - public String getContent() { - return this.content; + this.msgType = WxConsts.XmlMsgType.TEXT; } - public void setContent(String content) { - this.content = content; - } - - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java index b0eece7043..5b0857830e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java @@ -2,10 +2,16 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import java.io.Serializable; + @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxMpXmlOutTransferKefuMessage extends WxMpXmlOutMessage { private static final long serialVersionUID = 1850903037285841322L; @@ -13,30 +19,17 @@ public class WxMpXmlOutTransferKefuMessage extends WxMpXmlOutMessage { protected TransInfo transInfo; public WxMpXmlOutTransferKefuMessage() { - this.msgType = WxConsts.CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE; - } - - public TransInfo getTransInfo() { - return this.transInfo; - } - - public void setTransInfo(TransInfo transInfo) { - this.transInfo = transInfo; + this.msgType = WxConsts.KefuMsgType.TRANSFER_CUSTOMER_SERVICE; } @XStreamAlias("TransInfo") - public static class TransInfo { + @Data + public static class TransInfo implements Serializable { + private static final long serialVersionUID = -6317885617135706056L; @XStreamAlias("KfAccount") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) private String kfAccount; - public String getKfAccount() { - return this.kfAccount; - } - - public void setKfAccount(String kfAccount) { - this.kfAccount = kfAccount; - } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java index 3c042f6d28..7f43a56809 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java @@ -2,87 +2,66 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import java.io.Serializable; + @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage { - - /** - * - */ private static final long serialVersionUID = 1745902309380113978L; + @XStreamAlias("Video") protected final Video video = new Video(); public WxMpXmlOutVideoMessage() { - this.msgType = WxConsts.XML_MSG_VIDEO; - } - - public String getMediaId() { - return this.video.getMediaId(); - } - - public void setMediaId(String mediaId) { - this.video.setMediaId(mediaId); - } - - public String getTitle() { - return this.video.getTitle(); - } - - public void setTitle(String title) { - this.video.setTitle(title); - } - - public String getDescription() { - return this.video.getDescription(); + this.msgType = WxConsts.XmlMsgType.VIDEO; } - public void setDescription(String description) { - this.video.setDescription(description); - } - - @XStreamAlias("Video") - public static class Video { + @Data + public static class Video implements Serializable { + private static final long serialVersionUID = -6445448977569651183L; @XStreamAlias("MediaId") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) private String mediaId; @XStreamAlias("Title") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) private String title; @XStreamAlias("Description") - @XStreamConverter(value=XStreamCDataConverter.class) + @XStreamConverter(value = XStreamCDataConverter.class) private String description; - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } + } - public String getTitle() { - return this.title; - } + public String getMediaId() { + return this.video.mediaId; + } - public void setTitle(String title) { - this.title = title; - } + public void setMediaId(String mediaId) { + this.video.mediaId = mediaId; + } - public String getDescription() { - return this.description; - } + public String getTitle() { + return this.video.title; + } - public void setDescription(String description) { - this.description = description; - } + public void setTitle(String title) { + this.video.title = title; + } + public String getDescription() { + return this.video.description; } + public void setDescription(String description) { + this.video.description = description; + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java index ec354ddfe1..bd91b861ee 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java @@ -2,30 +2,23 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; @XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = true) public class WxMpXmlOutVoiceMessage extends WxMpXmlOutMessage { - - /** - * - */ private static final long serialVersionUID = 240367390249860551L; + @XStreamAlias("Voice") @XStreamConverter(value = XStreamMediaIdConverter.class) private String mediaId; public WxMpXmlOutVoiceMessage() { - this.msgType = WxConsts.XML_MSG_VOICE; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; + this.msgType = WxConsts.XmlMsgType.VOICE; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java deleted file mode 100644 index b39d2fed29..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.chanjar.weixin.mp.bean.result; - -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.mp.bean.WxMpCard; - -import java.io.Serializable; - -/** - * 卡券查询Code,核销Code接口返回结果 - * - * @author YuJian - * @version 15/11/11 - */ -public class WxMpCardResult implements Serializable { - - /** - * - */ - private static final long serialVersionUID = -7950878428289035637L; - - private String errorCode; - - private String errorMsg; - - private String openId; - - private WxMpCard card; - - private String userCardStatus; - - private Boolean canConsume; - - public String getErrorCode() { - return this.errorCode; - } - - public void setErrorCode(String errorCode) { - this.errorCode = errorCode; - } - - public String getErrorMsg() { - return this.errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } - - public String getOpenId() { - return this.openId; - } - - public void setOpenId(String openId) { - this.openId = openId; - } - - public WxMpCard getCard() { - return this.card; - } - - public void setCard(WxMpCard card) { - this.card = card; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getUserCardStatus() { - return this.userCardStatus; - } - - public void setUserCardStatus(String userCardStatus) { - this.userCardStatus = userCardStatus; - } - - public Boolean getCanConsume() { - return this.canConsume; - } - - public void setCanConsume(Boolean canConsume) { - this.canConsume = canConsume; - } - -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpChangeOpenid.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpChangeOpenid.java new file mode 100644 index 0000000000..203aa97f5b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpChangeOpenid.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.result; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.List; + +/** + * 主体变更迁移用户 openid 返回. + * + * @author 007gzs + */ +@Data +public class WxMpChangeOpenid implements Serializable { + private static final long serialVersionUID = -8132023284876534743L; + private String oriOpenid; + private String newOpenid; + private String errMsg; + public static WxMpChangeOpenid fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpChangeOpenid.class); + } + public static List fromJsonList(String json) { + Type collectionType = new TypeToken>() { + }.getType(); + Gson gson = WxMpGsonBuilder.create(); + JsonObject jsonObject = gson.fromJson(json, JsonObject.class); + return gson.fromJson(jsonObject.get("result_list"), collectionType); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfo.java new file mode 100644 index 0000000000..7473bb1323 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfo.java @@ -0,0 +1,175 @@ +package me.chanjar.weixin.mp.bean.result; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxBooleanTypeAdapter; +import me.chanjar.weixin.common.util.json.WxDateTypeAdapter; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + *
    + * 公众号的自动回复规则.
    + * 参考文档地址:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Getting_Rules_for_Auto_Replies.html
    + * Created by Binary Wang on 2017-7-8.
    + * 
    + * + * @author Binary Wang + */ +@Data +public class WxMpCurrentAutoReplyInfo implements Serializable { + private static final long serialVersionUID = 8294705001262751638L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static WxMpCurrentAutoReplyInfo fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCurrentAutoReplyInfo.class); + } + + @SerializedName("is_add_friend_reply_open") + @JsonAdapter(WxBooleanTypeAdapter.class) + private Boolean isAddFriendReplyOpen; + + @SerializedName("is_autoreply_open") + @JsonAdapter(WxBooleanTypeAdapter.class) + private Boolean isAutoReplyOpen; + + @SerializedName("add_friend_autoreply_info") + private AutoReplyInfo addFriendAutoReplyInfo; + + @SerializedName("message_default_autoreply_info") + private AutoReplyInfo messageDefaultAutoReplyInfo; + + @SerializedName("keyword_autoreply_info") + private KeywordAutoReplyInfo keywordAutoReplyInfo; + + @Data + public static class AutoReplyRule implements Serializable { + private static final long serialVersionUID = -6415971838145909046L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + @SerializedName("rule_name") + private String ruleName; + + @SerializedName("create_time") + @JsonAdapter(WxDateTypeAdapter.class) + private Date createTime; + + @SerializedName("reply_mode") + private String replyMode; + + @SerializedName("keyword_list_info") + private List keywordListInfo; + + @SerializedName("reply_list_info") + private List replyListInfo; + + } + + @Data + public static class ReplyInfo implements Serializable { + private static final long serialVersionUID = -3429575601599101690L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + private String type; + private String content; + + @SerializedName("news_info") + private NewsInfo newsInfo; + + } + + @Data + public static class NewsInfo implements Serializable { + private static final long serialVersionUID = 2958827725972593328L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + private List list; + + } + + @Data + public static class NewsItem implements Serializable { + private static final long serialVersionUID = -680356309029767176L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + @SerializedName("cover_url") + private String coverUrl; + private String author; + @SerializedName("content_url") + private String contentUrl; + private String digest; + @SerializedName("show_cover") + @JsonAdapter(WxBooleanTypeAdapter.class) + private Boolean showCover; + @SerializedName("source_url") + private String sourceUrl; + private String title; + + } + + @Data + public static class KeywordInfo implements Serializable { + private static final long serialVersionUID = 7720246983986706379L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + private String type; + @SerializedName("match_mode") + private String matchMode; + private String content; + + } + + @Data + public static class KeywordAutoReplyInfo implements Serializable { + private static final long serialVersionUID = -8789197949404753083L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + private List list; + } + + @Data + public static class AutoReplyInfo implements Serializable { + private static final long serialVersionUID = 4993719555937843712L; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + private String type; + private String content; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassGetResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassGetResult.java new file mode 100644 index 0000000000..c1f43feb01 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassGetResult.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + *
    + * 查询群发消息发送状态【订阅号与服务号认证后均可用】
    + * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html#%E6%9F%A5%E8%AF%A2%E7%BE%A4%E5%8F%91%E6%B6%88%E6%81%AF%E5%8F%91%E9%80%81%E7%8A%B6%E6%80%81%E3%80%90%E8%AE%A2%E9%98%85%E5%8F%B7%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%8F%B7%E8%AE%A4%E8%AF%81%E5%90%8E%E5%9D%87%E5%8F%AF%E7%94%A8%E3%80%91
    + */
    +@Data
    +public class WxMpMassGetResult extends WxMpResult implements Serializable {
    +
    +  private static final long serialVersionUID = -2909694117357278557L;
    +
    +  @SerializedName("msg_id")
    +  private Long msgId;
    +
    +  @SerializedName("msg_status")
    +  private String msgstatus;
    +
    +  public static WxMpMassGetResult fromJson(String json) {
    +    return WxMpGsonBuilder.create().fromJson(json, WxMpMassGetResult.class);
    +  }
    +
    +  @Override
    +  public String toString() {
    +    return WxMpGsonBuilder.create().toJson(this);
    +  }
    +
    +}
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassSendResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassSendResult.java
    index ffb8f0267a..d18b713437 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassSendResult.java
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassSendResult.java
    @@ -1,10 +1,10 @@
     package me.chanjar.weixin.mp.bean.result;
     
    -import me.chanjar.weixin.common.util.ToStringUtils;
    -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
    -
     import java.io.Serializable;
     
    +import lombok.Data;
    +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
    +
     /**
      * 
      * 群发消息一发送就返回的结果
    @@ -13,55 +13,25 @@
      * http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口#.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81.E7.BE.A4.E5.8F.91.E7.BB.93.E6.9E.9C
      *
      * 
    - * @author chanjarster * + * @author chanjarster */ +@Data public class WxMpMassSendResult implements Serializable { private static final long serialVersionUID = -4816336807575562818L; + private String errorCode; private String errorMsg; private String msgId; private String msgDataId; - public String getErrorCode() { - return this.errorCode; - } - - public void setErrorCode(String errorCode) { - this.errorCode = errorCode; - } - - public String getErrorMsg() { - return this.errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } - - public String getMsgId() { - return this.msgId; - } - - public void setMsgId(String msgId) { - this.msgId = msgId; - } - - public String getMsgDataId() { - return this.msgDataId; - } - - public void setMsgDataId(String msgDataId) { - this.msgDataId = msgDataId; - } - public static WxMpMassSendResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMpMassSendResult.class); } @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassSpeedGetResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassSpeedGetResult.java new file mode 100644 index 0000000000..ef53b5bebb --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassSpeedGetResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.bean.result; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + *
    + * 获取群发速度
    + * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html#9
    + * speed	realspeed
    + * 0	80w/分钟
    + * 1	60w/分钟
    + * 2	45w/分钟
    + * 3	30w/分钟
    + * 4	10w/分钟
    + * 
    + */ +@Data +public class WxMpMassSpeedGetResult implements Serializable { + + private static final long serialVersionUID = -6478157575168068334L; + + private Integer speed; + + private Integer realspeed; + + public static WxMpMassSpeedGetResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpMassSpeedGetResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassUploadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassUploadResult.java index 6a4f88d804..03613dcbed 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassUploadResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassUploadResult.java @@ -1,58 +1,33 @@ package me.chanjar.weixin.mp.bean.result; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + /** *
    - * 上传群发用的素材的结果
    + * 上传群发用的素材的结果.
      * 视频和图文消息需要在群发前上传素材
      * 
    - * @author chanjarster * + * @author chanjarster */ +@Data public class WxMpMassUploadResult implements Serializable { - - /** - * - */ private static final long serialVersionUID = 6568157943644994029L; + private String type; private String mediaId; private long createdAt; - public String getType() { - return this.type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMediaId() { - return this.mediaId; - } - - public void setMediaId(String mediaId) { - this.mediaId = mediaId; - } - - public long getCreatedAt() { - return this.createdAt; - } - - public void setCreatedAt(long createdAt) { - this.createdAt = createdAt; - } - public static WxMpMassUploadResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMpMassUploadResult.class); } @Override public String toString() { - return "WxUploadResult [type=" + this.type + ", media_id=" + this.mediaId + ", created_at=" + this.createdAt + "]"; + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java index d38317ce8f..24b87d7a0d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java @@ -2,13 +2,14 @@ import java.io.Serializable; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +/** + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 + */ +@Data public class WxMpOAuth2AccessToken implements Serializable { - - /** - * - */ private static final long serialVersionUID = -1345910558078620805L; private String accessToken; @@ -21,69 +22,18 @@ public class WxMpOAuth2AccessToken implements Serializable { private String scope; + /** + * https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=11513156443eZYea&version=&lang=zh_CN. + * 本接口在scope参数为snsapi_base时不再提供unionID字段。 + */ private String unionId; - public String getRefreshToken() { - return this.refreshToken; - } - - public void setRefreshToken(String refreshToken) { - this.refreshToken = refreshToken; - } - - public String getOpenId() { - return this.openId; - } - - public void setOpenId(String openId) { - this.openId = openId; - } - - public String getScope() { - return this.scope; - } - - public void setScope(String scope) { - this.scope = scope; - } - - public String getAccessToken() { - return this.accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public int getExpiresIn() { - return this.expiresIn; - } - - public void setExpiresIn(int expiresIn) { - this.expiresIn = expiresIn; - } - - public String getUnionId() { - return this.unionId; - } - - public void setUnionId(String unionId) { - this.unionId = unionId; - } - public static WxMpOAuth2AccessToken fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMpOAuth2AccessToken.class); } @Override public String toString() { - return "WxMpOAuth2AccessToken{" + - "accessToken='" + this.accessToken + '\'' + - ", expiresTime=" + this.expiresIn + - ", refreshToken='" + this.refreshToken + '\'' + - ", openId='" + this.openId + '\'' + - ", scope='" + this.scope + '\'' + - ", unionId='" + this.unionId + '\'' + - '}'; + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpQrCodeTicket.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpQrCodeTicket.java index a2917ecc5a..cd2d19942a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpQrCodeTicket.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpQrCodeTicket.java @@ -1,57 +1,32 @@ package me.chanjar.weixin.mp.bean.result; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; /** * 换取二维码的Ticket - * + * * @author chanjarster */ +@Data public class WxMpQrCodeTicket implements Serializable { - - /** - * - */ private static final long serialVersionUID = 5777119669111011584L; - protected String ticket; - protected int expire_seconds = -1; - protected String url; - - public String getTicket() { - return this.ticket; - } - - public void setTicket(String ticket) { - this.ticket = ticket; - } + protected String ticket; /** - * 如果返回-1说明是永久 + * 如果为-1,说明是永久 */ - public int getExpire_seconds() { - return this.expire_seconds; - } - - public void setExpire_seconds(int expire_seconds) { - this.expire_seconds = expire_seconds; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } + protected int expireSeconds = -1; + protected String url; public static WxMpQrCodeTicket fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpQrCodeTicket.class); + return WxMpGsonBuilder.create().fromJson(json, WxMpQrCodeTicket.class); } @Override public String toString() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpResult.java new file mode 100644 index 0000000000..9be4b7e8cb --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpResult.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.mp.bean.result; + +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * 基础的微信公众号平台请求结果. + */ +@Data +public class WxMpResult implements Serializable { + private static final long serialVersionUID = 2101652152604850904L; + protected String errcode; + protected String errmsg; + + /** + * 请求是否成功. + */ + public boolean isSuccess() { + return StringUtils.equalsIgnoreCase(errcode, "0"); + } + + public static WxMpResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMpResult.class); + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpSemanticQueryResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpSemanticQueryResult.java index c8a01d3efe..5c2962b82a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpSemanticQueryResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpSemanticQueryResult.java @@ -1,22 +1,21 @@ package me.chanjar.weixin.mp.bean.result; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; /** * 语义理解查询结果对象 - * + *

    * http://mp.weixin.qq.com/wiki/index.php?title=语义理解 * * @author Daniel Qian */ +@Data public class WxMpSemanticQueryResult implements Serializable { - - /** - * - */ private static final long serialVersionUID = 4811088544804441365L; + private String query; private String type; private String semantic; @@ -24,54 +23,6 @@ public class WxMpSemanticQueryResult implements Serializable { private String answer; private String text; - public String getQuery() { - return this.query; - } - - public void setQuery(String query) { - this.query = query; - } - - public String getType() { - return this.type; - } - - public void setType(String type) { - this.type = type; - } - - public String getSemantic() { - return this.semantic; - } - - public void setSemantic(String semantic) { - this.semantic = semantic; - } - - public String getResult() { - return this.result; - } - - public void setResult(String result) { - this.result = result; - } - - public String getAnswer() { - return this.answer; - } - - public void setAnswer(String answer) { - this.answer = answer; - } - - public String getText() { - return this.text; - } - - public void setText(String text) { - this.text = text; - } - public static WxMpSemanticQueryResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMpSemanticQueryResult.class); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java index 7131de5cd4..4d22fab9e9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java @@ -1,178 +1,92 @@ package me.chanjar.weixin.mp.bean.result; +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.List; + import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.io.Serializable; -import java.lang.reflect.Type; -import java.util.List; - /** - * 微信用户信息 - * @author chanjarster + * 微信用户信息. * + * @author chanjarster */ +@Data public class WxMpUser implements Serializable { - private static final long serialVersionUID = 5788154322646488738L; + private Boolean subscribe; private String openId; private String nickname; - private String sex; + /** + * 性别描述信息:男、女、未知等. + */ + private String sexDesc; + /** + * 性别表示:1,2等数字. + */ + private Integer sex; private String language; private String city; private String province; private String country; private String headImgUrl; private Long subscribeTime; + /** + * https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=11513156443eZYea&version=&lang=zh_CN + *

    +   * 只有在将公众号绑定到微信开放平台帐号后,才会出现该字段。
    +   * 另外,在用户未关注公众号时,将不返回用户unionID信息。
    +   * 已关注的用户,开发者可使用“获取用户基本信息接口”获取unionID;
    +   * 未关注用户,开发者可使用“微信授权登录接口”并将scope参数设置为snsapi_userinfo,获取用户unionID
    +   * 
    + */ private String unionId; - private Integer sexId; private String remark; private Integer groupId; private Long[] tagIds; - public Boolean getSubscribe() { - return this.subscribe; - } - - public void setSubscribe(Boolean subscribe) { - this.subscribe = subscribe; - } - - public String getOpenId() { - return this.openId; - } - - public void setOpenId(String openId) { - this.openId = openId; - } - - public String getNickname() { - return this.nickname; - } - - public void setNickname(String nickname) { - this.nickname = nickname; - } - - public String getSex() { - return this.sex; - } - - public void setSex(String sex) { - this.sex = sex; - } - - public String getLanguage() { - return this.language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public String getCity() { - return this.city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getProvince() { - return this.province; - } - - public void setProvince(String province) { - this.province = province; - } - - public String getCountry() { - return this.country; - } - - public void setCountry(String country) { - this.country = country; - } - - public String getHeadImgUrl() { - return this.headImgUrl; - } - - public void setHeadImgUrl(String headImgUrl) { - this.headImgUrl = headImgUrl; - } - - public Long getSubscribeTime() { - return this.subscribeTime; - } - - public void setSubscribeTime(Long subscribeTime) { - this.subscribeTime = subscribeTime; - } - /** - *只有在将公众号绑定到微信开放平台帐号后,才会出现该字段。 + * 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom). */ - public String getUnionId() { - return this.unionId; - } - - public void setUnionId(String unionId) { - this.unionId = unionId; - } - - public Integer getSexId() { - - return this.sexId; - } - - public void setSexId(Integer sexId) { - this.sexId = sexId; - } - - public String getRemark() { - return this.remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } + private String[] privileges; - public Integer getGroupId() { - return this.groupId; - } + /** + * subscribe_scene 返回用户关注的渠道来源. + * ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENEPROFILE LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_OTHERS 其他 + */ + private String subscribeScene; - public void setGroupId(Integer groupId) { - this.groupId = groupId; - } + /** + * qr_scene 二维码扫码场景(开发者自定义). + */ + private String qrScene; - public Long[] getTagIds() { - return this.tagIds; - } + /** + * qr_scene_str 二维码扫码场景描述(开发者自定义). + */ + private String qrSceneStr; - public void setTagIds(Long[] tagIds) { - this.tagIds = tagIds; - } public static WxMpUser fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUser.class); + return WxMpGsonBuilder.create().fromJson(json, WxMpUser.class); } public static List fromJsonList(String json) { Type collectionType = new TypeToken>() { }.getType(); - Gson gson = WxMpGsonBuilder.INSTANCE.create(); + Gson gson = WxMpGsonBuilder.create(); JsonObject jsonObject = gson.fromJson(json, JsonObject.class); return gson.fromJson(jsonObject.get("user_info_list"), collectionType); } @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserBlacklistGetResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserBlacklistGetResult.java index 3cfbc00915..40e2a7167e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserBlacklistGetResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserBlacklistGetResult.java @@ -1,57 +1,30 @@ package me.chanjar.weixin.mp.bean.result; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * @author miller */ -public class WxMpUserBlacklistGetResult { +@Data +public class WxMpUserBlacklistGetResult implements Serializable { + private static final long serialVersionUID = -8780216463588687626L; + protected int total = -1; protected int count = -1; protected List openidList = new ArrayList<>(); protected String nextOpenid; public static WxMpUserBlacklistGetResult fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUserBlacklistGetResult.class); - } - - public int getTotal() { - return this.total; - } - - public void setTotal(int total) { - this.total = total; - } - - public int getCount() { - return this.count; - } - - public void setCount(int count) { - this.count = count; - } - - public List getOpenidList() { - return this.openidList; - } - - public void setOpenidList(List openidList) { - this.openidList = openidList; - } - - public String getNextOpenid() { - return this.nextOpenid; - } - - public void setNextOpenid(String nextOpenid) { - this.nextOpenid = nextOpenid; + return WxMpGsonBuilder.create().fromJson(json, WxMpUserBlacklistGetResult.class); } @Override public String toString() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java index 87ce18cddb..e6c8d6541a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java @@ -1,52 +1,32 @@ package me.chanjar.weixin.mp.bean.result; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * 关注者列表 - * @author chanjarster * + * @author chanjarster */ -public class WxMpUserList { +@Data +public class WxMpUserList implements Serializable { + private static final long serialVersionUID = 1389073042674901032L; protected long total = -1; protected int count = -1; protected List openids = new ArrayList<>(); protected String nextOpenid; - public long getTotal() { - return this.total; - } - public void setTotal(long total) { - this.total = total; - } - public int getCount() { - return this.count; - } - public void setCount(int count) { - this.count = count; - } - public List getOpenids() { - return this.openids; - } - public void setOpenids(List openids) { - this.openids = openids; - } - public String getNextOpenid() { - return this.nextOpenid; - } - public void setNextOpenid(String nextOpenid) { - this.nextOpenid = nextOpenid; - } public static WxMpUserList fromJson(String json) { - return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUserList.class); + return WxMpGsonBuilder.create().fromJson(json, WxMpUserList.class); } @Override public String toString() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpDeviceIdentifier.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpDeviceIdentifier.java new file mode 100644 index 0000000000..e93bf75cf9 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpDeviceIdentifier.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.shake; + +import com.google.gson.JsonObject; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class WxMpDeviceIdentifier implements Serializable { + private Integer device_id; + private String uuid; + private Integer page_id; + private Integer major; + private Integer minor; + public JsonObject toJsonObject(){ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("device_id", device_id); + jsonObject.addProperty("uuid", uuid); + jsonObject.addProperty("major", major); + jsonObject.addProperty("minor", minor); + return jsonObject; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundDeviceBindPageQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundDeviceBindPageQuery.java new file mode 100644 index 0000000000..71da0b1c65 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundDeviceBindPageQuery.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.shake; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.Data; + +import java.util.Collection; + +@Data +public class WxMpShakeAroundDeviceBindPageQuery { + private WxMpDeviceIdentifier deviceIdentifier; + private Collection pageIds; + public String toJsonString(){ + JsonObject jsonObject = new JsonObject(); + jsonObject.add("device_identifier", deviceIdentifier.toJsonObject()); + JsonArray jsonArray = new JsonArray(); + for(Integer pageid: pageIds){ + jsonArray.add(pageid); + } + jsonObject.add("page_ids", jsonArray); + return jsonObject.toString(); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundPageAddQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundPageAddQuery.java new file mode 100644 index 0000000000..b04ced93bd --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundPageAddQuery.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.shake; + +import com.google.gson.JsonObject; +import lombok.Data; + +import java.io.Serializable; +@Data +public class WxMpShakeAroundPageAddQuery implements Serializable { + private String title; + private String description; + private String pageUrl; + private String comment; + private String iconUrl; + public String toJsonString(){ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("title", title); + jsonObject.addProperty("description", description); + jsonObject.addProperty("page_url", pageUrl); + jsonObject.addProperty("comment", comment); + jsonObject.addProperty("icon_url", iconUrl); + return jsonObject.toString(); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundPageAddResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundPageAddResult.java new file mode 100644 index 0000000000..f80cfe52d2 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundPageAddResult.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.mp.bean.shake; + +import com.google.gson.JsonObject; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +@Data +public class WxMpShakeAroundPageAddResult implements Serializable { + private Integer errorCode; + private String errorMsg; + private Integer pageId; + public static WxMpShakeAroundPageAddResult fromJson(String json) { + JsonObject jsonObject = WxMpGsonBuilder.create().fromJson(json, JsonObject.class); + WxMpShakeAroundPageAddResult result = new WxMpShakeAroundPageAddResult(); + result.setErrorCode(GsonHelper.getInteger(jsonObject, "errcode")); + result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + jsonObject = jsonObject.getAsJsonObject("data"); + if(jsonObject != null){ + result.setPageId(GsonHelper.getInteger(jsonObject, "page_id")); + } + return result; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundRelationSearchQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundRelationSearchQuery.java new file mode 100644 index 0000000000..390fe50964 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundRelationSearchQuery.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.shake; + +import com.google.gson.JsonObject; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class WxMpShakeAroundRelationSearchQuery implements Serializable { + private int type; + private Integer pageId; + private Integer begin; + private Integer count; + private WxMpDeviceIdentifier deviceIdentifier; + public String toJsonString(){ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("type", type); + switch (type){ + case 1: + jsonObject.add("device_identifier", deviceIdentifier.toJsonObject()); + break; + case 2: + jsonObject.addProperty("page_id", pageId); + jsonObject.addProperty("begin", begin); + jsonObject.addProperty("count", count); + break; + default: + throw new IllegalArgumentException("type error"); + } + return jsonObject.toString(); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundRelationSearchResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundRelationSearchResult.java new file mode 100644 index 0000000000..71dbd8d860 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/shake/WxMpShakeAroundRelationSearchResult.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.mp.bean.shake; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +@Data +public class WxMpShakeAroundRelationSearchResult implements Serializable { + private Integer errcode; + private String errmsg; + private WxMpShakeAcoundRelationSearch data; + public static WxMpShakeAroundRelationSearchResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpShakeAroundRelationSearchResult.class); + } + @Data + public static class WxMpShakeAcoundRelationSearch implements Serializable{ + private List relations; + private Integer total_count; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreBaseInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreBaseInfo.java index 91d52d2ee0..d224976575 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreBaseInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreBaseInfo.java @@ -1,561 +1,202 @@ package me.chanjar.weixin.mp.bean.store; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; import me.chanjar.weixin.common.annotation.Required; -import me.chanjar.weixin.common.util.ToStringUtils; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.math.BigDecimal; -import java.util.List; - /** - * 门店基础信息 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016-09-23. + *
    + *  门店基础信息
    + *  Created by Binary Wang on 2016-09-23.
    + * 
    + * + * @author Binary Wang */ -public class WxMpStoreBaseInfo { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public static WxMpStoreBaseInfo fromJson(String json) { - return WxMpGsonBuilder.create().fromJson(json, WxMpStoreBaseInfo.class); - } - - public String toJson() { - JsonElement base_info = WxMpGsonBuilder.create().toJsonTree(this); - JsonObject jsonObject = new JsonObject(); - jsonObject.add("base_info", base_info); - JsonObject business = new JsonObject(); - business.add("business", jsonObject); - return business.toString(); - } - - public static class WxMpStorePhoto { - /** - * 照片url - */ - @SerializedName("photo_url") - private String photoUrl; - } +@Data +@Builder +public class WxMpStoreBaseInfo implements Serializable { + private static final long serialVersionUID = 829577606838118218L; /** - * sid - * 商户自己的id,用于后续审核通过收到poi_id 的通知时,做对应关系。请商户自己保证唯一识别性 - */ + * sid + * 商户自己的id,用于后续审核通过收到poi_id 的通知时,做对应关系。请商户自己保证唯一识别性 + */ @SerializedName("sid") private String sid; - /** - * business_name - * 门店名称(仅为商户名,如:国美、麦当劳,不应包含地区、地址、分店名等信息,错误示例:北京国美) - * 不能为空,15个汉字或30个英文字符内 - */ + * business_name + * 门店名称(仅为商户名,如:国美、麦当劳,不应包含地区、地址、分店名等信息,错误示例:北京国美) + * 不能为空,15个汉字或30个英文字符内 + */ @Required @SerializedName("business_name") private String businessName; - /** - * branch_name - * 分店名称(不应包含地区信息,不应与门店名有重复,错误示例:北京王府井店) - * 10个字以内 - */ + * branch_name + * 分店名称(不应包含地区信息,不应与门店名有重复,错误示例:北京王府井店) + * 10个字以内 + */ @Required @SerializedName("branch_name") private String branchName; - /** - * province - * 门店所在的省份(直辖市填城市名,如:北京市) - * 10个字以内 - */ + * province + * 门店所在的省份(直辖市填城市名,如:北京市) + * 10个字以内 + */ @Required @SerializedName("province") private String province; - /** - * city - * 门店所在的城市 - * 10个字以内 - */ + * city + * 门店所在的城市 + * 10个字以内 + */ @Required @SerializedName("city") private String city; - /** - * district - * 门店所在地区 - * 10个字以内 - */ + * district + * 门店所在地区 + * 10个字以内 + */ @Required @SerializedName("district") private String district; - /** - * address - * 门店所在的详细街道地址(不要填写省市信息) - * (东莞等没有“区”行政区划的城市,该字段可不必填写。其余城市必填。) - */ + * address + * 门店所在的详细街道地址(不要填写省市信息) + * (东莞等没有“区”行政区划的城市,该字段可不必填写。其余城市必填。) + */ @Required @SerializedName("address") private String address; - /** - * telephone - * 门店的电话(纯数字,区号、分机号均由“-”隔开) - */ + * telephone + * 门店的电话(纯数字,区号、分机号均由“-”隔开) + */ @Required @SerializedName("telephone") private String telephone; - /** - * categories - * 门店的类型(不同级分类用“,”隔开,如:美食,川菜,火锅。详细分类参见附件:微信门店类目表) - */ + * categories + * 门店的类型(不同级分类用“,”隔开,如:美食,川菜,火锅。详细分类参见附件:微信门店类目表) + */ @Required @SerializedName("categories") private String[] categories; - /** - * offsetType - * 坐标类型,1 为火星坐标(目前只能选1) - */ + * offsetType + * 坐标类型,1 为火星坐标(目前只能选1) + */ @Required @SerializedName("offset_type") - private Integer offsetType = 1; - + @Builder.Default + private final Integer offsetType = 1; /** - * longitude - * 门店所在地理位置的经度 - */ + * longitude + * 门店所在地理位置的经度 + */ @Required @SerializedName("longitude") private BigDecimal longitude; - /** - * latitude - * 门店所在地理位置的纬度(经纬度均为火星坐标,最好选用腾讯地图标记的坐标) - */ + * latitude + * 门店所在地理位置的纬度(经纬度均为火星坐标,最好选用腾讯地图标记的坐标) + */ @Required @SerializedName("latitude") private BigDecimal latitude; - /** - * photo_list - * 图片列表,url 形式,可以有多张图片,尺寸为 640*340px。必须为上一接口生成的url。 - * 图片内容不允许与门店不相关,不允许为二维码、员工合照(或模特肖像)、营业执照、无门店正门的街景、地图截图、公交地铁站牌、菜单截图等 - */ + * photo_list + * 图片列表,url 形式,可以有多张图片,尺寸为 640*340px。必须为上一接口生成的url。 + * 图片内容不允许与门店不相关,不允许为二维码、员工合照(或模特肖像)、营业执照、无门店正门的街景、地图截图、公交地铁站牌、菜单截图等 + */ @SerializedName("photo_list") private List photos; - /** - * recommend - * 推荐品,餐厅可为推荐菜;酒店为推荐套房;景点为推荐游玩景点等,针对自己行业的推荐内容 - * 200字以内 - */ + * recommend + * 推荐品,餐厅可为推荐菜;酒店为推荐套房;景点为推荐游玩景点等,针对自己行业的推荐内容 + * 200字以内 + */ @SerializedName("recommend") private String recommend; - /** - * special - * 特色服务,如免费wifi,免费停车,送货上门等商户能提供的特色功能或服务 - */ + * special + * 特色服务,如免费wifi,免费停车,送货上门等商户能提供的特色功能或服务 + */ @SerializedName("special") private String special; - /** - * introduction - * 商户简介,主要介绍商户信息等 - * 300字以内 - */ + * introduction + * 商户简介,主要介绍商户信息等 + * 300字以内 + */ @SerializedName("introduction") private String introduction; - /** * open_time - * 营业时间,24 小时制表示,用“-”连接,如 8:00-20:00 - */ + * 营业时间,24 小时制表示,用“-”连接,如 8:00-20:00 + */ @SerializedName("open_time") private String openTime; - /** - * avg_price - * 人均价格,大于0 的整数 - */ + * avg_price + * 人均价格,大于0 的整数 + */ @SerializedName("avg_price") private Integer avgPrice; - /** * 门店是否可用状态。1 表示系统错误、2 表示审核中、3 审核通过、4 审核驳回。当该字段为1、2、4 状态时,poi_id 为空 */ @SerializedName("available_state") private Integer availableState; - /** * 扩展字段是否正在更新中。1 表示扩展字段正在更新中,尚未生效,不允许再次更新; 0 表示扩展字段没有在更新中或更新已生效,可以再次更新 */ @SerializedName("update_status") private Integer updateStatus; - /** * 门店poi id */ @SerializedName("poi_id") private String poiId; - public String getSid() { - return this.sid; - } - - public void setSid(String sid) { - this.sid = sid; - } - - public String getBusinessName() { - return this.businessName; - } - - public void setBusinessName(String businessName) { - this.businessName = businessName; - } - - public String getBranchName() { - return this.branchName; - } - - public void setBranchName(String branchName) { - this.branchName = branchName; - } - - public String getProvince() { - return this.province; - } - - public void setProvince(String province) { - this.province = province; - } - - public String getCity() { - return this.city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getDistrict() { - return this.district; - } - - public void setDistrict(String district) { - this.district = district; - } - - public String getAddress() { - return this.address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getTelephone() { - return this.telephone; - } - - public void setTelephone(String telephone) { - this.telephone = telephone; - } - - public String[] getCategories() { - return this.categories; - } - - public void setCategories(String[] categories) { - this.categories = categories; - } - - public Integer getOffsetType() { - return this.offsetType; - } - - public void setOffsetType(Integer offsetType) { - this.offsetType = offsetType; - } - - public BigDecimal getLongitude() { - return this.longitude; - } - - public void setLongitude(BigDecimal longitude) { - this.longitude = longitude; - } - - public BigDecimal getLatitude() { - return this.latitude; - } - - public void setLatitude(BigDecimal latitude) { - this.latitude = latitude; - } - - public List getPhotos() { - return this.photos; - } - - public void setPhotos(List photos) { - this.photos = photos; - } - - public String getRecommend() { - return this.recommend; - } - - public void setRecommend(String recommend) { - this.recommend = recommend; - } - - public String getSpecial() { - return this.special; - } - - public void setSpecial(String special) { - this.special = special; - } - - public String getIntroduction() { - return this.introduction; - } - - public void setIntroduction(String introduction) { - this.introduction = introduction; - } - - public String getOpenTime() { - return this.openTime; - } - - public void setOpenTime(String openTime) { - this.openTime = openTime; - } - - public Integer getAvgPrice() { - return this.avgPrice; - } - - public void setAvgPrice(Integer avgPrice) { - this.avgPrice = avgPrice; - } - - public Integer getAvailableState() { - return this.availableState; - } - - public void setAvailableState(Integer availableState) { - this.availableState = availableState; - } - - public Integer getUpdateStatus() { - return this.updateStatus; - } - - public void setUpdateStatus(Integer updateStatus) { - this.updateStatus = updateStatus; - } - - public String getPoiId() { - return this.poiId; + public static WxMpStoreBaseInfo fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpStoreBaseInfo.class); } - public void setPoiId(String poiId) { - this.poiId = poiId; + @Override + public String toString() { + return this.toJson(); } - public static WxMpStoreBaseInfoBuilder builder() { - return new WxMpStoreBaseInfoBuilder(); + public String toJson() { + JsonElement baseInfo = WxMpGsonBuilder.create().toJsonTree(this); + JsonObject jsonObject = new JsonObject(); + jsonObject.add("base_info", baseInfo); + JsonObject business = new JsonObject(); + business.add("business", jsonObject); + return business.toString(); } - public static class WxMpStoreBaseInfoBuilder { - private String sid; - private String businessName; - private String branchName; - private String province; - private String city; - private String district; - private String address; - private String telephone; - private String[] categories; - private Integer offsetType; - private BigDecimal longitude; - private BigDecimal latitude; - private List photos; - private String recommend; - private String special; - private String introduction; - private String openTime; - private Integer avgPrice; - private Integer availableState; - private Integer updateStatus; - private String poiId; - - public WxMpStoreBaseInfoBuilder sid(String sid) { - this.sid = sid; - return this; - } - - public WxMpStoreBaseInfoBuilder businessName(String businessName) { - this.businessName = businessName; - return this; - } - - public WxMpStoreBaseInfoBuilder branchName(String branchName) { - this.branchName = branchName; - return this; - } - - public WxMpStoreBaseInfoBuilder province(String province) { - this.province = province; - return this; - } - - public WxMpStoreBaseInfoBuilder city(String city) { - this.city = city; - return this; - } - - public WxMpStoreBaseInfoBuilder district(String district) { - this.district = district; - return this; - } - - public WxMpStoreBaseInfoBuilder address(String address) { - this.address = address; - return this; - } - - public WxMpStoreBaseInfoBuilder telephone(String telephone) { - this.telephone = telephone; - return this; - } - - public WxMpStoreBaseInfoBuilder categories(String[] categories) { - this.categories = categories; - return this; - } - - public WxMpStoreBaseInfoBuilder offsetType(Integer offsetType) { - this.offsetType = offsetType; - return this; - } - - public WxMpStoreBaseInfoBuilder longitude(BigDecimal longitude) { - this.longitude = longitude; - return this; - } - - public WxMpStoreBaseInfoBuilder latitude(BigDecimal latitude) { - this.latitude = latitude; - return this; - } - - public WxMpStoreBaseInfoBuilder photos(List photos) { - this.photos = photos; - return this; - } - - public WxMpStoreBaseInfoBuilder recommend(String recommend) { - this.recommend = recommend; - return this; - } - - public WxMpStoreBaseInfoBuilder special(String special) { - this.special = special; - return this; - } - - public WxMpStoreBaseInfoBuilder introduction(String introduction) { - this.introduction = introduction; - return this; - } - - public WxMpStoreBaseInfoBuilder openTime(String openTime) { - this.openTime = openTime; - return this; - } - - public WxMpStoreBaseInfoBuilder avgPrice(Integer avgPrice) { - this.avgPrice = avgPrice; - return this; - } - - public WxMpStoreBaseInfoBuilder availableState(Integer availableState) { - this.availableState = availableState; - return this; - } - - public WxMpStoreBaseInfoBuilder updateStatus(Integer updateStatus) { - this.updateStatus = updateStatus; - return this; - } - - public WxMpStoreBaseInfoBuilder poiId(String poiId) { - this.poiId = poiId; - return this; - } - - public WxMpStoreBaseInfoBuilder from(WxMpStoreBaseInfo origin) { - this.sid(origin.sid); - this.businessName(origin.businessName); - this.branchName(origin.branchName); - this.province(origin.province); - this.city(origin.city); - this.district(origin.district); - this.address(origin.address); - this.telephone(origin.telephone); - this.categories(origin.categories); - this.offsetType(origin.offsetType); - this.longitude(origin.longitude); - this.latitude(origin.latitude); - this.photos(origin.photos); - this.recommend(origin.recommend); - this.special(origin.special); - this.introduction(origin.introduction); - this.openTime(origin.openTime); - this.avgPrice(origin.avgPrice); - this.availableState(origin.availableState); - this.updateStatus(origin.updateStatus); - this.poiId(origin.poiId); - return this; - } + @Data + public static class WxMpStorePhoto implements Serializable { + private static final long serialVersionUID = -2942447907614186861L; + /** + * 照片url. + */ + @SerializedName("photo_url") + private String photoUrl; - public WxMpStoreBaseInfo build() { - WxMpStoreBaseInfo m = new WxMpStoreBaseInfo(); - m.sid = this.sid; - m.businessName = this.businessName; - m.branchName = this.branchName; - m.province = this.province; - m.city = this.city; - m.district = this.district; - m.address = this.address; - m.telephone = this.telephone; - m.categories = this.categories; - m.offsetType = this.offsetType; - m.longitude = this.longitude; - m.latitude = this.latitude; - m.photos = this.photos; - m.recommend = this.recommend; - m.special = this.special; - m.introduction = this.introduction; - m.openTime = this.openTime; - m.avgPrice = this.avgPrice; - m.availableState = this.availableState; - m.updateStatus = this.updateStatus; - m.poiId = this.poiId; - return m; - } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreInfo.java index 1450bc3cd4..58a9efd244 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreInfo.java @@ -1,23 +1,25 @@ package me.chanjar.weixin.mp.bean.store; +import java.io.Serializable; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -public class WxMpStoreInfo { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } +/** + * . + * + * @author BinaryWang + */ +@Data +public class WxMpStoreInfo implements Serializable { + private static final long serialVersionUID = 7300598931768355461L; @SerializedName("base_info") private WxMpStoreBaseInfo baseInfo; - public WxMpStoreBaseInfo getBaseInfo() { - return this.baseInfo; - } - - public void setBaseInfo(WxMpStoreBaseInfo baseInfo) { - this.baseInfo = baseInfo; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreListResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreListResult.java index f9cc3fbeed..6841297c05 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreListResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/store/WxMpStoreListResult.java @@ -1,81 +1,52 @@ package me.chanjar.weixin.mp.bean.store; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** + *
      * 门店列表结果类
    - * @author binarywang(Binary Wang)
    - *         Created by Binary Wang on 2016-09-27.
    + * Created by Binary Wang on 2016-09-27.
    + * 
    * + * @author Binary Wang */ -public class WxMpStoreListResult { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public static WxMpStoreListResult fromJson(String json) { - return WxMpGsonBuilder.create().fromJson(json, WxMpStoreListResult.class); - } +@Data +public class WxMpStoreListResult implements Serializable { + private static final long serialVersionUID = 5388907559949538663L; /** - * 错误码,0为正常 + * 错误码,0为正常. */ @SerializedName("errcode") private Integer errCode; - /** - * 错误信息 + * 错误信息. */ @SerializedName("errmsg") private String errMsg; - /** - * 门店信息列表 + * 门店信息列表. */ @SerializedName("business_list") private List businessList; - /** - * 门店信息总数 + * 门店信息总数. */ @SerializedName("total_count") private Integer totalCount; - public Integer getTotalCount() { - return this.totalCount; - } - - public void setTotalCount(Integer totalCount) { - this.totalCount = totalCount; - } - - public Integer getErrCode() { - return this.errCode; - } - - public void setErrCode(Integer errCode) { - this.errCode = errCode; - } - - public String getErrMsg() { - return this.errMsg; - } - - public void setErrMsg(String errMsg) { - this.errMsg = errMsg; - } - - public List getBusinessList() { - return this.businessList; + public static WxMpStoreListResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpStoreListResult.class); } - public void setBusinessList(List businessList) { - this.businessList = businessList; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessage.java new file mode 100644 index 0000000000..51c312d30a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessage.java @@ -0,0 +1,87 @@ +package me.chanjar.weixin.mp.bean.subscribe; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * @author Mklaus + * @date 2018-01-22 下午12:18 + */ +@Data +@NoArgsConstructor +@Builder +@AllArgsConstructor +public class WxMpSubscribeMessage { + + /** + * 接收者openid. + */ + private String toUser; + + /** + * 模板ID. + */ + private String templateId; + + /** + * 模板跳转链接. + *
    +   * url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。
    +   * 开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。
    +   * 
    + */ + private String url; + + /** + * 跳小程序所需数据,不需跳小程序可不用传该数据. + * + * @see #url + */ + private MiniProgram miniProgram; + + /** + * 订阅场景值 + */ + private String scene; + + /** + * 消息标题 (15字以内) + */ + private String title; + + /** + * 消息内容文本 (200字以内) + */ + private String contentValue; + + /** + * 消息内容文本颜色 + */ + private String contentColor; + + + public String toJson() { + return WxMpGsonBuilder.create().toJson(this); + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class MiniProgram implements Serializable { + private static final long serialVersionUID = -7945254706501974849L; + + private String appid; + private String pagePath; + + /** + * 是否使用path,否则使用pagepath. + * 加入此字段是基于微信官方接口变化多端的考虑 + */ + private boolean usePath = false; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxTagListUser.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxTagListUser.java index e1307be733..8ee6281353 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxTagListUser.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxTagListUser.java @@ -1,91 +1,66 @@ package me.chanjar.weixin.mp.bean.tag; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** + *
      * 获取标签下粉丝列表的结果对象
    - * @author binarywang(Binary Wang)
    - *         Created by Binary Wang on 2016-09-19.
    + * Created by Binary Wang on 2016-09-19.
    + * 
    + * + * @author Binary Wang */ -public class WxTagListUser { - - public static WxTagListUser fromJson(String json) { - return WxMpGsonBuilder.create().fromJson(json,WxTagListUser.class); - } - - public String toJson() { - return WxMpGsonBuilder.create().toJson(this); - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } +@Data +public class WxTagListUser implements Serializable { + private static final long serialVersionUID = -4551768374200676112L; /** - *"count":2,这次获取的粉丝数量 + * "count":2,这次获取的粉丝数量. */ @SerializedName("count") private Integer count; - /** - *"data" 粉丝列表 + * "data" 粉丝列表. */ @SerializedName("data") private WxTagListUserData data; - /** - *"next_openid" 拉取列表最后一个用户的openid + * "next_openid" 拉取列表最后一个用户的openid. */ @SerializedName("next_openid") private String nextOpenid; - public Integer getCount() { - return this.count; - } - - public void setCount(Integer count) { - this.count = count; - } - - public WxTagListUserData getData() { - return this.data; - } - - public void setData(WxTagListUserData data) { - this.data = data; + public static WxTagListUser fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxTagListUser.class); } - public String getNextOpenid() { - return this.nextOpenid; + public String toJson() { + return WxMpGsonBuilder.create().toJson(this); } - public void setNextOpenid(String nextOpenid) { - this.nextOpenid = nextOpenid; + @Override + public String toString() { + return this.toJson(); } - public static class WxTagListUserData { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } + @Data + public static class WxTagListUserData implements Serializable { + private static final long serialVersionUID = -8584537400336245701L; /** - * openid 列表 + * openid 列表. */ @SerializedName("openid") private List openidList; - public List getOpenidList() { - return this.openidList; - } - - public void setOpenidList(List openidList) { - this.openidList = openidList; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxUserTag.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxUserTag.java index 6554c26153..76d4f8bccf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxUserTag.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/tag/WxUserTag.java @@ -1,67 +1,51 @@ package me.chanjar.weixin.mp.bean.tag; -import com.google.gson.JsonParser; +import java.io.Serializable; +import java.util.List; + import com.google.gson.reflect.TypeToken; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** - * 用户标签对象 - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/9/2. + *
    + *  用户标签对象
    + *  Created by Binary Wang on 2016/9/2.
    + * 
    + * + * @author Binary Wang */ -public class WxUserTag { +@Data +public class WxUserTag implements Serializable { + private static final long serialVersionUID = -7722428695667031252L; + /** - * id 标签id,由微信分配 + * 标签id,由微信分配. */ private Long id; /** - * name 标签名,UTF8编码 + * 标签名,UTF8编码. */ private String name; /** - * count 此标签下粉丝数 + * 此标签下粉丝数. */ private Integer count; - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getCount() { - return this.count; - } - - public void setCount(Integer count) { - this.count = count; - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - public static WxUserTag fromJson(String json) { return WxMpGsonBuilder.create().fromJson( - new JsonParser().parse(json).getAsJsonObject().get("tag"), - WxUserTag.class); + GsonParser.parse(json).get("tag"), + WxUserTag.class); } public static List listFromJson(String json) { return WxMpGsonBuilder.create().fromJson( - new JsonParser().parse(json).getAsJsonObject().get("tags"), - new TypeToken>(){}.getType()); + GsonParser.parse(json).get("tags"), + new TypeToken>() { + }.getType()); } public String toJson() { @@ -70,6 +54,6 @@ public String toJson() { @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return this.toJson(); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplate.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplate.java index 368284f27c..4458ec3a8d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplate.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplate.java @@ -1,123 +1,73 @@ package me.chanjar.weixin.mp.bean.template; -import com.google.gson.JsonParser; +import java.io.Serializable; +import java.util.List; + import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import java.util.List; - /** *
      * 模板列表信息
      * Created by Binary Wang on 2016-10-17.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ -public class WxMpTemplate { - - private static final JsonParser JSON_PARSER = new JsonParser(); +@Data +public class WxMpTemplate implements Serializable { - public static List fromJson(String json) { - return WxMpGsonBuilder.create().fromJson(JSON_PARSER.parse(json).getAsJsonObject().get("template_list"), - new TypeToken>() { - }.getType()); - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } + private static final long serialVersionUID = -7366474522571199372L; /** - * template_id + * template_id. * 模板ID */ @SerializedName("template_id") private String templateId; - /** - * title + * title. * 模板标题 */ @SerializedName("title") private String title; - /** - * primary_industry + * primary_industry. * 模板所属行业的一级行业 */ @SerializedName("primary_industry") private String primaryIndustry; - /** - * deputy_industry + * deputy_industry. * 模板所属行业的二级行业 */ @SerializedName("deputy_industry") private String deputyIndustry; - /** - * content + * content. * 模板内容 */ @SerializedName("content") private String content; - /** - * example + * example. * 模板示例 */ @SerializedName("example") private String example; - public String getTemplateId() { - return this.templateId; - } - - public void setTemplateId(String templateId) { - this.templateId = templateId; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getPrimaryIndustry() { - return this.primaryIndustry; - } - - public void setPrimaryIndustry(String primaryIndustry) { - this.primaryIndustry = primaryIndustry; - } - - public String getDeputyIndustry() { - return this.deputyIndustry; - } - - public void setDeputyIndustry(String deputyIndustry) { - this.deputyIndustry = deputyIndustry; - } - - public String getContent() { - return this.content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getExample() { - return this.example; + public static List fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(GsonParser.parse(json).get("template_list"), + new TypeToken>() { + }.getType()); } - public void setExample(String example) { - this.example = example; + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateData.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateData.java index 0eb0eaa58d..ada9952040 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateData.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateData.java @@ -1,13 +1,16 @@ package me.chanjar.weixin.mp.bean.template; +import lombok.Data; + import java.io.Serializable; /** * @author Daniel Qian */ +@Data public class WxMpTemplateData implements Serializable { - private static final long serialVersionUID = 6301835292940277870L; + private String name; private String value; private String color; @@ -26,28 +29,4 @@ public WxMpTemplateData(String name, String value, String color) { this.color = color; } - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getColor() { - return this.color; - } - - public void setColor(String color) { - this.color = color; - } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustry.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustry.java index 6d0db1ab43..adab37850b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustry.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustry.java @@ -1,7 +1,10 @@ package me.chanjar.weixin.mp.bean.template; -import me.chanjar.weixin.common.util.ToStringUtils; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; @@ -9,98 +12,49 @@ /** * @author miller */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) public class WxMpTemplateIndustry implements Serializable { private static final long serialVersionUID = -7700398224795914722L; - private Industry primaryIndustry; - private Industry secondIndustry; - public WxMpTemplateIndustry() { + private WxMpTemplateIndustryEnum primaryIndustry; + private WxMpTemplateIndustryEnum secondIndustry; + + public static WxMpTemplateIndustry fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpTemplateIndustry.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); } - public WxMpTemplateIndustry(Industry primaryIndustry, Industry secondIndustry) { - this.primaryIndustry = primaryIndustry; - this.secondIndustry = secondIndustry; + public String toJson() { + return WxMpGsonBuilder.create().toJson(this); } /** - * @author miller - * 官方文档中,创建和获取的数据结构不一样。所以采用冗余字段的方式,实现相应的接口 + * 官方文档中,创建和获取的数据结构不一样。所以采用冗余字段的方式,实现相应的接口. */ + @Data + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) public static class Industry implements Serializable { private static final long serialVersionUID = -1707184885588012142L; private String id; private String firstClass; private String secondClass; - public Industry() { - } - public Industry(String id) { this.id = id; } - public Industry(String id, String firstClass, String secondClass) { - this.id = id; - this.firstClass = firstClass; - this.secondClass = secondClass; - } - @Override public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - public String getFirstClass() { - return this.firstClass; - } - - public void setFirstClass(String firstClass) { - this.firstClass = firstClass; + return WxMpGsonBuilder.create().toJson(this); } - - public String getSecondClass() { - return this.secondClass; - } - - public void setSecondClass(String secondClass) { - this.secondClass = secondClass; - } - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public static WxMpTemplateIndustry fromJson(String json) { - return WxMpGsonBuilder.create().fromJson(json, WxMpTemplateIndustry.class); - } - - public String toJson() { - return WxMpGsonBuilder.create().toJson(this); - } - - public Industry getPrimaryIndustry() { - return this.primaryIndustry; - } - - public void setPrimaryIndustry(Industry primaryIndustry) { - this.primaryIndustry = primaryIndustry; - } - - public Industry getSecondIndustry() { - return this.secondIndustry; - } - - public void setSecondIndustry(Industry secondIndustry) { - this.secondIndustry = secondIndustry; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryEnum.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryEnum.java new file mode 100644 index 0000000000..6620d86aa4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryEnum.java @@ -0,0 +1,225 @@ +package me.chanjar.weixin.mp.bean.template; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 模版消息行业枚举. + * + * @author Binary Wang + * @date 2019-10-18 + */ +@Getter +@AllArgsConstructor +public enum WxMpTemplateIndustryEnum { + /** + * IT科技 - 互联网|电子商务 + */ + E_COMMERCE("IT科技", "互联网|电子商务", 1), + /** + * IT科技 - IT软件与服务 + */ + IT_SOFTWARE_AND_SERVICES("IT科技", "IT软件与服务", 2), + /** + * IT科技 - IT硬件与设备 + */ + IT_HARDWARE_AND_EQUIPMENT("IT科技", "IT硬件与设备", 3), + /** + * IT科技 - 电子技术 + */ + ELECTRONIC_TECHNIQUE("IT科技", "电子技术", 4), + /** + * IT科技 - 通信与运营商 + */ + COMMUNICATION_AND_OPERATOR("IT科技", "通信与运营商", 5), + /** + * IT科技 - 网络游戏 + */ + ONLINE_GAME("IT科技", "网络游戏", 6), + /** + * 金融业 - 银行 + */ + BANK("金融业", "银行", 7), + /** + * 金融业 - 证券|基金|理财|信托(实际是这个) + */ + FUND("金融业", "证券基金理财信托", 8), + /** + * 金融业 - 保险 + */ + INSURANCE("金融业", "保险", 9), + /** + * 餐饮 - 餐饮 + */ + REPAST("餐饮", "餐饮", 10), + /** + * 酒店旅游 - 酒店 + */ + HOTEL("酒店旅游", "酒店", 11), + /** + * 酒店旅游 - 旅游 + */ + TRAVEL("酒店旅游", "旅游", 12), + /** + * 运输与仓储 - 快递 + */ + EXPRESS("运输与仓储", "快递", 13), + /** + * 运输与仓储 - 物流 + */ + LOGISTICS("运输与仓储", "物流", 14), + /** + * 运输与仓储 - 仓储 + */ + STORAGE("运输与仓储", "仓储", 15), + /** + * 教育 - 培训 + */ + CULTIVATE("教育", "培训", 16), + /** + * 教育 - 院校 + */ + ACADEMY("教育", "院校", 17), + /** + * 政府与公共事业 - 学术科研 + */ + ACADEMIC_RESEARCH("政府与公共事业", "学术科研", 18), + /** + * 政府与公共事业 - 交警 + */ + TRAFFIC_POLICE("政府与公共事业", "交警", 19), + /** + * 政府与公共事业 - 博物馆 + */ + MUSEUM("政府与公共事业", "博物馆", 20), + /** + * 政府与公共事业 - 公共事业非盈利机构 + */ + PUBLIC_WORKS_NONPROFIT("政府与公共事业", "公共事业非盈利机构", 21), + /** + * 医药护理 - 医药医疗 + */ + MEDICAL_HEALTH("医药护理", "医药医疗", 22), + /** + * 医药护理 - 护理美容 + */ + CARE_AND_BEAUTY("医药护理", "护理美容", 23), + /** + * 医药护理 - 保健与卫生 + */ + HEALTH_AND_HYGIENE("医药护理", "保健与卫生", 24), + /** + * 交通工具 - 汽车相关 + */ + AUTOMOTIVE_RELATED("交通工具", "汽车相关", 25), + /** + * 交通工具 - 摩托车相关 + */ + MOTORCYCLE_CORRELATION("交通工具", "摩托车相关", 26), + /** + * 交通工具 - 火车相关 + */ + THE_TRAIN_RELATED("交通工具", "火车相关", 27), + /** + * 交通工具 - 飞机相关 + */ + THE_PLANE_RELATED("交通工具", "飞机相关", 28), + /** + * 房地产 - 房地产|建筑(实际上是这个) + */ + ARCHITECTURE("房地产", "建筑", 29), + /** + * 房地产 - 物业 + */ + REAL_ESTATE("房地产", "物业", 30), + /** + * 消费品 - 消费品 + */ + CONSUMER_GOODS("消费品", "消费品", 31), + /** + * 商业服务 - 法律 + */ + LEGISLATION("商业服务", "法律", 32), + /** + * 商业服务 - 会展 + */ + CONVENTION_AND_EXHIBITION("商业服务", "会展", 33), + /** + * 商业服务 - 中介服务 + */ + INTERMEDIARY_SERVICES("商业服务", "中介服务", 34), + /** + * 商业服务 - 认证 + */ + AUTHENTICATION("商业服务", "认证", 35), + /** + * 商业服务 - 审计 + */ + AUDIT("商业服务", "审计", 36), + /** + * 文体娱乐 - 传媒 + */ + MASS_MEDIA("文体娱乐", "传媒", 37), + /** + * 文体娱乐 - 体育 + */ + SPORTS("文体娱乐", "体育", 38), + /** + * 文体娱乐 - 娱乐休闲 + */ + LEISURE_AND_ENTERTAINMENT("文体娱乐", "娱乐休闲", 39), + /** + * 印刷 - 印刷 + */ + PRINTING("印刷", "印刷", 40), + /** + * 其它 - 其它 + */ + OTHER("其它", "其它", 41); + + /** + * 主行业(一级行业) + */ + public final String firstClass; + /** + * 副行业(二级行业) + */ + public final String secondClass; + /** + * 行业代码 + */ + public final Integer code; + + /** + * 查找行业 + * + * @param firstClass 主行业名称 + * @param secondClass 副行业名称 + * @return . + */ + public static WxMpTemplateIndustryEnum findByClass(String firstClass, String secondClass) { + for (WxMpTemplateIndustryEnum industryEnum : WxMpTemplateIndustryEnum.values()) { + if (industryEnum.firstClass.equals(firstClass) && industryEnum.secondClass.contains(secondClass)) { + return industryEnum; + } + } + + return null; + } + + /** + * 查找行业 + * + * @param code 行业编码 + * @return . + */ + public static WxMpTemplateIndustryEnum findByCode(int code) { + for (WxMpTemplateIndustryEnum industryEnum : WxMpTemplateIndustryEnum.values()) { + if (industryEnum.code == code) { + return industryEnum; + } + } + + return null; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java index f70090ef5b..99c3df358e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java @@ -1,179 +1,89 @@ package me.chanjar.weixin.mp.bean.template; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + /** + * 模板消息. * 参考 http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN 发送模板消息接口部分 + * + * @author Binary Wang */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder public class WxMpTemplateMessage implements Serializable { private static final long serialVersionUID = 5063374783759519418L; /** - * 接收者openid + * 接收者openid. */ private String toUser; /** - * 模板ID + * 模板ID. */ private String templateId; /** + * 模板跳转链接. *
    -   * 跳小程序所需数据,不需跳小程序可不用传该数据
        * url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。
        * 开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。
        * 
    */ private String url; + /** - * 模板跳转链接 + * 跳小程序所需数据,不需跳小程序可不用传该数据. + * * @see #url */ private MiniProgram miniProgram; /** - * 模板数据 + * 模板数据. */ + @Builder.Default private List data = new ArrayList<>(); - public WxMpTemplateMessage() { - } - - public String getToUser() { - return this.toUser; - } - - public void setToUser(String toUser) { - this.toUser = toUser; - } - - public String getTemplateId() { - return this.templateId; - } - - public void setTemplateId(String templateId) { - this.templateId = templateId; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public List getData() { - return this.data; - } - - public void setData(List data) { - this.data = data; - } - - public void addWxMpTemplateData(WxMpTemplateData datum) { + public WxMpTemplateMessage addData(WxMpTemplateData datum) { + if (this.data == null) { + this.data = new ArrayList<>(); + } this.data.add(datum); - } - - public MiniProgram getMiniProgram() { - return this.miniProgram; - } - - public void setMiniProgram(MiniProgram miniProgram) { - this.miniProgram = miniProgram; + return this; } public String toJson() { - return WxMpGsonBuilder.INSTANCE.create().toJson(this); + return WxMpGsonBuilder.create().toJson(this); } - public static WxMpTemplateMessageBuilder builder() { - return new WxMpTemplateMessageBuilder(); - } + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class MiniProgram implements Serializable { + private static final long serialVersionUID = -7945254706501974849L; - public static class MiniProgram { private String appid; private String pagePath; - public MiniProgram() { - } - - public MiniProgram(String appid, String pagePath) { - this.appid = appid; - this.pagePath = pagePath; - } - - public String getAppid() { - return this.appid; - } - - public void setAppid(String appid) { - this.appid = appid; - } - - public String getPagePath() { - return this.pagePath; - } - - public void setPagePath(String pagePath) { - this.pagePath = pagePath; - } - } - - public static class WxMpTemplateMessageBuilder { - private String toUser; - private String templateId; - private String url; - private List data = new ArrayList<>(); - private MiniProgram miniProgram; - - public WxMpTemplateMessageBuilder toUser(String toUser) { - this.toUser = toUser; - return this; - } - - public WxMpTemplateMessageBuilder templateId(String templateId) { - this.templateId = templateId; - return this; - } - - public WxMpTemplateMessageBuilder url(String url) { - this.url = url; - return this; - } - - public WxMpTemplateMessageBuilder data(List data) { - this.data = data; - return this; - } - - public WxMpTemplateMessageBuilder from(WxMpTemplateMessage origin) { - this.toUser(origin.toUser); - this.templateId(origin.templateId); - this.url(origin.url); - this.data(origin.data); - return this; - } - - public WxMpTemplateMessageBuilder miniProgram(MiniProgram miniProgram) { - this.miniProgram = miniProgram; - return this; - } - - public WxMpTemplateMessage build() { - WxMpTemplateMessage m = new WxMpTemplateMessage(); - m.toUser = this.toUser; - m.templateId = this.templateId; - m.url = this.url; - m.data = this.data; - m.miniProgram = this.miniProgram; - return m; - } + /** + * 是否使用path,否则使用pagepath. + * 加入此字段是基于微信官方接口变化多端的考虑 + */ + private boolean usePath = false; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/wifi/WxMpWifiShopDataResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/wifi/WxMpWifiShopDataResult.java new file mode 100644 index 0000000000..50aec028d1 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/wifi/WxMpWifiShopDataResult.java @@ -0,0 +1,114 @@ +package me.chanjar.weixin.mp.bean.wifi; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.util.List; + +/** + * 门店Wi-Fi信息. + * + * @author Binary Wang + * @date 2019-06-16 + */ +@Data +public class WxMpWifiShopDataResult { + public static WxMpWifiShopDataResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("data"), + WxMpWifiShopDataResult.class); + } + + /** + * 门店名称. + */ + @SerializedName("shop_name") + private String shopName; + + /** + * 无线网络设备的ssid,未添加设备为空,多个ssid时显示第一个. + */ + @SerializedName("ssid") + private String ssid; + + /** + * 无线网络设备的ssid列表,返回数组格式. + */ + @SerializedName("ssid_list") + private String[] ssidList; + + /** + * ssid和密码的列表,数组格式。当为密码型设备时,密码才有值. + */ + @SerializedName("ssid_password_list") + private List ssidPasswordList; + + /** + * 设备密码,当设备类型为密码型时返回. + */ + @SerializedName("password") + private String password; + + /** + * 门店内设备的设备类型,0-未添加设备,4-密码型设备,31-portal型设备. + */ + @SerializedName("protocol_type") + private Integer protocolType; + + /** + * 门店内设备总数. + */ + @SerializedName("ap_count") + private Integer apCount; + + /** + * 商家主页模板类型. + */ + @SerializedName("template_id") + private Integer templateId; + + /** + * 商家主页链接. + */ + @SerializedName("homepage_url") + private String homepageUrl; + + /** + * 顶部常驻入口上显示的文本内容:0--欢迎光临+公众号名称;1--欢迎光临+门店名称;2--已连接+公众号名称+WiFi;3--已连接+门店名称+Wi-Fi. + */ + @SerializedName("bar_type") + private Integer barType; + + /** + * 连网完成页链接. + */ + @SerializedName("finishpage_url") + private String finishPageUrl; + + /** + * 商户自己的id,与门店poi_id对应关系,建议在添加门店时候建立关联关系,具体请参考“微信门店接口”. + */ + @SerializedName("sid") + private String sid; + + /** + * 门店ID(适用于微信卡券、微信门店业务),具体定义参考微信门店,与shop_id一一对应. + */ + @SerializedName("poi_id") + private String poiId; + + @Data + public static class SsidPassword { + /** + * 无线网络设备的ssid. + */ + private String ssid; + + /** + * 无线网络设备的password. + */ + private String password; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/wifi/WxMpWifiShopListResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/wifi/WxMpWifiShopListResult.java new file mode 100644 index 0000000000..63bd638d40 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/wifi/WxMpWifiShopListResult.java @@ -0,0 +1,93 @@ +package me.chanjar.weixin.mp.bean.wifi; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.util.List; + +/** + *
    + *  Created by BinaryWang on 2018/6/10.
    + * 
    + * + * @author Binary Wang + */ +@Data +public class WxMpWifiShopListResult { + public static WxMpWifiShopListResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson( + GsonParser.parse(json).get("data"), + WxMpWifiShopListResult.class); + } + + /** + * 总数. + */ + @SerializedName("totalcount") + private int totalCount; + + /** + * 分页下标. + */ + @SerializedName("pageindex") + private int pageIndex; + + /** + * 分页页数. + */ + @SerializedName("pagecount") + private int pageCount; + + private List records; + + @Data + public static class Record { + + /** + * 门店ID(适用于微信连Wi-Fi业务). + */ + @SerializedName("shop_id") + private Integer shopId; + + /** + * 门店名称. + */ + @SerializedName("shop_name") + private String shopName; + + /** + * 无线网络设备的ssid,未添加设备为空,多个ssid时显示第一个. + */ + @SerializedName("ssid") + private String ssid; + + /** + * 无线网络设备的ssid列表,返回数组格式. + */ + @SerializedName("ssid_list") + private List ssidList; + + /** + * 门店内设备的设备类型. + * 0-未添加设备,1-专业型设备,4-密码型设备,5-portal自助型设备,31-portal改造型设备 + */ + @SerializedName("protocol_type") + private Integer protocolType; + + /** + * 商户自己的id. + * 与门店poi_id对应关系,建议在添加门店时候建立关联关系,具体请参考“微信门店接口” + */ + @SerializedName("sid") + private String sid; + + /** + * 门店ID(适用于微信卡券、微信门店业务). + * 具体定义参考微信门店,与shop_id一一对应 + */ + @SerializedName("poi_id") + private String poiId; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/BaseBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/BaseBuilder.java index 3e40d17836..efd20e0d5b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/BaseBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/BaseBuilder.java @@ -7,7 +7,7 @@ public class BaseBuilder { protected String toUser; @SuppressWarnings("unchecked") - public T toUser(String toUser) { + public T toUser(String toUser) { this.toUser = toUser; return (T) this; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/ImageBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/ImageBuilder.java index 9533f5b351..438b0a98a1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/ImageBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/ImageBuilder.java @@ -8,14 +8,14 @@ *
      * 用法: WxMpKefuMessage m = WxMpKefuMessage.IMAGE().mediaId(...).toUser(...).build();
      * 
    - * @author chanjarster * + * @author chanjarster */ public final class ImageBuilder extends BaseBuilder { private String mediaId; public ImageBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_IMAGE; + this.msgType = WxConsts.KefuMsgType.IMAGE; } public ImageBuilder mediaId(String media_id) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MiniProgramPageBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MiniProgramPageBuilder.java new file mode 100644 index 0000000000..0aedbae9c6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MiniProgramPageBuilder.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.mp.builder.kefu; + +import me.chanjar.weixin.common.api.WxConsts.KefuMsgType; +import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; + +/** + * 小程序卡片 builder + *
    + * 用法:
    + * WxMpKefuMessage m = WxMpKefuMessage.MINIPROGRAMPAGE().title("xxxx").thumbMediaId("xxxxx").appId("xxxx").pagePath("****").toUser(...).build();
    + * 
    + * + * @author boris.bao + */ +public final class MiniProgramPageBuilder extends BaseBuilder { + + private String title; + private String appId; + private String pagePath; + private String thumbMediaId; + + public MiniProgramPageBuilder() { + this.msgType = KefuMsgType.MINIPROGRAMPAGE; + } + + + public MiniProgramPageBuilder title(String title) { + this.title = title; + return this; + } + + public MiniProgramPageBuilder appId(String appId) { + this.appId = appId; + return this; + } + + + public MiniProgramPageBuilder pagePath(String pagePath) { + this.pagePath = pagePath; + return this; + } + + + public MiniProgramPageBuilder thumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + return this; + } + + + @Override + public WxMpKefuMessage build() { + WxMpKefuMessage m = super.build(); + m.setTitle(this.title); + m.setMiniProgramAppId(this.appId); + m.setMiniProgramPagePath(this.pagePath); + m.setThumbMediaId(this.thumbMediaId); + return m; + } + + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsBuilder.java index 615802b31f..0d520132f4 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsBuilder.java @@ -9,14 +9,14 @@ * 用法: * WxMpKefuMessage m = WxMpKefuMessage.NEWS().mediaId("xxxxx").toUser(...).build(); *
    - * @author Binary Wang * + * @author Binary Wang */ public final class MpNewsBuilder extends BaseBuilder { private String mediaId; public MpNewsBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_MPNEWS; + this.msgType = WxConsts.KefuMsgType.MPNEWS; } public MpNewsBuilder mediaId(String mediaId) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MusicBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MusicBuilder.java index 8d23bacd4e..0cd40db438 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MusicBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MusicBuilder.java @@ -24,7 +24,7 @@ public final class MusicBuilder extends BaseBuilder { private String hqMusicUrl; public MusicBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_MUSIC; + this.msgType = WxConsts.KefuMsgType.MUSIC; } public MusicBuilder musicUrl(String musicurl) { @@ -47,8 +47,8 @@ public MusicBuilder description(String description) { return this; } - public MusicBuilder thumbMediaId(String thumb_media_id) { - this.thumbMediaId = thumb_media_id; + public MusicBuilder thumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; return this; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java index 900babb5ba..3c00303557 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java @@ -4,6 +4,7 @@ import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -12,19 +13,23 @@ * 用法: * WxMpKefuMessage m = WxMpKefuMessage.NEWS().addArticle(article).toUser(...).build(); *
    - * @author chanjarster * + * @author chanjarster */ public final class NewsBuilder extends BaseBuilder { - private List articles = new ArrayList<>(); public NewsBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_NEWS; + this.msgType = WxConsts.KefuMsgType.NEWS; + } + + public NewsBuilder addArticle(WxMpKefuMessage.WxArticle... articles) { + Collections.addAll(this.articles, articles); + return this; } - public NewsBuilder addArticle(WxMpKefuMessage.WxArticle article) { - this.articles.add(article); + public NewsBuilder articles(List articles) { + this.articles = articles; return this; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/TextBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/TextBuilder.java index 9e5f938403..b8f58bdd34 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/TextBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/TextBuilder.java @@ -8,14 +8,14 @@ *
      * 用法: WxMpKefuMessage m = WxMpKefuMessage.TEXT().content(...).toUser(...).build();
      * 
    - * @author chanjarster * + * @author chanjarster */ public final class TextBuilder extends BaseBuilder { private String content; public TextBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_TEXT; + this.msgType = WxConsts.KefuMsgType.TEXT; } public TextBuilder content(String content) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VideoBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VideoBuilder.java index 13ade68ca5..e9276637d3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VideoBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VideoBuilder.java @@ -14,8 +14,8 @@ * .toUser(...) * .build(); *
    - * @author chanjarster * + * @author chanjarster */ public final class VideoBuilder extends BaseBuilder { private String mediaId; @@ -24,7 +24,7 @@ public final class VideoBuilder extends BaseBuilder { private String thumbMediaId; public VideoBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_VIDEO; + this.msgType = WxConsts.KefuMsgType.VIDEO; } public VideoBuilder mediaId(String mediaId) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VoiceBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VoiceBuilder.java index c9cb32b2fa..a5211548cb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VoiceBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VoiceBuilder.java @@ -8,14 +8,14 @@ *
      * 用法: WxMpKefuMessage m = WxMpKefuMessage.VOICE().mediaId(...).toUser(...).build();
      * 
    - * @author chanjarster * + * @author chanjarster */ public final class VoiceBuilder extends BaseBuilder { private String mediaId; public VoiceBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_VOICE; + this.msgType = WxConsts.KefuMsgType.VOICE; } public VoiceBuilder mediaId(String media_id) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxCardBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxCardBuilder.java index e600df6cb5..64cb067206 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxCardBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxCardBuilder.java @@ -8,14 +8,14 @@ *
      * 用法: WxMpKefuMessage m = WxMpKefuMessage.WXCARD().cardId(...).toUser(...).build();
      * 
    - * @author mgcnrx11 * + * @author mgcnrx11 */ public final class WxCardBuilder extends BaseBuilder { private String cardId; public WxCardBuilder() { - this.msgType = WxConsts.CUSTOM_MSG_WXCARD; + this.msgType = WxConsts.KefuMsgType.WXCARD; } public WxCardBuilder cardId(String cardId) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxMsgMenuBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxMsgMenuBuilder.java new file mode 100644 index 0000000000..ac3edc236e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxMsgMenuBuilder.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.mp.builder.kefu; + +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 菜单消息builder + *
    + * 用法:
    + * WxMpKefuMessage m = WxMpKefuMessage.MSGMENU().addMenus(lists).headContent(headContent).tailContent(tailContent).toUser(...).build();
    + * 
    + * + * @author billytomato + */ +public final class WxMsgMenuBuilder extends BaseBuilder { + private List msgMenus = new ArrayList<>(); + private String headContent; + private String tailContent; + + + public WxMsgMenuBuilder() { + this.msgType = WxConsts.KefuMsgType.MSGMENU; + } + + public WxMsgMenuBuilder addMenus(WxMpKefuMessage.MsgMenu... msgMenus) { + Collections.addAll(this.msgMenus, msgMenus); + return this; + } + + public WxMsgMenuBuilder msgMenus(List msgMenus) { + this.msgMenus = msgMenus; + return this; + } + + public WxMsgMenuBuilder headContent(String headContent) { + this.headContent = headContent; + return this; + } + + public WxMsgMenuBuilder tailContent(String tailContent) { + this.tailContent = tailContent; + return this; + } + + @Override + public WxMpKefuMessage build() { + WxMpKefuMessage m = super.build(); + m.setHeadContent(this.headContent); + m.setTailContent(this.tailContent); + m.setMsgMenus(this.msgMenus); + return m; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/BaseBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/BaseBuilder.java index 60cf675d66..741bffae66 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/BaseBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/BaseBuilder.java @@ -9,13 +9,13 @@ public abstract class BaseBuilder { protected String fromUserName; @SuppressWarnings("unchecked") - public BuilderType toUser(String touser) { + public BuilderType toUser(String touser) { this.toUserName = touser; return (BuilderType) this; } @SuppressWarnings("unchecked") - public BuilderType fromUser(String fromusername) { + public BuilderType fromUser(String fromusername) { this.fromUserName = fromusername; return (BuilderType) this; } @@ -25,7 +25,7 @@ public BuilderType fromUser(String fromusername) { public void setCommon(WxMpXmlOutMessage m) { m.setToUserName(this.toUserName); m.setFromUserName(this.fromUserName); - m.setCreateTime(System.currentTimeMillis() / 1000l); + m.setCreateTime(System.currentTimeMillis() / 1000L); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/DeviceBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/DeviceBuilder.java new file mode 100644 index 0000000000..664c2e3a02 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/DeviceBuilder.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.mp.builder.outxml; + +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutDeviceMessage; + +/** + * 设备消息 Builder + * @author biggates + * @see https://iot.weixin.qq.com/wiki/new/index.html?page=3-4-2 + */ +public final class DeviceBuilder extends BaseBuilder { + + private String deviceId; + private String deviceType; + private String content; + private String sessionId; + + public DeviceBuilder deviceType(String deviceType) { + this.deviceType = deviceType; + return this; + } + + public DeviceBuilder deviceId(String deviceId) { + this.deviceId = deviceId; + return this; + } + + public DeviceBuilder content(String content) { + this.content = content; + return this; + } + + public DeviceBuilder sessionId(String sessionId) { + this.sessionId = sessionId; + return this; + } + + @Override + public WxMpXmlOutDeviceMessage build() { + WxMpXmlOutDeviceMessage m = new WxMpXmlOutDeviceMessage(); + setCommon(m); + m.setDeviceId(this.deviceId); + m.setDeviceType(this.deviceType); + m.setContent(this.content); + m.setSessionId(this.sessionId); + return m; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/ImageBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/ImageBuilder.java index 0bf441bf9d..6ce0ce9127 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/ImageBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/ImageBuilder.java @@ -4,6 +4,7 @@ /** * 图片消息builder + * * @author chanjarster */ public final class ImageBuilder extends BaseBuilder { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java index 1936c5f59a..01af0d627f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java @@ -3,25 +3,31 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * 图文消息builder + * * @author chanjarster */ public final class NewsBuilder extends BaseBuilder { + private List articles = new ArrayList<>(); - protected final List articles = new ArrayList<>(); + public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item... items) { + Collections.addAll(this.articles, items); + return this; + } - public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item item) { - this.articles.add(item); + public NewsBuilder articles(List articles){ + this.articles = articles; return this; } @Override public WxMpXmlOutNewsMessage build() { WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); - for(WxMpXmlOutNewsMessage.Item item : this.articles) { + for (WxMpXmlOutNewsMessage.Item item : this.articles) { m.addArticle(item); } setCommon(m); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TextBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TextBuilder.java index c721a7254c..96674d7426 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TextBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TextBuilder.java @@ -4,8 +4,8 @@ /** * 文本消息builder - * @author chanjarster * + * @author chanjarster */ public final class TextBuilder extends BaseBuilder { private String content; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TransferCustomerServiceBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TransferCustomerServiceBuilder.java index f025c7a816..8821608dae 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TransferCustomerServiceBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TransferCustomerServiceBuilder.java @@ -1,17 +1,19 @@ package me.chanjar.weixin.mp.builder.outxml; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage; import org.apache.commons.lang3.StringUtils; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage; + /** * 客服消息builder *
    - * 用法: WxMpKefuMessage m = WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE().content(...).toUser(...).build();
    + * 用法: WxMpXmlOutTransferKefuMessage m = WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE().kfAccount("").toUser("").build();
      * 
    * * @author chanjarster */ -public final class TransferCustomerServiceBuilder extends BaseBuilder { +public final class TransferCustomerServiceBuilder + extends BaseBuilder { private String kfAccount; public TransferCustomerServiceBuilder kfAccount(String kf) { @@ -23,11 +25,12 @@ public TransferCustomerServiceBuilder kfAccount(String kf) { public WxMpXmlOutTransferKefuMessage build() { WxMpXmlOutTransferKefuMessage m = new WxMpXmlOutTransferKefuMessage(); setCommon(m); - if(StringUtils.isNotBlank(this.kfAccount)){ + if (StringUtils.isNotBlank(this.kfAccount)) { WxMpXmlOutTransferKefuMessage.TransInfo transInfo = new WxMpXmlOutTransferKefuMessage.TransInfo(); transInfo.setKfAccount(this.kfAccount); m.setTransInfo(transInfo); } + return m; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VideoBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VideoBuilder.java index d8ac721eb1..9a965f5b78 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VideoBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VideoBuilder.java @@ -4,8 +4,8 @@ /** * 视频消息builder - * @author chanjarster * + * @author chanjarster */ public final class VideoBuilder extends BaseBuilder { @@ -17,10 +17,12 @@ public VideoBuilder title(String title) { this.title = title; return this; } + public VideoBuilder description(String description) { this.description = description; return this; } + public VideoBuilder mediaId(String mediaId) { this.mediaId = mediaId; return this; @@ -30,9 +32,9 @@ public VideoBuilder mediaId(String mediaId) { public WxMpXmlOutVideoMessage build() { WxMpXmlOutVideoMessage m = new WxMpXmlOutVideoMessage(); setCommon(m); - m.setTitle(this.title); - m.setDescription(this.description); - m.setMediaId(this.mediaId); + m.getVideo().setTitle(this.title); + m.getVideo().setDescription(this.description); + m.getVideo().setMediaId(this.mediaId); return m; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VoiceBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VoiceBuilder.java index 57d7d31e3e..c9e06bdfaf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VoiceBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VoiceBuilder.java @@ -4,6 +4,7 @@ /** * 语音消息builder + * * @author chanjarster */ public final class VoiceBuilder extends BaseBuilder { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/WxMpConfigStorage.java new file mode 100644 index 0000000000..88520adf6f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/WxMpConfigStorage.java @@ -0,0 +1,210 @@ +package me.chanjar.weixin.mp.config; + +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.mp.bean.WxMpHostConfig; + +import java.io.File; +import java.util.concurrent.locks.Lock; + +/** + * 微信客户端配置存储. + * + * @author chanjarster + */ +public interface WxMpConfigStorage { + /** + * Gets access token. + * + * @return the access token + */ + String getAccessToken(); + + /** + * Gets access token lock. + * + * @return the access token lock + */ + Lock getAccessTokenLock(); + + /** + * Is access token expired boolean. + * + * @return the boolean + */ + boolean isAccessTokenExpired(); + + /** + * 强制将access token过期掉. + */ + void expireAccessToken(); + + /** + * 应该是线程安全的. + * + * @param accessToken 要更新的WxAccessToken对象 + */ + void updateAccessToken(WxAccessToken accessToken); + + /** + * 应该是线程安全的. + * + * @param accessToken 新的accessToken值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateAccessToken(String accessToken, int expiresInSeconds); + + /** + * Gets ticket. + * + * @param type the type + * @return the ticket + */ + String getTicket(TicketType type); + + /** + * Gets ticket lock. + * + * @param type the type + * @return the ticket lock + */ + Lock getTicketLock(TicketType type); + + /** + * Is ticket expired boolean. + * + * @param type the type + * @return the boolean + */ + boolean isTicketExpired(TicketType type); + + /** + * 强制将ticket过期掉. + * + * @param type the type + */ + void expireTicket(TicketType type); + + /** + * 更新ticket. + * 应该是线程安全的 + * + * @param type ticket类型 + * @param ticket 新的ticket值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateTicket(TicketType type, String ticket, int expiresInSeconds); + + /** + * Gets app id. + * + * @return the app id + */ + String getAppId(); + + /** + * Gets secret. + * + * @return the secret + */ + String getSecret(); + + /** + * Gets token. + * + * @return the token + */ + String getToken(); + + /** + * Gets aes key. + * + * @return the aes key + */ + String getAesKey(); + + /** + * Gets template id. + * + * @return the template id + */ + String getTemplateId(); + + /** + * Gets expires time. + * + * @return the expires time + */ + long getExpiresTime(); + + /** + * Gets oauth 2 redirect uri. + * + * @return the oauth 2 redirect uri + */ + String getOauth2redirectUri(); + + /** + * Gets http proxy host. + * + * @return the http proxy host + */ + String getHttpProxyHost(); + + /** + * Gets http proxy port. + * + * @return the http proxy port + */ + int getHttpProxyPort(); + + /** + * Gets http proxy username. + * + * @return the http proxy username + */ + String getHttpProxyUsername(); + + /** + * Gets http proxy password. + * + * @return the http proxy password + */ + String getHttpProxyPassword(); + + /** + * Gets tmp dir file. + * + * @return the tmp dir file + */ + File getTmpDirFile(); + + /** + * http client builder. + * + * @return ApacheHttpClientBuilder apache http client builder + */ + ApacheHttpClientBuilder getApacheHttpClientBuilder(); + + /** + * 是否自动刷新token. + * + * @return the boolean + */ + boolean autoRefreshToken(); + + /** + * 得到微信接口地址域名部分的自定义设置信息. + * + * @return the host config + */ + WxMpHostConfig getHostConfig(); + + /** + * 设置微信接口地址域名部分的自定义设置信息. + * + * @param hostConfig host config + */ + void setHostConfig(WxMpHostConfig hostConfig); +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpDefaultConfigImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpDefaultConfigImpl.java new file mode 100644 index 0000000000..637b7b5576 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpDefaultConfigImpl.java @@ -0,0 +1,196 @@ +package me.chanjar.weixin.mp.config.impl; + +import lombok.Data; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.mp.bean.WxMpHostConfig; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.File; +import java.io.Serializable; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化. + * + * @author chanjarster + */ +@Data +public class WxMpDefaultConfigImpl implements WxMpConfigStorage, Serializable { + private static final long serialVersionUID = -6646519023303395185L; + + protected volatile String appId; + protected volatile String secret; + protected volatile String token; + protected volatile String templateId; + protected volatile String accessToken; + protected volatile String aesKey; + protected volatile long expiresTime; + + protected volatile String oauth2redirectUri; + + protected volatile String httpProxyHost; + protected volatile int httpProxyPort; + protected volatile String httpProxyUsername; + protected volatile String httpProxyPassword; + + protected volatile String jsapiTicket; + protected volatile long jsapiTicketExpiresTime; + + protected volatile String sdkTicket; + protected volatile long sdkTicketExpiresTime; + + protected volatile String cardApiTicket; + protected volatile long cardApiTicketExpiresTime; + + protected volatile Lock accessTokenLock = new ReentrantLock(); + protected volatile Lock jsapiTicketLock = new ReentrantLock(); + protected volatile Lock sdkTicketLock = new ReentrantLock(); + protected volatile Lock cardApiTicketLock = new ReentrantLock(); + + protected volatile File tmpDirFile; + + protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder; + + private WxMpHostConfig hostConfig = null; + + @Override + public boolean isAccessTokenExpired() { + return System.currentTimeMillis() > this.expiresTime; + } + + @Override + public synchronized void updateAccessToken(WxAccessToken accessToken) { + updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + this.accessToken = accessToken; + this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + + @Override + public void expireAccessToken() { + this.expiresTime = 0; + } + + @Override + public String getTicket(TicketType type) { + switch (type) { + case SDK: + return this.sdkTicket; + case JSAPI: + return this.jsapiTicket; + case WX_CARD: + return this.cardApiTicket; + default: + return null; + } + } + + public void setTicket(TicketType type, String ticket) { + switch (type) { + case JSAPI: + this.jsapiTicket = ticket; + break; + case WX_CARD: + this.cardApiTicket = ticket; + break; + case SDK: + this.sdkTicket = ticket; + break; + default: + } + } + + @Override + public Lock getTicketLock(TicketType type) { + switch (type) { + case SDK: + return this.sdkTicketLock; + case JSAPI: + return this.jsapiTicketLock; + case WX_CARD: + return this.cardApiTicketLock; + default: + return null; + } + } + + @Override + public boolean isTicketExpired(TicketType type) { + switch (type) { + case SDK: + return System.currentTimeMillis() > this.sdkTicketExpiresTime; + case JSAPI: + return System.currentTimeMillis() > this.jsapiTicketExpiresTime; + case WX_CARD: + return System.currentTimeMillis() > this.cardApiTicketExpiresTime; + default: + return false; + } + } + + @Override + public synchronized void updateTicket(TicketType type, String ticket, int expiresInSeconds) { + switch (type) { + case JSAPI: + this.jsapiTicket = ticket; + // 预留200秒的时间 + this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + break; + case WX_CARD: + this.cardApiTicket = ticket; + // 预留200秒的时间 + this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + break; + case SDK: + this.sdkTicket = ticket; + // 预留200秒的时间 + this.sdkTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + break; + default: + } + } + + @Override + public void expireTicket(TicketType type) { + switch (type) { + case JSAPI: + this.jsapiTicketExpiresTime = 0; + break; + case WX_CARD: + this.cardApiTicketExpiresTime = 0; + break; + case SDK: + this.sdkTicketExpiresTime = 0; + break; + default: + } + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + @Override + public boolean autoRefreshToken() { + return true; + } + + @Override + public WxMpHostConfig getHostConfig() { + return this.hostConfig; + } + + @Override + public void setHostConfig(WxMpHostConfig hostConfig) { + this.hostConfig = hostConfig; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java new file mode 100644 index 0000000000..7f54eb0bb9 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java @@ -0,0 +1,99 @@ +package me.chanjar.weixin.mp.config.impl; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.common.redis.WxRedisOps; + +import java.util.concurrent.TimeUnit; + +/** + * 基于Redis的微信配置provider. + * + *
    + *    使用说明:本实现仅供参考,并不完整,
    + *    比如为减少项目依赖,未加入redis分布式锁的实现,如有需要请自行实现。
    + * 
    + * + * @author nickwong + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxMpRedisConfigImpl extends WxMpDefaultConfigImpl { + private static final long serialVersionUID = -988502871997239733L; + + private static final String ACCESS_TOKEN_KEY_TPL = "%s:access_token:%s"; + private static final String TICKET_KEY_TPL = "%s:ticket:key:%s:%s"; + private static final String LOCK_KEY_TPL = "%s:lock:%s:"; + + private final WxRedisOps redisOps; + private final String keyPrefix; + + private String accessTokenKey; + private String lockKey; + + public WxMpRedisConfigImpl(WxRedisOps redisOps, String keyPrefix) { + this.redisOps = redisOps; + this.keyPrefix = keyPrefix; + } + + /** + * 每个公众号生成独有的存储key. + */ + @Override + public void setAppId(String appId) { + super.setAppId(appId); + this.accessTokenKey = String.format(ACCESS_TOKEN_KEY_TPL, this.keyPrefix, appId); + this.lockKey = String.format(LOCK_KEY_TPL, this.keyPrefix, appId); + accessTokenLock = this.redisOps.getLock(lockKey.concat("accessTokenLock")); + jsapiTicketLock = this.redisOps.getLock(lockKey.concat("jsapiTicketLock")); + sdkTicketLock = this.redisOps.getLock(lockKey.concat("sdkTicketLock")); + cardApiTicketLock = this.redisOps.getLock(lockKey.concat("cardApiTicketLock")); + } + + private String getTicketRedisKey(TicketType type) { + return String.format(TICKET_KEY_TPL, this.keyPrefix, appId, type.getCode()); + } + + @Override + public String getAccessToken() { + return redisOps.getValue(this.accessTokenKey); + } + + @Override + public boolean isAccessTokenExpired() { + Long expire = redisOps.getExpire(this.accessTokenKey); + return expire == null || expire < 2; + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + redisOps.setValue(this.accessTokenKey, accessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public void expireAccessToken() { + redisOps.expire(this.accessTokenKey, 0, TimeUnit.SECONDS); + } + + @Override + public String getTicket(TicketType type) { + return redisOps.getValue(this.getTicketRedisKey(type)); + } + + @Override + public boolean isTicketExpired(TicketType type) { + return redisOps.getExpire(this.getTicketRedisKey(type)) < 2; + } + + @Override + public synchronized void updateTicket(TicketType type, String jsapiTicket, int expiresInSeconds) { + redisOps.setValue(this.getTicketRedisKey(type), jsapiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public void expireTicket(TicketType type) { + redisOps.expire(this.getTicketRedisKey(type), 0, TimeUnit.SECONDS); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedissonConfigImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedissonConfigImpl.java new file mode 100644 index 0000000000..f1aa6b9ca7 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedissonConfigImpl.java @@ -0,0 +1,101 @@ +package me.chanjar.weixin.mp.config.impl; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.common.redis.RedissonWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; + +/** + * @author wuxingye + * @date 2020/6/12 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class WxMpRedissonConfigImpl extends WxMpDefaultConfigImpl { + + private static final long serialVersionUID = -5139855123878455556L; + private static final String ACCESS_TOKEN_KEY_TPL = "%s:access_token:%s"; + private static final String TICKET_KEY_TPL = "%s:ticket:key:%s:%s"; + private static final String LOCK_KEY_TPL = "%s:lock:%s:"; + private final WxRedisOps redisOps; + private final String keyPrefix; + private String accessTokenKey; + private String lockKey; + + public WxMpRedissonConfigImpl(@NonNull RedissonClient redissonClient, String keyPrefix) { + this(new RedissonWxRedisOps(redissonClient), keyPrefix); + } + + public WxMpRedissonConfigImpl(@NonNull RedissonClient redissonClient) { + this(redissonClient, null); + } + + private WxMpRedissonConfigImpl(@NonNull WxRedisOps redisOps, String keyPrefix) { + this.redisOps = redisOps; + this.keyPrefix = keyPrefix; + } + + /** + * 每个公众号生成独有的存储key. + */ + @Override + public void setAppId(String appId) { + super.setAppId(appId); + this.accessTokenKey = String.format(ACCESS_TOKEN_KEY_TPL, this.keyPrefix, appId); + this.lockKey = String.format(LOCK_KEY_TPL, this.keyPrefix, appId); + accessTokenLock = this.redisOps.getLock(lockKey.concat("accessTokenLock")); + jsapiTicketLock = this.redisOps.getLock(lockKey.concat("jsapiTicketLock")); + sdkTicketLock = this.redisOps.getLock(lockKey.concat("sdkTicketLock")); + cardApiTicketLock = this.redisOps.getLock(lockKey.concat("cardApiTicketLock")); + } + + private String getTicketRedisKey(TicketType type) { + return String.format(TICKET_KEY_TPL, this.keyPrefix, appId, type.getCode()); + } + + @Override + public String getAccessToken() { + return redisOps.getValue(this.accessTokenKey); + } + + @Override + public boolean isAccessTokenExpired() { + Long expire = redisOps.getExpire(this.accessTokenKey); + return expire == null || expire < 2; + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + redisOps.setValue(this.accessTokenKey, accessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public void expireAccessToken() { + redisOps.expire(this.accessTokenKey, 0, TimeUnit.SECONDS); + } + + @Override + public String getTicket(TicketType type) { + return redisOps.getValue(this.getTicketRedisKey(type)); + } + + @Override + public boolean isTicketExpired(TicketType type) { + return redisOps.getExpire(this.getTicketRedisKey(type)) < 2; + } + + @Override + public synchronized void updateTicket(TicketType type, String jsapiTicket, int expiresInSeconds) { + redisOps.setValue(this.getTicketRedisKey(type), jsapiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public void expireTicket(TicketType type) { + redisOps.expire(this.getTicketRedisKey(type), 0, TimeUnit.SECONDS); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java new file mode 100644 index 0000000000..4d7ef4beb5 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java @@ -0,0 +1,144 @@ +package me.chanjar.weixin.mp.constant; + +/** + *
    + * 微信公众号事件的相关常量
    + * Created by Binary Wang on 2017-5-10.
    + * 
    + * + * @author Binary Wang + */ +public class WxMpEventConstants { + /** + * 门店审核事件. + */ + public static final String POI_CHECK_NOTIFY = "poi_check_notify"; + + /** + * 接收会员信息事件. + */ + public static final String SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; + + /** + * 微信摇一摇周边>>摇一摇事件通知. + */ + public static final String SHAKEAROUND_USER_SHAKE = "ShakearoundUserShake"; + + + /** + * 卡券相关事件. + */ + public static class Card { + public static final String CARD_PASS_CHECK = "card_pass_check"; + public static final String CARD_NOT_PASS_CHECK = "card_not_pass_check"; + public static final String USER_GET_CARD = "user_get_card"; + public static final String USER_DEL_CARD = "user_del_card"; + public static final String USER_CONSUME_CARD = "user_consume_card"; + public static final String USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell"; + public static final String USER_VIEW_CARD = "user_view_card"; + public static final String USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; + + /** + * 卡券转赠事件. + */ + public static final String USER_GIFTING_CARD = "user_gifting_card"; + + /** + * 库存报警. + */ + public static final String CARD_SKU_REMIND = "card_sku_remind"; + + /** + * 会员卡内容更新事件. + */ + public static final String UPDATE_MEMBER_CARD = "update_member_card"; + + /** + * 券点流水详情事件. + */ + public static final String CARD_PAY_ORDER = "card_pay_order"; + + /** + * 用户购买礼品卡付款成功事件. + */ + public static final String GIFTCARD_PAY_DONE = "giftcard_pay_done"; + + /** + * 用户购买后赠送事件. + */ + public static final String GIFTCARD_SEND_TO_FRIEND = "giftcard_send_to_friend"; + + /** + * 用户领取礼品卡成功事件. + */ + public static final String GIFTCARD_USER_ACCEPT = "giftcard_user_accept"; + } + + + /** + * 客服相关事件. + */ + public static class CustomerService { + /** + * 客服接入会话. + */ + public static final String KF_CREATE_SESSION = "kf_create_session"; + + /** + * 客服关闭会话. + */ + public static final String KF_CLOSE_SESSION = "kf_close_session"; + + /** + * 客服转接会话. + */ + public static final String KF_SWITCH_SESSION = "kf_switch_session"; + } + + /** + * 微信认证事件. + */ + public static class Qualification { + + /** + * 资质认证成功. + */ + public static final String QUALIFICATION_VERIFY_SUCCESS = "qualification_verify_success"; + /** + * 资质认证失败. + */ + public static final String QUALIFICATION_VERIFY_FAIL = "qualification_verify_fail"; + /** + * 名称认证成功. + */ + public static final String NAMING_VERIFY_SUCCESS = "naming_verify_success"; + /** + * 名称认证失败. + */ + public static final String NAMING_VERIFY_FAIL = "naming_verify_fail"; + /** + * 年审通知. + */ + public static final String ANNUAL_RENEW = "annual_renew"; + /** + * 认证过期失效通知. + */ + public static final String VERIFY_EXPIRED = "verify_expired"; + } + + /** + * 电子发票. + */ + public static class Invoice { + /** + * 用户授权事件. + */ + public static final String USER_AUTHORIZE_INVOICE = "user_authorize_invoice"; + + /** + * 统一开票接口-异步通知开票结果. + */ + public static final String CLOUD_INVOICE_INVOICERESULT_EVENT = "cloud_invoice_invoiceresult_event"; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/AiLangType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/AiLangType.java new file mode 100644 index 0000000000..b37772b01a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/AiLangType.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.mp.enums; + +import lombok.Getter; + +/** + *
    + *  AI开放接口里的语言类型,目前只支持两种:中文和英文
    + *  Created by BinaryWang on 2018/6/10.
    + * 
    + * + * @author Binary Wang + */ +@Getter +public enum AiLangType { + /** + * 中文 汉语. + */ + zh_CN("zh_CN"), + /** + * 英文 英语. + */ + en_US("en_US"); + + private String code; + + AiLangType(String code) { + this.code = code; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxCardType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxCardType.java new file mode 100644 index 0000000000..46cc43f887 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxCardType.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.mp.enums; + +/** + * 微信卡券类型. + * + * @author chenyixin + * @date 2019-09-07 23:33 + **/ +public enum WxCardType { + MEMBER_CARD("MEMBER_CARD"), + GROUPON("GROUPON"), + CASH("CASH"), + DISCOUNT("DISCOUNT"), + GIFT("GIFT"), + GENERAL_COUPON("GENERAL_COUPON"); + + private String code; + + WxCardType(String code) { + this.code = code; + } + + public String getCode() { + return code; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxMpApiUrl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxMpApiUrl.java new file mode 100644 index 0000000000..7e10867658 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxMpApiUrl.java @@ -0,0 +1,1159 @@ +package me.chanjar.weixin.mp.enums; + +import lombok.AllArgsConstructor; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; + +import static me.chanjar.weixin.mp.bean.WxMpHostConfig.*; + +/** + *
    + *  公众号接口api地址
    + *  Created by BinaryWang on 2019-06-03.
    + * 
    + * + * @author Binary Wang + */ +public interface WxMpApiUrl { + + /** + * 得到api完整地址. + * + * @param config 微信公众号配置 + * @return api地址 + */ + String getUrl(WxMpConfigStorage config); + + @AllArgsConstructor + enum Device implements WxMpApiUrl { + /** + * get_bind_device. + */ + DEVICE_GET_BIND_DEVICE(API_DEFAULT_HOST_URL, "/device/get_bind_device"), + /** + * get_openid. + */ + DEVICE_GET_OPENID(API_DEFAULT_HOST_URL, "/device/get_openid"), + /** + * compel_unbind. + */ + DEVICE_COMPEL_UNBIND(API_DEFAULT_HOST_URL, "/device/compel_unbind?"), + /** + * unbind. + */ + DEVICE_UNBIND(API_DEFAULT_HOST_URL, "/device/unbind?"), + /** + * compel_bind. + */ + DEVICE_COMPEL_BIND(API_DEFAULT_HOST_URL, "/device/compel_bind"), + /** + * bind. + */ + DEVICE_BIND(API_DEFAULT_HOST_URL, "/device/bind"), + /** + * authorize_device. + */ + DEVICE_AUTHORIZE_DEVICE(API_DEFAULT_HOST_URL, "/device/authorize_device"), + /** + * getqrcode. + */ + DEVICE_GETQRCODE(API_DEFAULT_HOST_URL, "/device/getqrcode"), + /** + * transmsg. + */ + DEVICE_TRANSMSG(API_DEFAULT_HOST_URL, "/device/transmsg"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + + } + + @AllArgsConstructor + enum Other implements WxMpApiUrl { + /** + * 获取access_token. + */ + GET_ACCESS_TOKEN_URL(API_DEFAULT_HOST_URL, "/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"), + /** + * 获得各种类型的ticket. + */ + GET_TICKET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/ticket/getticket?type="), + /** + * 长链接转短链接接口. + */ + SHORTURL_API_URL(API_DEFAULT_HOST_URL, "/cgi-bin/shorturl"), + /** + * 语义查询接口. + */ + SEMANTIC_SEMPROXY_SEARCH_URL(API_DEFAULT_HOST_URL, "/semantic/semproxy/search"), + /** + * 用code换取oauth2的access token. + */ + OAUTH2_ACCESS_TOKEN_URL(API_DEFAULT_HOST_URL, "/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"), + /** + * 刷新oauth2的access token. + */ + OAUTH2_REFRESH_TOKEN_URL(API_DEFAULT_HOST_URL, "/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s"), + /** + * 用oauth2获取用户信息. + */ + OAUTH2_USERINFO_URL(API_DEFAULT_HOST_URL, "/sns/userinfo?access_token=%s&openid=%s&lang=%s"), + /** + * 验证oauth2的access token是否有效. + */ + OAUTH2_VALIDATE_TOKEN_URL(API_DEFAULT_HOST_URL, "/sns/auth?access_token=%s&openid=%s"), + /** + * 获取微信服务器IP地址. + */ + GET_CALLBACK_IP_URL(API_DEFAULT_HOST_URL, "/cgi-bin/getcallbackip"), + /** + * 网络检测. + */ + NETCHECK_URL(API_DEFAULT_HOST_URL, "/cgi-bin/callback/check"), + /** + * 第三方使用网站应用授权登录的url. + */ + QRCONNECT_URL(OPEN_DEFAULT_HOST_URL, "/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"), + /** + * oauth2授权的url连接. + */ + CONNECT_OAUTH2_AUTHORIZE_URL(OPEN_DEFAULT_HOST_URL, "/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&connect_redirect=1#wechat_redirect"), + /** + * 获取公众号的自动回复规则. + */ + GET_CURRENT_AUTOREPLY_INFO_URL(API_DEFAULT_HOST_URL, "/cgi-bin/get_current_autoreply_info"), + /** + * 公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零. + */ + CLEAR_QUOTA_URL(API_DEFAULT_HOST_URL, "/cgi-bin/clear_quota"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Marketing implements WxMpApiUrl { + /** + * sets add. + */ + USER_ACTION_SETS_ADD(API_DEFAULT_HOST_URL, "/marketing/user_action_sets/add?version=v1.0"), + /** + * get. + */ + USER_ACTION_SETS_GET(API_DEFAULT_HOST_URL, "/marketing/user_action_sets/get"), + /** + * add. + */ + USER_ACTIONS_ADD(API_DEFAULT_HOST_URL, "/marketing/user_actions/add?version=v1.0"), + /** + * get. + */ + WECHAT_AD_LEADS_GET(API_DEFAULT_HOST_URL, "/marketing/wechat_ad_leads/get"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Menu implements WxMpApiUrl { + /** + * get_current_selfmenu_info. + */ + GET_CURRENT_SELFMENU_INFO(API_DEFAULT_HOST_URL, "/cgi-bin/get_current_selfmenu_info"), + /** + * trymatch. + */ + MENU_TRYMATCH(API_DEFAULT_HOST_URL, "/cgi-bin/menu/trymatch"), + /** + * get. + */ + MENU_GET(API_DEFAULT_HOST_URL, "/cgi-bin/menu/get"), + /** + * delconditional. + */ + MENU_DELCONDITIONAL(API_DEFAULT_HOST_URL, "/cgi-bin/menu/delconditional"), + /** + * delete. + */ + MENU_DELETE(API_DEFAULT_HOST_URL, "/cgi-bin/menu/delete"), + /** + * create. + */ + MENU_CREATE(API_DEFAULT_HOST_URL, "/cgi-bin/menu/create"), + /** + * addconditional. + */ + MENU_ADDCONDITIONAL(API_DEFAULT_HOST_URL, "/cgi-bin/menu/addconditional"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + + @AllArgsConstructor + enum Qrcode implements WxMpApiUrl { + /** + * create. + */ + QRCODE_CREATE(API_DEFAULT_HOST_URL, "/cgi-bin/qrcode/create"), + /** + * showqrcode. + */ + SHOW_QRCODE(MP_DEFAULT_HOST_URL, "/cgi-bin/showqrcode"), + /** + * showqrcode. + */ + SHOW_QRCODE_WITH_TICKET(MP_DEFAULT_HOST_URL, "/cgi-bin/showqrcode?ticket=%s"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum ShakeAround implements WxMpApiUrl { + /** + * getshakeinfo. + */ + SHAKEAROUND_USER_GETSHAKEINFO(API_DEFAULT_HOST_URL, "/shakearound/user/getshakeinfo"), + /** + * add. + */ + SHAKEAROUND_PAGE_ADD(API_DEFAULT_HOST_URL, "/shakearound/page/add"), + /** + * bindpage. + */ + SHAKEAROUND_DEVICE_BINDPAGE(API_DEFAULT_HOST_URL, "/shakearound/device/bindpage"), + /** + * search. + */ + SHAKEAROUND_RELATION_SEARCH(API_DEFAULT_HOST_URL, "/shakearound/relation/search"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum SubscribeMsg implements WxMpApiUrl { + /** + * subscribemsg. + */ + SUBSCRIBE_MESSAGE_AUTHORIZE_URL(MP_DEFAULT_HOST_URL, "/mp/subscribemsg?action=get_confirm&appid=%s&scene=%d&template_id=%s&redirect_url=%s&reserved=%s#wechat_redirect"), + /** + * subscribe. + */ + SEND_MESSAGE_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/template/subscribe"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum TemplateMsg implements WxMpApiUrl { + /** + * send. + */ + MESSAGE_TEMPLATE_SEND(API_DEFAULT_HOST_URL, "/cgi-bin/message/template/send"), + /** + * api_set_industry. + */ + TEMPLATE_API_SET_INDUSTRY(API_DEFAULT_HOST_URL, "/cgi-bin/template/api_set_industry"), + /** + * get_industry. + */ + TEMPLATE_GET_INDUSTRY(API_DEFAULT_HOST_URL, "/cgi-bin/template/get_industry"), + /** + * api_add_template. + */ + TEMPLATE_API_ADD_TEMPLATE(API_DEFAULT_HOST_URL, "/cgi-bin/template/api_add_template"), + /** + * get_all_private_template. + */ + TEMPLATE_GET_ALL_PRIVATE_TEMPLATE(API_DEFAULT_HOST_URL, "/cgi-bin/template/get_all_private_template"), + /** + * del_private_template. + */ + TEMPLATE_DEL_PRIVATE_TEMPLATE(API_DEFAULT_HOST_URL, "/cgi-bin/template/del_private_template"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum UserBlacklist implements WxMpApiUrl { + /** + * getblacklist. + */ + GETBLACKLIST(API_DEFAULT_HOST_URL, "/cgi-bin/tags/members/getblacklist"), + /** + * batchblacklist. + */ + BATCHBLACKLIST(API_DEFAULT_HOST_URL, "/cgi-bin/tags/members/batchblacklist"), + /** + * batchunblacklist. + */ + BATCHUNBLACKLIST(API_DEFAULT_HOST_URL, "/cgi-bin/tags/members/batchunblacklist"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum UserTag implements WxMpApiUrl { + /** + * create. + */ + TAGS_CREATE(API_DEFAULT_HOST_URL, "/cgi-bin/tags/create"), + /** + * get. + */ + TAGS_GET(API_DEFAULT_HOST_URL, "/cgi-bin/tags/get"), + /** + * update. + */ + TAGS_UPDATE(API_DEFAULT_HOST_URL, "/cgi-bin/tags/update"), + /** + * delete. + */ + TAGS_DELETE(API_DEFAULT_HOST_URL, "/cgi-bin/tags/delete"), + /** + * get. + */ + TAG_GET(API_DEFAULT_HOST_URL, "/cgi-bin/user/tag/get"), + /** + * batchtagging. + */ + TAGS_MEMBERS_BATCHTAGGING(API_DEFAULT_HOST_URL, "/cgi-bin/tags/members/batchtagging"), + /** + * batchuntagging. + */ + TAGS_MEMBERS_BATCHUNTAGGING(API_DEFAULT_HOST_URL, "/cgi-bin/tags/members/batchuntagging"), + /** + * getidlist. + */ + TAGS_GETIDLIST(API_DEFAULT_HOST_URL, "/cgi-bin/tags/getidlist"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Wifi implements WxMpApiUrl { + /** + * list. + */ + BIZWIFI_SHOP_LIST(API_DEFAULT_HOST_URL, "/bizwifi/shop/list"), + + /** + * get. + */ + BIZWIFI_SHOP_GET(API_DEFAULT_HOST_URL, "/bizwifi/shop/get"), + + /** + * upadte. + */ + BIZWIFI_SHOP_UPDATE(API_DEFAULT_HOST_URL, "/bizwifi/shop/update"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum AiOpen implements WxMpApiUrl { + /** + * translatecontent. + */ + TRANSLATE_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/voice/translatecontent?lfrom=%s<o=%s"), + /** + * addvoicetorecofortext. + */ + VOICE_UPLOAD_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/voice/addvoicetorecofortext?format=%s&voice_id=%s&lang=%s"), + /** + * queryrecoresultfortext. + */ + VOICE_QUERY_RESULT_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/voice/queryrecoresultfortext"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Ocr implements WxMpApiUrl { + /** + * 身份证识别. + */ + IDCARD(API_DEFAULT_HOST_URL, "/cv/ocr/idcard?img_url=%s"), + + FILEIDCARD(API_DEFAULT_HOST_URL, "/cv/ocr/idcard"), + + /** + * 银行卡OCR识别 + */ + BANK_CARD(API_DEFAULT_HOST_URL, "/cv/ocr/bankcard?img_url=%s"), + + /** + * 银行卡OCR识别(文件) + */ + FILE_BANK_CARD(API_DEFAULT_HOST_URL, "/cv/ocr/bankcard"), + + /** + * 行驶证OCR识别 + */ + DRIVING(API_DEFAULT_HOST_URL, "/cv/ocr/driving?img_url=%s"), + /** + * 行驶证OCR识别(文件) + */ + FILE_DRIVING(API_DEFAULT_HOST_URL, "/cv/ocr/driving"), + + /** + * 驾驶证OCR识别 + */ + DRIVING_LICENSE(API_DEFAULT_HOST_URL, "/cv/ocr/drivinglicense?img_url=%s"), + + /** + * 驾驶证OCR识别(文件) + */ + FILE_DRIVING_LICENSE(API_DEFAULT_HOST_URL, "/cv/ocr/drivinglicense"), + + /** + * 营业执照OCR识别 + */ + BIZ_LICENSE(API_DEFAULT_HOST_URL, "/cv/ocr/bizlicense?img_url=%s"), + + /** + * 营业执照OCR识别(文件) + */ + FILE_BIZ_LICENSE(API_DEFAULT_HOST_URL, "/cv/ocr/bizlicense"), + + /** + * 通用印刷体OCR识别 + */ + COMM(API_DEFAULT_HOST_URL, "/cv/ocr/comm?img_url=%s"), + + /** + * 通用印刷体OCR识别(文件) + */ + FILE_COMM(API_DEFAULT_HOST_URL, "/cv/ocr/comm"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + if (config == null) { + return buildUrl(null, prefix, path); + } + + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Card implements WxMpApiUrl { + /** + * create. + */ + CARD_CREATE(API_DEFAULT_HOST_URL, "/card/create"), + /** + * get. + */ + CARD_GET(API_DEFAULT_HOST_URL, "/card/get"), + /** + * wx_card. + */ + CARD_GET_TICKET(API_DEFAULT_HOST_URL, "/cgi-bin/ticket/getticket?type=wx_card"), + /** + * decrypt. + */ + CARD_CODE_DECRYPT(API_DEFAULT_HOST_URL, "/card/code/decrypt"), + /** + * get. + */ + CARD_CODE_GET(API_DEFAULT_HOST_URL, "/card/code/get"), + /** + * consume. + */ + CARD_CODE_CONSUME(API_DEFAULT_HOST_URL, "/card/code/consume"), + /** + * mark. + */ + CARD_CODE_MARK(API_DEFAULT_HOST_URL, "/card/code/mark"), + /** + * set. + */ + CARD_TEST_WHITELIST(API_DEFAULT_HOST_URL, "/card/testwhitelist/set"), + /** + * create. + */ + CARD_QRCODE_CREATE(API_DEFAULT_HOST_URL, "/card/qrcode/create"), + /** + * create. + */ + CARD_LANDING_PAGE_CREATE(API_DEFAULT_HOST_URL, "/card/landingpage/create"), + /** + * 将用户的卡券设置为失效状态. + */ + CARD_CODE_UNAVAILABLE(API_DEFAULT_HOST_URL, "/card/code/unavailable"), + /** + * 卡券删除. + */ + CARD_DELETE(API_DEFAULT_HOST_URL, "/card/delete"), + + /** + * 导入code接口. + */ + CARD_CODE_DEPOSIT(API_DEFAULT_HOST_URL, "/card/code/deposit"), + + /** + * 查询导入code数目接口 + */ + CARD_CODE_DEPOSIT_COUNT(API_DEFAULT_HOST_URL, "/card/code/getdepositcount"), + + /** + * 核查code接口 + */ + CARD_CODE_CHECKCODE(API_DEFAULT_HOST_URL, "/card/code/checkcode"), + + /** + * 图文消息群发卡券 + */ + CARD_MPNEWS_GETHTML(API_DEFAULT_HOST_URL, "/card/mpnews/gethtml"), + + /** + * 修改库存接口 + */ + CARD_MODIFY_STOCK(API_DEFAULT_HOST_URL, "/card/modifystock"), + + /** + * 更改Code接口 + */ + CARD_CODE_UPDATE(API_DEFAULT_HOST_URL, "/card/code/update"), + + /** + * 设置买单接口 + */ + CARD_PAYCELL_SET(API_DEFAULT_HOST_URL, "/card/paycell/set"), + + /** + * 设置自助核销接口 + */ + CARD_SELF_CONSUME_CELL_SET(API_DEFAULT_HOST_URL, "/card/selfconsumecell/set"), + + /** + * 获取用户已领取卡券接口 + */ + CARD_USER_CARD_LIST(API_DEFAULT_HOST_URL, "/card/user/getcardlist"), + ; + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum DataCube implements WxMpApiUrl { + /** + * getusersummary. + */ + GET_USER_SUMMARY(API_DEFAULT_HOST_URL, "/datacube/getusersummary"), + /** + * getusercumulate. + */ + GET_USER_CUMULATE(API_DEFAULT_HOST_URL, "/datacube/getusercumulate"), + /** + * getarticlesummary. + */ + GET_ARTICLE_SUMMARY(API_DEFAULT_HOST_URL, "/datacube/getarticlesummary"), + /** + * getarticletotal. + */ + GET_ARTICLE_TOTAL(API_DEFAULT_HOST_URL, "/datacube/getarticletotal"), + /** + * getuserread. + */ + GET_USER_READ(API_DEFAULT_HOST_URL, "/datacube/getuserread"), + /** + * getuserreadhour. + */ + GET_USER_READ_HOUR(API_DEFAULT_HOST_URL, "/datacube/getuserreadhour"), + /** + * getusershare. + */ + GET_USER_SHARE(API_DEFAULT_HOST_URL, "/datacube/getusershare"), + /** + * getusersharehour. + */ + GET_USER_SHARE_HOUR(API_DEFAULT_HOST_URL, "/datacube/getusersharehour"), + /** + * getupstreammsg. + */ + GET_UPSTREAM_MSG(API_DEFAULT_HOST_URL, "/datacube/getupstreammsg"), + /** + * getupstreammsghour. + */ + GET_UPSTREAM_MSG_HOUR(API_DEFAULT_HOST_URL, "/datacube/getupstreammsghour"), + /** + * getupstreammsgweek. + */ + GET_UPSTREAM_MSG_WEEK(API_DEFAULT_HOST_URL, "/datacube/getupstreammsgweek"), + /** + * getupstreammsgmonth. + */ + GET_UPSTREAM_MSG_MONTH(API_DEFAULT_HOST_URL, "/datacube/getupstreammsgmonth"), + /** + * getupstreammsgdist. + */ + GET_UPSTREAM_MSG_DIST(API_DEFAULT_HOST_URL, "/datacube/getupstreammsgdist"), + /** + * getupstreammsgdistweek. + */ + GET_UPSTREAM_MSG_DIST_WEEK(API_DEFAULT_HOST_URL, "/datacube/getupstreammsgdistweek"), + /** + * getupstreammsgdistmonth. + */ + GET_UPSTREAM_MSG_DIST_MONTH(API_DEFAULT_HOST_URL, "/datacube/getupstreammsgdistmonth"), + /** + * getinterfacesummary. + */ + GET_INTERFACE_SUMMARY(API_DEFAULT_HOST_URL, "/datacube/getinterfacesummary"), + /** + * getinterfacesummaryhour. + */ + GET_INTERFACE_SUMMARY_HOUR(API_DEFAULT_HOST_URL, "/datacube/getinterfacesummaryhour"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Kefu implements WxMpApiUrl { + /** + * send. + */ + MESSAGE_CUSTOM_SEND(API_DEFAULT_HOST_URL, "/cgi-bin/message/custom/send"), + /** + * getkflist. + */ + GET_KF_LIST(API_DEFAULT_HOST_URL, "/cgi-bin/customservice/getkflist"), + /** + * getonlinekflist. + */ + GET_ONLINE_KF_LIST(API_DEFAULT_HOST_URL, "/cgi-bin/customservice/getonlinekflist"), + /** + * add. + */ + KFACCOUNT_ADD(API_DEFAULT_HOST_URL, "/customservice/kfaccount/add"), + /** + * update. + */ + KFACCOUNT_UPDATE(API_DEFAULT_HOST_URL, "/customservice/kfaccount/update"), + /** + * inviteworker. + */ + KFACCOUNT_INVITE_WORKER(API_DEFAULT_HOST_URL, "/customservice/kfaccount/inviteworker"), + /** + * uploadheadimg. + */ + KFACCOUNT_UPLOAD_HEAD_IMG(API_DEFAULT_HOST_URL, "/customservice/kfaccount/uploadheadimg?kf_account=%s"), + /** + * del kfaccount. + */ + KFACCOUNT_DEL(API_DEFAULT_HOST_URL, "/customservice/kfaccount/del?kf_account=%s"), + /** + * create. + */ + KFSESSION_CREATE(API_DEFAULT_HOST_URL, "/customservice/kfsession/create"), + /** + * close. + */ + KFSESSION_CLOSE(API_DEFAULT_HOST_URL, "/customservice/kfsession/close"), + /** + * getsession. + */ + KFSESSION_GET_SESSION(API_DEFAULT_HOST_URL, "/customservice/kfsession/getsession?openid=%s"), + /** + * getsessionlist. + */ + KFSESSION_GET_SESSION_LIST(API_DEFAULT_HOST_URL, "/customservice/kfsession/getsessionlist?kf_account=%s"), + /** + * getwaitcase. + */ + KFSESSION_GET_WAIT_CASE(API_DEFAULT_HOST_URL, "/customservice/kfsession/getwaitcase"), + /** + * getmsglist. + */ + MSG_RECORD_LIST(API_DEFAULT_HOST_URL, "/customservice/msgrecord/getmsglist"), + /** + * typing. + */ + CUSTOM_TYPING(API_DEFAULT_HOST_URL, "/cgi-bin/message/custom/typing"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum MassMessage implements WxMpApiUrl { + /** + * 上传群发用的图文消息. + */ + MEDIA_UPLOAD_NEWS_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/uploadnews"), + /** + * 上传群发用的视频. + */ + MEDIA_UPLOAD_VIDEO_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/uploadvideo"), + /** + * 分组群发消息. + */ + MESSAGE_MASS_SENDALL_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/mass/sendall"), + /** + * 按openId列表群发消息. + */ + MESSAGE_MASS_SEND_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/mass/send"), + /** + * 群发消息预览接口. + */ + MESSAGE_MASS_PREVIEW_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/mass/preview"), + /** + * 删除群发接口. + */ + MESSAGE_MASS_DELETE_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/mass/delete"), + + + /** + * 获取群发速度. + */ + MESSAGE_MASS_SPEED_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/mass/speed/get"), + + + /** + * 设置群发速度. + */ + MESSAGE_MASS_SPEED_SET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/mass/speed/set"), + + + /** + * 查询群发消息发送状态【订阅号与服务号认证后均可用】 + */ + MESSAGE_MASS_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/message/mass/get"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Material implements WxMpApiUrl { + /** + * get. + */ + MEDIA_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/get"), + /** + * jssdk media get. + */ + JSSDK_MEDIA_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/get/jssdk"), + /** + * upload. + */ + MEDIA_UPLOAD_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/upload?type=%s"), + /** + * uploadimg. + */ + IMG_UPLOAD_URL(API_DEFAULT_HOST_URL, "/cgi-bin/media/uploadimg"), + /** + * add_material. + */ + MATERIAL_ADD_URL(API_DEFAULT_HOST_URL, "/cgi-bin/material/add_material?type=%s"), + /** + * add_news. + */ + NEWS_ADD_URL(API_DEFAULT_HOST_URL, "/cgi-bin/material/add_news"), + /** + * get_material. + */ + MATERIAL_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/material/get_material"), + /** + * update_news. + */ + NEWS_UPDATE_URL(API_DEFAULT_HOST_URL, "/cgi-bin/material/update_news"), + /** + * del_material. + */ + MATERIAL_DEL_URL(API_DEFAULT_HOST_URL, "/cgi-bin/material/del_material"), + /** + * get_materialcount. + */ + MATERIAL_GET_COUNT_URL(API_DEFAULT_HOST_URL, "/cgi-bin/material/get_materialcount"), + /** + * batchget_material. + */ + MATERIAL_BATCHGET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/material/batchget_material"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum MemberCard implements WxMpApiUrl { + /** + * create. + */ + MEMBER_CARD_CREATE(API_DEFAULT_HOST_URL, "/card/create"), + /** + * activate. + */ + MEMBER_CARD_ACTIVATE(API_DEFAULT_HOST_URL, "/card/membercard/activate"), + /** + * get userinfo. + */ + MEMBER_CARD_USER_INFO_GET(API_DEFAULT_HOST_URL, "/card/membercard/userinfo/get"), + /** + * updateuser. + */ + MEMBER_CARD_UPDATE_USER(API_DEFAULT_HOST_URL, "/card/membercard/updateuser"), + /** + * 会员卡激活之微信开卡接口(wx_activate=true情况调用). + */ + MEMBER_CARD_ACTIVATE_USER_FORM(API_DEFAULT_HOST_URL, "/card/membercard/activateuserform/set"), + /** + * 获取会员卡开卡插件参数. + */ + MEMBER_CARD_ACTIVATE_URL(API_DEFAULT_HOST_URL, "/card/membercard/activate/geturl"), + /** + * 会员卡信息更新. + */ + MEMBER_CARD_UPDATE(API_DEFAULT_HOST_URL, "/card/update"), + /** + * 跳转型会员卡开卡字段. + * 获取用户提交资料(wx_activate=true情况调用),开发者根据activate_ticket获取到用户填写的信息 + */ + MEMBER_CARD_ACTIVATE_TEMP_INFO(API_DEFAULT_HOST_URL, "/card/membercard/activatetempinfo/get"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Store implements WxMpApiUrl { + /** + * getwxcategory. + */ + POI_GET_WX_CATEGORY_URL(API_DEFAULT_HOST_URL, "/cgi-bin/poi/getwxcategory"), + /** + * updatepoi. + */ + POI_UPDATE_URL(API_DEFAULT_HOST_URL, "/cgi-bin/poi/updatepoi"), + /** + * getpoilist. + */ + POI_LIST_URL(API_DEFAULT_HOST_URL, "/cgi-bin/poi/getpoilist"), + /** + * delpoi. + */ + POI_DEL_URL(API_DEFAULT_HOST_URL, "/cgi-bin/poi/delpoi"), + /** + * getpoi. + */ + POI_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/poi/getpoi"), + /** + * addpoi. + */ + POI_ADD_URL(API_DEFAULT_HOST_URL, "/cgi-bin/poi/addpoi"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum User implements WxMpApiUrl { + /** + * batchget. + */ + USER_INFO_BATCH_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/user/info/batchget"), + /** + * get. + */ + USER_GET_URL(API_DEFAULT_HOST_URL, "/cgi-bin/user/get"), + /** + * info. + */ + USER_INFO_URL(API_DEFAULT_HOST_URL, "/cgi-bin/user/info"), + /** + * updateremark. + */ + USER_INFO_UPDATE_REMARK_URL(API_DEFAULT_HOST_URL, "/cgi-bin/user/info/updateremark"), + /** + * changeopenid. + */ + USER_CHANGE_OPENID_URL(API_DEFAULT_HOST_URL, "/cgi-bin/changeopenid"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Comment implements WxMpApiUrl { + /** + * 打开已群发文章评论. + */ + OPEN(API_DEFAULT_HOST_URL, "/cgi-bin/comment/open"), + + /** + * 关闭已群发文章评论. + */ + CLOSE(API_DEFAULT_HOST_URL, "/cgi-bin/comment/close"), + + /** + * 查看指定文章的评论数据. + */ + LIST(API_DEFAULT_HOST_URL, "/cgi-bin/comment/list"), + + /** + * 将评论标记精选. + */ + MARK_ELECT(API_DEFAULT_HOST_URL, "/cgi-bin/comment/markelect"), + + /** + * 将评论取消精选. + */ + UNMARK_ELECT(API_DEFAULT_HOST_URL, "/cgi-bin/comment/unmarkelect"), + + /** + * 删除评论. + */ + DELETE(API_DEFAULT_HOST_URL, "/cgi-bin/comment/delete"), + + /** + * 回复评论. + */ + REPLY_ADD(API_DEFAULT_HOST_URL, "/cgi-bin/comment/reply/add"), + + /** + * 删除回复. + */ + REPLY_DELETE(API_DEFAULT_HOST_URL, "/cgi-bin/comment/reply/delete"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum ImgProc implements WxMpApiUrl { + /** + * 二维码/条码识别 + */ + QRCODE(API_DEFAULT_HOST_URL, "/cv/img/qrcode?img_url=%s"), + + /** + * 二维码/条码识别(文件) + */ + FILE_QRCODE(API_DEFAULT_HOST_URL, "/cv/img/qrcode"), + + /** + * 图片高清化 + */ + SUPER_RESOLUTION(API_DEFAULT_HOST_URL, "/cv/img/superresolution?img_url=%s"), + + /** + * 图片高清化(文件) + */ + FILE_SUPER_RESOLUTION(API_DEFAULT_HOST_URL, "/cv/img/superresolution"), + + /** + * 图片智能裁剪 + */ + AI_CROP(API_DEFAULT_HOST_URL, "/cv/img/aicrop?img_url=%s&ratios=%s"), + + /** + * 图片智能裁剪(文件) + */ + FILE_AI_CROP(API_DEFAULT_HOST_URL, "/cv/img/aicrop?ratios=%s"); + + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + if (null == config) { + return buildUrl(null, prefix, path); + } + return buildUrl(config.getHostConfig(), prefix, path); + } + } + + @AllArgsConstructor + enum Invoice implements WxMpApiUrl { + + /** + * 获取用户开票授权地址 + */ + GET_AUTH_URL(API_DEFAULT_HOST_URL, "/card/invoice/getauthurl"), + + /** + * 获取用户开票授权信息 + */ + GET_AUTH_DATA(API_DEFAULT_HOST_URL, "/card/invoice/getauthdata"), + + /** + * 拒绝为用户开票 + */ + REJECT_INSERT(API_DEFAULT_HOST_URL, "/card/invoice/rejectinsert"), + + /** + * 开票 + */ + MAKE_OUT_INVOICE(API_DEFAULT_HOST_URL, "/card/invoice/makeoutinvoice"), + + /** + * 发票冲红 + */ + CLEAR_OUT_INVOICE(API_DEFAULT_HOST_URL, "/card/invoice/clearoutinvoice"), + + /** + * 查询发票信息 + */ + QUERY_INVOICE_INFO(API_DEFAULT_HOST_URL, "/card/invoice/queryinvoceinfo"), + + /** + * 设置商户信息联系 + */ + SET_CONTACT_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=set_contact"), + + /** + * 获取商户联系信息 + */ + GET_CONTACT_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=get_contact"), + + /** + * 设置授权页面字段 + */ + SET_AUTH_FIELD_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=set_auth_field"), + + /** + * 获取授权页面字段 + */ + GET_AUTH_FIELD_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=get_auth_field"), + + /** + * 设置关联商户 + */ + SET_PAY_MCH_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=set_pay_mch"), + + /** + * 获取关联商户 + */ + GET_PAY_MCH_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=get_pay_mch"), + ; + private final String prefix; + private final String path; + + @Override + public String getUrl(WxMpConfigStorage config) { + if (null == config) { + return buildUrl(null, prefix, path); + } + return buildUrl(config.getHostConfig(), prefix, path); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/WxMpConfigStorageHolder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/WxMpConfigStorageHolder.java new file mode 100644 index 0000000000..e844c4866b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/WxMpConfigStorageHolder.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.mp.util; + +/** + * @author yd + * @date 2019-03-20 22:06 + */ +public class WxMpConfigStorageHolder { + private final static ThreadLocal THREAD_LOCAL = new ThreadLocal() { + @Override + protected String initialValue() { + return "default"; + } + }; + + public static String get() { + return THREAD_LOCAL.get(); + } + + public static void set(String label) { + THREAD_LOCAL.set(label); + } + + /** + * 此方法需要用户根据自己程序代码,在适当位置手动触发调用,本SDK里无法判断调用时机 + */ + public static void remove() { + THREAD_LOCAL.remove(); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java index 39165b5c17..9f22dbed3e 100755 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java @@ -1,41 +1,46 @@ -/** - * 对公众平台发送给公众账号的消息加解密示例代码. - * - * @copyright Copyright (c) 1998-2014 Tencent Inc. - */ - -// ------------------------------------------------------------------------ - -/** - * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - */ -package me.chanjar.weixin.mp.util.crypto; - -import me.chanjar.weixin.mp.api.WxMpConfigStorage; -import org.apache.commons.codec.binary.Base64; - -public class WxMpCryptUtil extends me.chanjar.weixin.common.util.crypto.WxCryptUtil { - - /** - * 构造函数 - * - * @param wxMpConfigStorage - */ - public WxMpCryptUtil(WxMpConfigStorage wxMpConfigStorage) { - /* - * @param token 公众平台上,开发者设置的token - * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey - * @param appId 公众平台appid - */ - String encodingAesKey = wxMpConfigStorage.getAesKey(); - String token = wxMpConfigStorage.getToken(); - String appId = wxMpConfigStorage.getAppId(); - - this.token = token; - this.appidOrCorpid = appId; - this.aesKey = Base64.decodeBase64(encodingAesKey + "="); - } - -} +/** + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + *

    + * 针对org.apache.commons.codec.binary.Base64, + * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) + * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi + */ + +// ------------------------------------------------------------------------ + +/** + * 针对org.apache.commons.codec.binary.Base64, + * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) + * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi + */ +package me.chanjar.weixin.mp.util.crypto; + +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; + +public class WxMpCryptUtil extends me.chanjar.weixin.common.util.crypto.WxCryptUtil { + + /** + * 构造函数 + * + * @param wxMpConfigStorage + */ + public WxMpCryptUtil(WxMpConfigStorage wxMpConfigStorage) { + /* + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appId 公众平台appid + */ + String encodingAesKey = wxMpConfigStorage.getAesKey(); + String token = wxMpConfigStorage.getToken(); + String appId = wxMpConfigStorage.getAppId(); + + this.token = token; + this.appidOrCorpid = appId; + this.aesKey = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey)); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java deleted file mode 100644 index 9502afe66d..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.chanjar.weixin.mp.util.http; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -public class MaterialDeleteRequestExecutor implements RequestExecutor { - - - public MaterialDeleteRequestExecutor() { - super(); - } - - @Override - public Boolean execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String materialId) throws WxErrorException, IOException { - HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpPost.setConfig(config); - } - - Map params = new HashMap<>(); - params.put("media_id", materialId); - httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - try(CloseableHttpResponse response = httpclient.execute(httpPost)){ - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } else { - return true; - } - }finally { - httpPost.releaseConnection(); - } - } - -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java deleted file mode 100644 index f61602abdb..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.chanjar.weixin.mp.util.http; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -public class MaterialNewsInfoRequestExecutor implements RequestExecutor { - - public MaterialNewsInfoRequestExecutor() { - super(); - } - - @Override - public WxMpMaterialNews execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String materialId) throws WxErrorException, IOException { - HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpPost.setConfig(config); - } - - Map params = new HashMap<>(); - params.put("media_id", materialId); - httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - try(CloseableHttpResponse response = httpclient.execute(httpPost)){ - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } else { - return WxMpGsonBuilder.create().fromJson(responseContent, WxMpMaterialNews.class); - } - }finally { - httpPost.releaseConnection(); - } - - } - -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java deleted file mode 100644 index b06c34a60d..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.chanjar.weixin.mp.util.http; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; -import me.chanjar.weixin.mp.bean.material.WxMpMaterial; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.impl.client.CloseableHttpClient; - -import java.io.*; -import java.util.Map; - -public class MaterialUploadRequestExecutor implements RequestExecutor { - - @Override - public WxMpMaterialUploadResult execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, WxMpMaterial material) throws WxErrorException, IOException { - HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig response = RequestConfig.custom().setProxy(httpProxy).build(); - httpPost.setConfig(response); - } - - if (material == null) { - throw new WxErrorException(WxError.newBuilder().setErrorMsg("非法请求,material参数为空").build()); - } - - File file = material.getFile(); - if (file == null || !file.exists()) { - throw new FileNotFoundException(); - } - - MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); - multipartEntityBuilder - .addBinaryBody("media", file) - .setMode(HttpMultipartMode.RFC6532); - Map form = material.getForm(); - if (material.getForm() != null) { - multipartEntityBuilder.addTextBody("description", WxGsonBuilder.create().toJson(form)); - } - - httpPost.setEntity(multipartEntityBuilder.build()); - httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); - - try (CloseableHttpResponse response = httpclient.execute(httpPost)) { - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } else { - return WxMpMaterialUploadResult.fromJson(responseContent); - } - } finally { - httpPost.releaseConnection(); - } - } - -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java deleted file mode 100644 index 2486ca8090..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.chanjar.weixin.mp.util.http; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.InputStreamResponseHandler; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; -import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; -import org.apache.http.Header; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.entity.ContentType; -import org.apache.http.impl.client.CloseableHttpClient; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URLEncoder; -import java.util.UUID; - -/** - * 获得QrCode图片 请求执行器 - * @author chanjarster - * - */ -public class QrCodeRequestExecutor implements RequestExecutor { - - @Override - public File execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, - WxMpQrCodeTicket ticket) throws WxErrorException, IOException { - if (ticket != null) { - if (uri.indexOf('?') == -1) { - uri += '?'; - } - uri += uri.endsWith("?") - ? "ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8") - : "&ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8"); - } - - HttpGet httpGet = new HttpGet(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpGet.setConfig(config); - } - - try (CloseableHttpResponse response = httpclient.execute(httpGet); - InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { - Header[] contentTypeHeader = response.getHeaders("Content-Type"); - if (contentTypeHeader != null && contentTypeHeader.length > 0) { - // 出错 - if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) { - String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - throw new WxErrorException(WxError.fromJson(responseContent)); - } - } - return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); - } finally { - httpGet.releaseConnection(); - } - - } - -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java index 022a59f111..872bf8c757 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java @@ -1,11 +1,15 @@ package me.chanjar.weixin.mp.util.json; -import com.google.gson.*; -import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.mp.bean.WxMpCard; - import java.lang.reflect.Type; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.card.WxMpCard; + /** * Created by YuJian on 15/11/11. * @@ -18,11 +22,16 @@ public class WxMpCardGsonAdapter implements JsonDeserializer { public WxMpCard deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { WxMpCard card = new WxMpCard(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); card.setCardId(GsonHelper.getString(jsonObject, "card_id")); card.setBeginTime(GsonHelper.getLong(jsonObject, "begin_time")); card.setEndTime(GsonHelper.getLong(jsonObject, "end_time")); + card.setUserCardStatus(GsonHelper.getString(jsonObject, "user_card_status")); + card.setMembershipNumber(GsonHelper.getString(jsonObject, "membership_number")); + card.setCode(GsonHelper.getString(jsonObject, "code")); + card.setBonus(GsonHelper.getInteger(jsonObject, "bonus")); return card; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java index 9d7866494b..defe8822bb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java @@ -1,12 +1,16 @@ package me.chanjar.weixin.mp.util.json; -import com.google.gson.*; +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.mp.bean.WxMpCard; -import me.chanjar.weixin.mp.bean.result.WxMpCardResult; - -import java.lang.reflect.Type; +import me.chanjar.weixin.mp.bean.card.WxMpCard; +import me.chanjar.weixin.mp.bean.card.WxMpCardResult; /** * Created by YuJian on 15/11/11. @@ -18,6 +22,7 @@ public class WxMpCardResultGsonAdapter implements JsonDeserializer() { }.getType()); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpChangeOpenidGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpChangeOpenidGsonAdapter.java new file mode 100644 index 0000000000..430726ca49 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpChangeOpenidGsonAdapter.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid; + +import java.lang.reflect.Type; + +public class WxMpChangeOpenidGsonAdapter implements JsonDeserializer { + + @Override + public WxMpChangeOpenid deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject o = json.getAsJsonObject(); + WxMpChangeOpenid changeOpenid = new WxMpChangeOpenid(); + changeOpenid.setOriOpenid(GsonHelper.getString(o, "ori_openid")); + changeOpenid.setNewOpenid(GsonHelper.getString(o, "new_openid")); + changeOpenid.setErrMsg(GsonHelper.getString(o, "err_msg")); + return changeOpenid; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java index 286778ad4b..82e2b36318 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java @@ -3,17 +3,26 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.card.WxMpCard; +import me.chanjar.weixin.mp.bean.card.WxMpCardResult; import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserCumulate; import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserSummary; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import me.chanjar.weixin.mp.bean.material.*; +import me.chanjar.weixin.mp.bean.card.membercard.WxMpMemberCardActivateTempInfoResult; +import me.chanjar.weixin.mp.bean.card.membercard.WxMpMemberCardUpdateResult; +import me.chanjar.weixin.mp.bean.card.membercard.WxMpMemberCardUserInfoResult; import me.chanjar.weixin.mp.bean.result.*; +import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage; import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; +/** + * @author someone + */ public class WxMpGsonBuilder { - public static final GsonBuilder INSTANCE = new GsonBuilder(); + private static final GsonBuilder INSTANCE = new GsonBuilder(); static { INSTANCE.disableHtmlEscaping(); @@ -22,23 +31,24 @@ public class WxMpGsonBuilder { INSTANCE.registerTypeAdapter(WxMpMassTagMessage.class, new WxMpMassTagMessageGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMassOpenIdsMessage.class, new WxMpMassOpenIdsMessageGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpUser.class, new WxMpUserGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpChangeOpenid.class, new WxMpChangeOpenidGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpUserList.class, new WxUserListGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMassVideo.class, new WxMpMassVideoAdapter()); INSTANCE.registerTypeAdapter(WxMpMassSendResult.class, new WxMpMassSendResultAdapter()); INSTANCE.registerTypeAdapter(WxMpMassUploadResult.class, new WxMpMassUploadResultAdapter()); INSTANCE.registerTypeAdapter(WxMpQrCodeTicket.class, new WxQrCodeTicketAdapter()); INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpSubscribeMessage.class, new WxMpSubscribeMessageGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter()); INSTANCE.registerTypeAdapter(WxMpOAuth2AccessToken.class, new WxMpOAuth2AccessTokenAdapter()); INSTANCE.registerTypeAdapter(WxDataCubeUserSummary.class, new WxMpUserSummaryGsonAdapter()); INSTANCE.registerTypeAdapter(WxDataCubeUserCumulate.class, new WxMpUserCumulateGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialUploadResult.class, new WxMpMaterialUploadResultAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialVideoInfoResult.class, new WxMpMaterialVideoInfoResultAdapter()); - INSTANCE.registerTypeAdapter(WxMpMassNews.WxMpMassNewsArticle.class, new WxMpMassNewsArticleGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialArticleUpdate.class, new WxMpMaterialArticleUpdateGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialCountResult.class, new WxMpMaterialCountResultAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialNews.class, new WxMpMaterialNewsGsonAdapter()); - INSTANCE.registerTypeAdapter(WxMpMaterialNews.WxMpMaterialNewsArticle.class, new WxMpMaterialNewsArticleGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpNewsArticle.class, new WxMpNewsArticleGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialNewsBatchGetResult.class, new WxMpMaterialNewsBatchGetGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialNewsBatchGetResult.WxMaterialNewsBatchGetNewsItem.class, new WxMpMaterialNewsBatchGetGsonItemAdapter()); INSTANCE.registerTypeAdapter(WxMpMaterialFileBatchGetResult.class, new WxMpMaterialFileBatchGetGsonAdapter()); @@ -49,6 +59,9 @@ public class WxMpGsonBuilder { INSTANCE.registerTypeAdapter(WxMediaImgUploadResult.class, new WxMediaImgUploadResultGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpTemplateIndustry.class, new WxMpIndustryGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpUserBlacklistGetResult.class, new WxUserBlacklistGetResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpMemberCardUserInfoResult.class, new WxMpMemberCardUserInfoResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpMemberCardUpdateResult.class, new WxMpMemberCardUpdateResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpMemberCardActivateTempInfoResult.class, new WxMpMemberCardActivateTempInfoResultGsonAdapter()); } public static Gson create() { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java index eb0972bcdc..30ac9c1a74 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java @@ -3,41 +3,43 @@ import com.google.gson.*; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; +import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustryEnum; import java.lang.reflect.Type; /** * @author miller */ -public class WxMpIndustryGsonAdapter - implements JsonSerializer, JsonDeserializer { - private static WxMpTemplateIndustry.Industry convertFromJson(JsonObject json) { - WxMpTemplateIndustry.Industry industry = new WxMpTemplateIndustry.Industry(); - industry.setFirstClass(GsonHelper.getString(json, "first_class")); - industry.setSecondClass(GsonHelper.getString(json, "second_class")); - return industry; - } - +public class WxMpIndustryGsonAdapter implements JsonSerializer, JsonDeserializer { @Override - public JsonElement serialize(WxMpTemplateIndustry wxMpIndustry, Type type, - JsonSerializationContext jsonSerializationContext) { + public JsonElement serialize(WxMpTemplateIndustry wxMpIndustry, Type type, JsonSerializationContext context) { JsonObject json = new JsonObject(); - json.addProperty("industry_id1", wxMpIndustry.getPrimaryIndustry().getId()); - json.addProperty("industry_id2", wxMpIndustry.getSecondIndustry().getId()); + json.addProperty("industry_id1", wxMpIndustry.getPrimaryIndustry().getCode()); + json.addProperty("industry_id2", wxMpIndustry.getSecondIndustry().getCode()); return json; } @Override - public WxMpTemplateIndustry deserialize(JsonElement jsonElement, Type type, - JsonDeserializationContext jsonDeserializationContext) + public WxMpTemplateIndustry deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { - WxMpTemplateIndustry wxMpIndustry = new WxMpTemplateIndustry(); - JsonObject primaryIndustry = jsonElement.getAsJsonObject() - .get("primary_industry").getAsJsonObject(); - wxMpIndustry.setPrimaryIndustry(convertFromJson(primaryIndustry)); - JsonObject secondaryIndustry = jsonElement.getAsJsonObject() - .get("secondary_industry").getAsJsonObject(); - wxMpIndustry.setSecondIndustry(convertFromJson(secondaryIndustry)); - return wxMpIndustry; + return new WxMpTemplateIndustry() + .setPrimaryIndustry(this.convertFromJson(jsonElement.getAsJsonObject().get("primary_industry").getAsJsonObject())) + .setSecondIndustry(this.convertFromJson(jsonElement.getAsJsonObject().get("secondary_industry").getAsJsonObject())); } + + private WxMpTemplateIndustryEnum convertFromJson(JsonObject json) { + String firstClass = GsonHelper.getString(json, "first_class"); + String secondClass = GsonHelper.getString(json, "second_class"); + final WxMpTemplateIndustryEnum industryEnum = WxMpTemplateIndustryEnum.findByClass(firstClass, secondClass); + if (industryEnum != null) { + return industryEnum; + } + + if (secondClass.contains("|")) { + secondClass = secondClass.split("\\|")[1]; + } + + return WxMpTemplateIndustryEnum.findByClass(firstClass, secondClass); + } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java index 56297ce2bd..e9e5112d31 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java @@ -1,15 +1,7 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; -import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.api.WxConsts.KefuMsgType; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import org.apache.commons.lang3.StringUtils; @@ -23,68 +15,89 @@ public JsonElement serialize(WxMpKefuMessage message, Type typeOfSrc, JsonSerial messageJson.addProperty("touser", message.getToUser()); messageJson.addProperty("msgtype", message.getMsgType()); - if (WxConsts.CUSTOM_MSG_TEXT.equals(message.getMsgType())) { - JsonObject text = new JsonObject(); - text.addProperty("content", message.getContent()); - messageJson.add("text", text); - } - - if (WxConsts.CUSTOM_MSG_IMAGE.equals(message.getMsgType())) { - JsonObject image = new JsonObject(); - image.addProperty("media_id", message.getMediaId()); - messageJson.add("image", image); - } - - if (WxConsts.CUSTOM_MSG_VOICE.equals(message.getMsgType())) { - JsonObject voice = new JsonObject(); - voice.addProperty("media_id", message.getMediaId()); - messageJson.add("voice", voice); - } - - if (WxConsts.CUSTOM_MSG_VIDEO.equals(message.getMsgType())) { - JsonObject video = new JsonObject(); - video.addProperty("media_id", message.getMediaId()); - video.addProperty("thumb_media_id", message.getThumbMediaId()); - video.addProperty("title", message.getTitle()); - video.addProperty("description", message.getDescription()); - messageJson.add("video", video); - } - - if (WxConsts.CUSTOM_MSG_MUSIC.equals(message.getMsgType())) { - JsonObject music = new JsonObject(); - music.addProperty("title", message.getTitle()); - music.addProperty("description", message.getDescription()); - music.addProperty("thumb_media_id", message.getThumbMediaId()); - music.addProperty("musicurl", message.getMusicUrl()); - music.addProperty("hqmusicurl", message.getHqMusicUrl()); - messageJson.add("music", music); - } - - if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { - JsonObject newsJsonObject = new JsonObject(); - JsonArray articleJsonArray = new JsonArray(); - for (WxMpKefuMessage.WxArticle article : message.getArticles()) { - JsonObject articleJson = new JsonObject(); - articleJson.addProperty("title", article.getTitle()); - articleJson.addProperty("description", article.getDescription()); - articleJson.addProperty("url", article.getUrl()); - articleJson.addProperty("picurl", article.getPicUrl()); - articleJsonArray.add(articleJson); + switch (message.getMsgType()) { + case KefuMsgType.TEXT: + JsonObject text = new JsonObject(); + text.addProperty("content", message.getContent()); + messageJson.add("text", text); + break; + case KefuMsgType.IMAGE: + JsonObject image = new JsonObject(); + image.addProperty("media_id", message.getMediaId()); + messageJson.add("image", image); + break; + case KefuMsgType.VOICE: + JsonObject voice = new JsonObject(); + voice.addProperty("media_id", message.getMediaId()); + messageJson.add("voice", voice); + break; + case KefuMsgType.VIDEO: + JsonObject video = new JsonObject(); + video.addProperty("media_id", message.getMediaId()); + video.addProperty("thumb_media_id", message.getThumbMediaId()); + video.addProperty("title", message.getTitle()); + video.addProperty("description", message.getDescription()); + messageJson.add("video", video); + break; + case KefuMsgType.MUSIC: + JsonObject music = new JsonObject(); + music.addProperty("title", message.getTitle()); + music.addProperty("description", message.getDescription()); + music.addProperty("thumb_media_id", message.getThumbMediaId()); + music.addProperty("musicurl", message.getMusicUrl()); + music.addProperty("hqmusicurl", message.getHqMusicUrl()); + messageJson.add("music", music); + break; + case KefuMsgType.NEWS: + JsonObject newsJsonObject = new JsonObject(); + JsonArray articleJsonArray = new JsonArray(); + for (WxMpKefuMessage.WxArticle article : message.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("description", article.getDescription()); + articleJson.addProperty("url", article.getUrl()); + articleJson.addProperty("picurl", article.getPicUrl()); + articleJsonArray.add(articleJson); + } + newsJsonObject.add("articles", articleJsonArray); + messageJson.add("news", newsJsonObject); + break; + case KefuMsgType.MPNEWS: + JsonObject json = new JsonObject(); + json.addProperty("media_id", message.getMpNewsMediaId()); + messageJson.add("mpnews", json); + break; + case KefuMsgType.WXCARD: + JsonObject wxcard = new JsonObject(); + wxcard.addProperty("card_id", message.getCardId()); + messageJson.add("wxcard", wxcard); + break; + case KefuMsgType.MINIPROGRAMPAGE: + JsonObject miniProgramPage = new JsonObject(); + miniProgramPage.addProperty("title", message.getTitle()); + miniProgramPage.addProperty("appid", message.getMiniProgramAppId()); + miniProgramPage.addProperty("pagepath", message.getMiniProgramPagePath()); + miniProgramPage.addProperty("thumb_media_id", message.getThumbMediaId()); + messageJson.add("miniprogrampage", miniProgramPage); + break; + case KefuMsgType.MSGMENU: { + JsonObject msgmenuJsonObject = new JsonObject(); + JsonArray listJsonArray = new JsonArray(); + for (WxMpKefuMessage.MsgMenu list : message.getMsgMenus()) { + JsonObject listJson = new JsonObject(); + listJson.addProperty("id", list.getId()); + listJson.addProperty("content", list.getContent()); + listJsonArray.add(listJson); + } + msgmenuJsonObject.addProperty("head_content",message.getHeadContent()); + msgmenuJsonObject.add("list", listJsonArray); + msgmenuJsonObject.addProperty("tail_content",message.getTailContent()); + messageJson.add("msgmenu", msgmenuJsonObject); + break; + } + default: { + throw new RuntimeException("非法消息类型,暂不支持"); } - newsJsonObject.add("articles", articleJsonArray); - messageJson.add("news", newsJsonObject); - } - - if (WxConsts.CUSTOM_MSG_MPNEWS.equals(message.getMsgType())) { - JsonObject json = new JsonObject(); - json.addProperty("media_id", message.getMpNewsMediaId()); - messageJson.add("mpnews", json); - } - - if (WxConsts.CUSTOM_MSG_WXCARD.equals(message.getMsgType())) { - JsonObject wxcard = new JsonObject(); - wxcard.addProperty("card_id", message.getCardId()); - messageJson.add("wxcard", wxcard); } if (StringUtils.isNotBlank(message.getKfAccount())) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsArticleGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsArticleGsonAdapter.java deleted file mode 100644 index e4d8840344..0000000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsArticleGsonAdapter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ -package me.chanjar.weixin.mp.util.json; - -import com.google.gson.*; -import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.mp.bean.WxMpMassNews; - -import java.lang.reflect.Type; - -public class WxMpMassNewsArticleGsonAdapter implements JsonSerializer, JsonDeserializer { - - @Override - public JsonElement serialize(WxMpMassNews.WxMpMassNewsArticle article, Type typeOfSrc, JsonSerializationContext context) { - JsonObject articleJson = new JsonObject(); - - articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); - articleJson.addProperty("title", article.getTitle()); - articleJson.addProperty("content", article.getContent()); - if (null != article.getAuthor()) { - articleJson.addProperty("author", article.getAuthor()); - } - if (null != article.getContentSourceUrl()) { - articleJson.addProperty("content_source_url", article.getContentSourceUrl()); - } - if (null != article.getDigest()) { - articleJson.addProperty("digest", article.getDigest()); - } - articleJson.addProperty("show_cover_pic", article.isShowCoverPic() ? "1" : "0"); - return articleJson; - } - - @Override - public WxMpMassNews.WxMpMassNewsArticle deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { - JsonObject articleInfo = jsonElement.getAsJsonObject(); - WxMpMassNews.WxMpMassNewsArticle article = new WxMpMassNews.WxMpMassNewsArticle(); - - JsonElement title = articleInfo.get("title"); - if (title != null && !title.isJsonNull()) { - article.setTitle(GsonHelper.getAsString(title)); - } - JsonElement content = articleInfo.get("content"); - if (content != null && !content.isJsonNull()) { - article.setContent(GsonHelper.getAsString(content)); - } - JsonElement contentSourceUrl = articleInfo.get("content_source_url"); - if (contentSourceUrl != null && !contentSourceUrl.isJsonNull()) { - article.setContentSourceUrl(GsonHelper.getAsString(contentSourceUrl)); - } - JsonElement author = articleInfo.get("author"); - if (author != null && !author.isJsonNull()) { - article.setAuthor(GsonHelper.getAsString(author)); - } - JsonElement digest = articleInfo.get("digest"); - if (digest != null && !digest.isJsonNull()) { - article.setDigest(GsonHelper.getAsString(digest)); - } - JsonElement thumbMediaId = articleInfo.get("thumb_media_id"); - if (thumbMediaId != null && !thumbMediaId.isJsonNull()) { - article.setThumbMediaId(GsonHelper.getAsString(thumbMediaId)); - } - JsonElement showCoverPic = articleInfo.get("show_cover_pic"); - if (showCoverPic != null && !showCoverPic.isJsonNull()) { - article.setShowCoverPic(GsonHelper.getAsBoolean(showCoverPic)); - } - return article; - } -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java index fecf494d85..015ad9262a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java @@ -1,15 +1,8 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; import me.chanjar.weixin.mp.bean.WxMpMassNews; +import me.chanjar.weixin.mp.bean.material.WxMpNewsArticle; import java.lang.reflect.Type; @@ -20,8 +13,8 @@ public JsonElement serialize(WxMpMassNews message, Type typeOfSrc, JsonSerializa JsonObject newsJson = new JsonObject(); JsonArray articleJsonArray = new JsonArray(); - for (WxMpMassNews.WxMpMassNewsArticle article : message.getArticles()) { - JsonObject articleJson = WxMpGsonBuilder.create().toJsonTree(article, WxMpMassNews.WxMpMassNewsArticle.class).getAsJsonObject(); + for (WxMpNewsArticle article : message.getArticles()) { + JsonObject articleJson = WxMpGsonBuilder.create().toJsonTree(article, WxMpNewsArticle.class).getAsJsonObject(); articleJsonArray.add(articleJson); } newsJson.add("articles", articleJsonArray); @@ -37,7 +30,7 @@ public WxMpMassNews deserialize(JsonElement jsonElement, Type type, JsonDeserial JsonArray articles = json.getAsJsonArray("articles"); for (JsonElement article1 : articles) { JsonObject articleInfo = article1.getAsJsonObject(); - WxMpMassNews.WxMpMassNewsArticle article = WxMpGsonBuilder.create().fromJson(articleInfo, WxMpMassNews.WxMpMassNewsArticle.class); + WxMpNewsArticle article = WxMpGsonBuilder.create().fromJson(articleInfo, WxMpNewsArticle.class); wxMpMassNews.addArticle(article); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java index 64dfbb1234..10b1b72711 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java @@ -1,19 +1,15 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage; +import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Type; +/** + * @author someone + */ public class WxMpMassOpenIdsMessageGsonAdapter implements JsonSerializer { @Override @@ -26,33 +22,45 @@ public JsonElement serialize(WxMpMassOpenIdsMessage message, Type typeOfSrc, Jso } messageJson.add("touser", toUsers); - if (WxConsts.MASS_MSG_NEWS.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.MPNEWS.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_NEWS, sub); + messageJson.add(WxConsts.MassMsgType.MPNEWS, sub); } - if (WxConsts.MASS_MSG_TEXT.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.TEXT.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("content", message.getContent()); - messageJson.add(WxConsts.MASS_MSG_TEXT, sub); + messageJson.add(WxConsts.MassMsgType.TEXT, sub); } - if (WxConsts.MASS_MSG_VOICE.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.VOICE.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_VOICE, sub); + messageJson.add(WxConsts.MassMsgType.VOICE, sub); } - if (WxConsts.MASS_MSG_IMAGE.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.IMAGE.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_IMAGE, sub); + messageJson.add(WxConsts.MassMsgType.IMAGE, sub); } - if (WxConsts.MASS_MSG_VIDEO.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.MPVIDEO.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_VIDEO, sub); + messageJson.add(WxConsts.MassMsgType.MPVIDEO, sub); } messageJson.addProperty("msgtype", message.getMsgType()); - messageJson.addProperty("send_ignore_reprint", message.isSendIgnoreReprint() ? 0 : 1); + + /* + 开发者可以对群发接口的 send_ignore_reprint 参数进行设置,指定待群发的文章被判定为转载时,是否继续群发。 + 当 send_ignore_reprint 参数设置为1时,文章被判定为转载时,且原创文允许转载时,将继续进行群发操作。 + 当 send_ignore_reprint 参数设置为0时,文章被判定为转载时,将停止群发操作。 + send_ignore_reprint 默认为0。 + */ + messageJson.addProperty("send_ignore_reprint", message.isSendIgnoreReprint() ? 1 : 0); + + if (StringUtils.isNotEmpty(message.getClientMsgId())) { + messageJson.addProperty("clientmsgid", message.getClientMsgId()); + } + return messageJson; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java index fd210fb33a..bd8ede336d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java @@ -18,30 +18,30 @@ public JsonElement serialize(WxMpMassPreviewMessage wxMpMassPreviewMessage, Type JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("towxname", wxMpMassPreviewMessage.getToWxUserName()); jsonObject.addProperty("touser", wxMpMassPreviewMessage.getToWxUserOpenid()); - if (WxConsts.MASS_MSG_NEWS.equals(wxMpMassPreviewMessage.getMsgType())) { + if (WxConsts.MassMsgType.MPNEWS.equals(wxMpMassPreviewMessage.getMsgType())) { JsonObject news = new JsonObject(); news.addProperty("media_id", wxMpMassPreviewMessage.getMediaId()); - jsonObject.add(WxConsts.MASS_MSG_NEWS, news); + jsonObject.add(WxConsts.MassMsgType.MPNEWS, news); } - if (WxConsts.MASS_MSG_TEXT.equals(wxMpMassPreviewMessage.getMsgType())) { + if (WxConsts.MassMsgType.TEXT.equals(wxMpMassPreviewMessage.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("content", wxMpMassPreviewMessage.getContent()); - jsonObject.add(WxConsts.MASS_MSG_TEXT, sub); + jsonObject.add(WxConsts.MassMsgType.TEXT, sub); } - if (WxConsts.MASS_MSG_VOICE.equals(wxMpMassPreviewMessage.getMsgType())) { + if (WxConsts.MassMsgType.VOICE.equals(wxMpMassPreviewMessage.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", wxMpMassPreviewMessage.getMediaId()); - jsonObject.add(WxConsts.MASS_MSG_VOICE, sub); + jsonObject.add(WxConsts.MassMsgType.VOICE, sub); } - if (WxConsts.MASS_MSG_IMAGE.equals(wxMpMassPreviewMessage.getMsgType())) { + if (WxConsts.MassMsgType.IMAGE.equals(wxMpMassPreviewMessage.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", wxMpMassPreviewMessage.getMediaId()); - jsonObject.add(WxConsts.MASS_MSG_IMAGE, sub); + jsonObject.add(WxConsts.MassMsgType.IMAGE, sub); } - if (WxConsts.MASS_MSG_VIDEO.equals(wxMpMassPreviewMessage.getMsgType())) { + if (WxConsts.MassMsgType.MPVIDEO.equals(wxMpMassPreviewMessage.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", wxMpMassPreviewMessage.getMediaId()); - jsonObject.add(WxConsts.MASS_MSG_VIDEO, sub); + jsonObject.add(WxConsts.MassMsgType.MPVIDEO, sub); } jsonObject.addProperty("msgtype", wxMpMassPreviewMessage.getMsgType()); return jsonObject; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassSendResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassSendResultAdapter.java index 5a652f0aab..aebbca5ec8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassSendResultAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassSendResultAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java index 5b133b0ed1..b73540d1f1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.JsonElement; @@ -14,9 +6,15 @@ import com.google.gson.JsonSerializer; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.bean.WxMpMassTagMessage; +import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Type; +/** + * 群发消息json转换适配器. + * + * @author chanjarster + */ public class WxMpMassTagMessageGsonAdapter implements JsonSerializer { @Override @@ -32,33 +30,38 @@ public JsonElement serialize(WxMpMassTagMessage message, Type typeOfSrc, JsonSer } messageJson.add("filter", filter); - if (WxConsts.MASS_MSG_NEWS.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.MPNEWS.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_NEWS, sub); + messageJson.add(WxConsts.MassMsgType.MPNEWS, sub); } - if (WxConsts.MASS_MSG_TEXT.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.TEXT.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("content", message.getContent()); - messageJson.add(WxConsts.MASS_MSG_TEXT, sub); + messageJson.add(WxConsts.MassMsgType.TEXT, sub); } - if (WxConsts.MASS_MSG_VOICE.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.VOICE.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_VOICE, sub); + messageJson.add(WxConsts.MassMsgType.VOICE, sub); } - if (WxConsts.MASS_MSG_IMAGE.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.IMAGE.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_IMAGE, sub); + messageJson.add(WxConsts.MassMsgType.IMAGE, sub); } - if (WxConsts.MASS_MSG_VIDEO.equals(message.getMsgType())) { + if (WxConsts.MassMsgType.MPVIDEO.equals(message.getMsgType())) { JsonObject sub = new JsonObject(); sub.addProperty("media_id", message.getMediaId()); - messageJson.add(WxConsts.MASS_MSG_VIDEO, sub); + messageJson.add(WxConsts.MassMsgType.MPVIDEO, sub); } messageJson.addProperty("msgtype", message.getMsgType()); messageJson.addProperty("send_ignore_reprint", message.isSendIgnoreReprint() ? 0 : 1); + + if (StringUtils.isNotEmpty(message.getClientMsgId())) { + messageJson.addProperty("clientmsgid", message.getClientMsgId()); + } + return messageJson; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassUploadResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassUploadResultAdapter.java index e20175d767..b526e8ce6e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassUploadResultAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassUploadResultAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassVideoAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassVideoAdapter.java index e7bd8c3997..f6570f4881 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassVideoAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassVideoAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.JsonElement; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialArticleUpdateGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialArticleUpdateGsonAdapter.java index 2705462e8a..cb7784e9f5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialArticleUpdateGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialArticleUpdateGsonAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.JsonElement; @@ -13,7 +5,7 @@ import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import me.chanjar.weixin.mp.bean.material.WxMpMaterialArticleUpdate; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; +import me.chanjar.weixin.mp.bean.material.WxMpNewsArticle; import java.lang.reflect.Type; @@ -24,7 +16,7 @@ public JsonElement serialize(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate JsonObject articleUpdateJson = new JsonObject(); articleUpdateJson.addProperty("media_id", wxMpMaterialArticleUpdate.getMediaId()); articleUpdateJson.addProperty("index", wxMpMaterialArticleUpdate.getIndex()); - articleUpdateJson.add("articles", WxMpGsonBuilder.create().toJsonTree(wxMpMaterialArticleUpdate.getArticles(), WxMpMaterialNews.WxMpMaterialNewsArticle.class)); + articleUpdateJson.add("articles", WxMpGsonBuilder.create().toJsonTree(wxMpMaterialArticleUpdate.getArticles(), WxMpNewsArticle.class)); return articleUpdateJson; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialCountResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialCountResultAdapter.java index 3f96addfc7..ce002a28cc 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialCountResultAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialCountResultAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonAdapter.java index 1460813e82..b19fa422e2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonItemAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonItemAdapter.java index 7b4bee2e00..d17bb50c39 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonItemAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialFileBatchGetGsonItemAdapter.java @@ -1,38 +1,30 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialFileBatchGetResult; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem; import java.lang.reflect.Type; import java.util.Date; -public class WxMpMaterialFileBatchGetGsonItemAdapter implements JsonDeserializer { +public class WxMpMaterialFileBatchGetGsonItemAdapter implements JsonDeserializer { @Override - public WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { - WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem wxMaterialFileBatchGetNewsItem = new WxMpMaterialFileBatchGetResult.WxMaterialFileBatchGetNewsItem(); + public WxMaterialFileBatchGetNewsItem deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxMaterialFileBatchGetNewsItem newsItem = new WxMaterialFileBatchGetNewsItem(); JsonObject json = jsonElement.getAsJsonObject(); if (json.get("media_id") != null && !json.get("media_id").isJsonNull()) { - wxMaterialFileBatchGetNewsItem.setMediaId(GsonHelper.getAsString(json.get("media_id"))); + newsItem.setMediaId(GsonHelper.getAsString(json.get("media_id"))); } if (json.get("update_time") != null && !json.get("update_time").isJsonNull()) { - wxMaterialFileBatchGetNewsItem.setUpdateTime(new Date(1000 * GsonHelper.getAsLong(json.get("update_time")))); + newsItem.setUpdateTime(new Date(1000 * GsonHelper.getAsLong(json.get("update_time")))); } if (json.get("name") != null && !json.get("name").isJsonNull()) { - wxMaterialFileBatchGetNewsItem.setName(GsonHelper.getAsString(json.get("name"))); + newsItem.setName(GsonHelper.getAsString(json.get("name"))); } if (json.get("url") != null && !json.get("url").isJsonNull()) { - wxMaterialFileBatchGetNewsItem.setUrl(GsonHelper.getAsString(json.get("url"))); + newsItem.setUrl(GsonHelper.getAsString(json.get("url"))); } - return wxMaterialFileBatchGetNewsItem; + return newsItem; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonAdapter.java index 2a88a8f236..203b1ad8ce 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonItemAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonItemAdapter.java index e56ecf4239..2c455e76da 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonItemAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsBatchGetGsonItemAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java index 8da6dacdc4..3204090b75 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java @@ -1,17 +1,13 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; +import me.chanjar.weixin.mp.bean.material.WxMpNewsArticle; import java.lang.reflect.Type; +import java.text.SimpleDateFormat; +import java.util.Date; public class WxMpMaterialNewsGsonAdapter implements JsonSerializer, JsonDeserializer { @@ -20,12 +16,22 @@ public JsonElement serialize(WxMpMaterialNews wxMpMaterialNews, Type typeOfSrc, JsonObject newsJson = new JsonObject(); JsonArray articleJsonArray = new JsonArray(); - for (WxMpMaterialNews.WxMpMaterialNewsArticle article : wxMpMaterialNews.getArticles()) { - JsonObject articleJson = WxMpGsonBuilder.create().toJsonTree(article, WxMpMaterialNews.WxMpMaterialNewsArticle.class).getAsJsonObject(); + for (WxMpNewsArticle article : wxMpMaterialNews.getArticles()) { + JsonObject articleJson = WxMpGsonBuilder.create().toJsonTree(article, WxMpNewsArticle.class).getAsJsonObject(); articleJsonArray.add(articleJson); } newsJson.add("articles", articleJsonArray); + if (wxMpMaterialNews.getCreateTime() != null) { + newsJson.addProperty("create_time", + SimpleDateFormat.getDateTimeInstance().format(wxMpMaterialNews.getCreateTime())); + } + + if (wxMpMaterialNews.getUpdateTime() != null) { + newsJson.addProperty("update_time", + SimpleDateFormat.getDateTimeInstance().format(wxMpMaterialNews.getUpdateTime())); + } + return newsJson; } @@ -37,10 +43,21 @@ public WxMpMaterialNews deserialize(JsonElement jsonElement, Type type, JsonDese JsonArray articles = json.getAsJsonArray("news_item"); for (JsonElement article1 : articles) { JsonObject articleInfo = article1.getAsJsonObject(); - WxMpMaterialNews.WxMpMaterialNewsArticle article = WxMpGsonBuilder.create().fromJson(articleInfo, WxMpMaterialNews.WxMpMaterialNewsArticle.class); + WxMpNewsArticle article = WxMpGsonBuilder.create().fromJson(articleInfo, WxMpNewsArticle.class); wxMpMaterialNews.addArticle(article); } } + + if (json.get("create_time") != null && !json.get("create_time").isJsonNull()) { + Date createTime = new Date(GsonHelper.getAsLong(json.get("create_time"))* 1000); + wxMpMaterialNews.setCreateTime(createTime); + } + + if (json.get("update_time") != null && !json.get("update_time").isJsonNull()) { + Date updateTime = new Date(GsonHelper.getAsLong(json.get("update_time"))* 1000); + wxMpMaterialNews.setUpdateTime(updateTime); + } + return wxMpMaterialNews; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialUploadResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialUploadResultAdapter.java index 0ccefd8336..db279ce1d3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialUploadResultAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialUploadResultAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardActivateTempInfoResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardActivateTempInfoResultGsonAdapter.java new file mode 100644 index 0000000000..fc554c4807 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardActivateTempInfoResultGsonAdapter.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardUserInfo; +import me.chanjar.weixin.mp.bean.card.membercard.NameValues; +import me.chanjar.weixin.mp.bean.card.membercard.WxMpMemberCardActivateTempInfoResult; + +import java.lang.reflect.Type; + +/** + * Json to WxMpMemberCardActivateTempInfoResultGsonAdapter 的转换适配器 + * + * @author thomas(351402401 @ qq.com) + * @version 2019/4/26 + */ +public class WxMpMemberCardActivateTempInfoResultGsonAdapter implements JsonDeserializer { + + @Override + public WxMpMemberCardActivateTempInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxMpMemberCardActivateTempInfoResult result = new WxMpMemberCardActivateTempInfoResult(); + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + result.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); + result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + + JsonObject userInfoJsonObject = jsonObject.getAsJsonObject("info"); + MemberCardUserInfo cardUserInfo = new MemberCardUserInfo(); + + JsonArray commonFieldListObj = userInfoJsonObject.getAsJsonArray("common_field_list"); + NameValues[] commonFieldListValues = new NameValues[commonFieldListObj.size()]; + for (int i = 0; i < commonFieldListObj.size(); i++) { + JsonObject commonField = commonFieldListObj.get(i).getAsJsonObject(); + NameValues commonNameValues = new NameValues(); + commonNameValues.setName(GsonHelper.getString(commonField, "name")); + commonNameValues.setValue(GsonHelper.getString(commonField, "value")); + commonFieldListValues[i] = commonNameValues; + } + cardUserInfo.setCommonFieldList(commonFieldListValues); + + JsonArray customFieldListObj = userInfoJsonObject.getAsJsonArray("custom_field_list"); + NameValues[] customFieldListValues = new NameValues[customFieldListObj.size()]; + for (int i = 0; i < customFieldListObj.size(); i++) { + JsonObject customField = customFieldListObj.get(i).getAsJsonObject(); + NameValues customNameValues = new NameValues(); + customNameValues.setName(GsonHelper.getString(customField, "name")); + customNameValues.setValue(GsonHelper.getString(customField, "value")); + + JsonArray valueListArray = customField.getAsJsonArray("value_list"); + String[] valueList = new String[valueListArray.size()]; + for (int j = 0; j < valueListArray.size(); j++) { + valueList[j] = valueListArray.get(j).getAsString(); + } + customNameValues.setValueList(valueList); + customFieldListValues[i] = customNameValues; + } + cardUserInfo.setCustomFieldList(customFieldListValues); + + result.setUserInfo(cardUserInfo); + + return result; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUpdateResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUpdateResultGsonAdapter.java new file mode 100644 index 0000000000..2690d3416b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUpdateResultGsonAdapter.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.card.membercard.WxMpMemberCardUpdateResult; + +import java.lang.reflect.Type; + +/** + * Json to WxMpMemberCardUpdateResult 的转换适配器 + * + * @author YuJian + * @version 2017/7/15 + */ +public class WxMpMemberCardUpdateResultGsonAdapter implements JsonDeserializer { + + @Override + public WxMpMemberCardUpdateResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext + jsonDeserializationContext) throws JsonParseException { + + WxMpMemberCardUpdateResult result = new WxMpMemberCardUpdateResult(); + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + result.setOpenId(GsonHelper.getString(jsonObject, "openid")); + result.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); + result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + result.setResultBalance(GsonHelper.getDouble(jsonObject, "result_balance")); + result.setResultBonus(GsonHelper.getInteger(jsonObject, "result_bonus")); + + return result; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java new file mode 100644 index 0000000000..270e67e8eb --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java @@ -0,0 +1,85 @@ +package me.chanjar.weixin.mp.util.json; + +import java.lang.reflect.Type; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.card.membercard.MemberCardUserInfo; +import me.chanjar.weixin.mp.bean.card.membercard.NameValues; +import me.chanjar.weixin.mp.bean.card.membercard.WxMpMemberCardUserInfoResult; + +import static me.chanjar.weixin.common.util.json.GsonHelper.getString; + +/** + * Json to WxMpMemberCardUserInfoResult 的转换适配器 + * + * @author YuJian(mgcnrx11 @ gmail.com) + * @version 2017/7/11 + */ +public class WxMpMemberCardUserInfoResultGsonAdapter implements JsonDeserializer { + + @Override + public WxMpMemberCardUserInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) + throws JsonParseException { + WxMpMemberCardUserInfoResult result = new WxMpMemberCardUserInfoResult(); + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + result.setOpenId(getString(jsonObject, "openid")); + result.setErrorCode(getString(jsonObject, "errcode")); + result.setErrorMsg(getString(jsonObject, "errmsg")); + result.setNickname(getString(jsonObject, "nickname")); + result.setMembershipNumber(getString(jsonObject, "membership_number")); + result.setBonus(GsonHelper.getInteger(jsonObject, "bonus")); + result.setBalance(GsonHelper.getDouble(jsonObject, "balance")); + result.setSex(getString(jsonObject, "sex")); + result.setUserCardStatus(getString(jsonObject, "user_card_status")); + result.setHasActive(GsonHelper.getBoolean(jsonObject, "has_active")); + + JsonObject userInfoJsonObject = jsonObject.getAsJsonObject("user_info"); + if (userInfoJsonObject == null) { + return result; + } + + JsonArray commonFieldListObj = userInfoJsonObject.getAsJsonArray("common_field_list"); + NameValues[] commonFieldListValues = new NameValues[commonFieldListObj.size()]; + for (int i = 0; i < commonFieldListObj.size(); i++) { + JsonObject commonField = commonFieldListObj.get(i).getAsJsonObject(); + NameValues commonNameValues = new NameValues(); + commonNameValues.setName(getString(commonField, "name")); + commonNameValues.setValue(getString(commonField, "value")); + commonFieldListValues[i] = commonNameValues; + } + + MemberCardUserInfo cardUserInfo = new MemberCardUserInfo(); + cardUserInfo.setCommonFieldList(commonFieldListValues); + + JsonArray customFieldListObj = userInfoJsonObject.getAsJsonArray("custom_field_list"); + NameValues[] customFieldListValues = new NameValues[customFieldListObj.size()]; + for (int i = 0; i < customFieldListObj.size(); i++) { + JsonObject customField = customFieldListObj.get(i).getAsJsonObject(); + NameValues customNameValues = new NameValues(); + customNameValues.setName(getString(customField, "name")); + customNameValues.setValue(getString(customField, "value")); + + JsonArray valueListArray = customField.getAsJsonArray("value_list"); + String[] valueList = new String[valueListArray.size()]; + for (int j = 0; j < valueListArray.size(); j++) { + valueList[j] = valueListArray.get(j).getAsString(); + } + customNameValues.setValueList(valueList); + customFieldListValues[i] = customNameValues; + } + + cardUserInfo.setCustomFieldList(customFieldListValues); + + result.setUserInfo(cardUserInfo); + + return result; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpNewsArticleGsonAdapter.java similarity index 64% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpNewsArticleGsonAdapter.java index a74877f126..ff88cb9cbd 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpNewsArticleGsonAdapter.java @@ -1,23 +1,18 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; +import me.chanjar.weixin.mp.bean.material.WxMpNewsArticle; +import org.apache.commons.lang3.BooleanUtils; import java.lang.reflect.Type; -public class WxMpMaterialNewsArticleGsonAdapter implements JsonSerializer, JsonDeserializer { - +/** + * @author codepiano + */ +public class WxMpNewsArticleGsonAdapter implements JsonSerializer, JsonDeserializer { @Override - public JsonElement serialize(WxMpMaterialNews.WxMpMaterialNewsArticle article, Type typeOfSrc, JsonSerializationContext context) { + public JsonElement serialize(WxMpNewsArticle article, Type typeOfSrc, JsonSerializationContext context) { JsonObject articleJson = new JsonObject(); articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); @@ -37,13 +32,23 @@ public JsonElement serialize(WxMpMaterialNews.WxMpMaterialNewsArticle article, T if (null != article.getUrl()) { articleJson.addProperty("url", article.getUrl()); } + + if (null != article.getNeedOpenComment()) { + articleJson.addProperty("need_open_comment", + BooleanUtils.toInteger(article.getNeedOpenComment(), 1, 0)); + } + + if (null != article.getOnlyFansCanComment()) { + articleJson.addProperty("only_fans_can_comment", + BooleanUtils.toInteger(article.getOnlyFansCanComment(), 1, 0)); + } return articleJson; } @Override - public WxMpMaterialNews.WxMpMaterialNewsArticle deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + public WxMpNewsArticle deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { JsonObject articleInfo = jsonElement.getAsJsonObject(); - WxMpMaterialNews.WxMpMaterialNewsArticle article = new WxMpMaterialNews.WxMpMaterialNewsArticle(); + WxMpNewsArticle article = new WxMpNewsArticle(); JsonElement title = articleInfo.get("title"); if (title != null && !title.isJsonNull()) { @@ -75,12 +80,22 @@ public WxMpMaterialNews.WxMpMaterialNewsArticle deserialize(JsonElement jsonElem } JsonElement showCoverPic = articleInfo.get("show_cover_pic"); if (showCoverPic != null && !showCoverPic.isJsonNull()) { - article.setShowCoverPic(GsonHelper.getAsBoolean(showCoverPic)); + article.setShowCoverPic(BooleanUtils.toBoolean(showCoverPic.getAsInt())); } JsonElement url = articleInfo.get("url"); if (url != null && !url.isJsonNull()) { article.setUrl(GsonHelper.getAsString(url)); } + + JsonElement needOpenComment = articleInfo.get("need_open_comment"); + if (needOpenComment != null && !needOpenComment.isJsonNull()) { + article.setNeedOpenComment(BooleanUtils.toBoolean(needOpenComment.getAsInt())); + } + + JsonElement onlyFansCanComment = articleInfo.get("only_fans_can_comment"); + if (onlyFansCanComment != null && !onlyFansCanComment.isJsonNull()) { + article.setOnlyFansCanComment(BooleanUtils.toBoolean(onlyFansCanComment.getAsInt())); + } return article; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java index 247182d2c4..f5b2a87d79 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSubscribeMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSubscribeMessageGsonAdapter.java new file mode 100644 index 0000000000..6f32195a7c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSubscribeMessageGsonAdapter.java @@ -0,0 +1,59 @@ +package me.chanjar.weixin.mp.util.json; + +import java.lang.reflect.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage; + +/** + * @author Mklaus + * @date 2018-01-22 下午12:31 + */ +public class WxMpSubscribeMessageGsonAdapter implements JsonSerializer { + + @Override + public JsonElement serialize(WxMpSubscribeMessage message, Type type, JsonSerializationContext jsonSerializationContext) { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("touser", message.getToUser()); + messageJson.addProperty("template_id", message.getTemplateId()); + + if (message.getUrl() != null) { + messageJson.addProperty("url", message.getUrl()); + } + + final WxMpSubscribeMessage.MiniProgram miniProgram = message.getMiniProgram(); + if (miniProgram != null) { + JsonObject miniProgramJson = new JsonObject(); + miniProgramJson.addProperty("appid", miniProgram.getAppid()); + if (miniProgram.isUsePath()) { + miniProgramJson.addProperty("path", miniProgram.getPagePath()); + } else { + miniProgramJson.addProperty("pagepath", miniProgram.getPagePath()); + } + messageJson.add("miniprogram", miniProgramJson); + } + + messageJson.addProperty("scene", message.getScene()); + messageJson.addProperty("title", message.getTitle()); + + JsonObject data = new JsonObject(); + messageJson.add("data", data); + + JsonObject content = new JsonObject(); + data.add("content", content); + + if (message.getContentValue() != null) { + content.addProperty("value", message.getContentValue()); + } + + if (message.getContentColor() != null) { + content.addProperty("color", message.getContentColor()); + } + + return messageJson; + + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java index d6e0ff1d65..73f8c4e3ab 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java @@ -1,13 +1,7 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; +import java.lang.reflect.Type; + import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; @@ -15,8 +9,9 @@ import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; -import java.lang.reflect.Type; - +/** + * @author chanjarster + */ public class WxMpTemplateMessageGsonAdapter implements JsonSerializer { @Override @@ -28,10 +23,15 @@ public JsonElement serialize(WxMpTemplateMessage message, Type typeOfSrc, JsonSe messageJson.addProperty("url", message.getUrl()); } - if(message.getMiniProgram() !=null){ + final WxMpTemplateMessage.MiniProgram miniProgram = message.getMiniProgram(); + if (miniProgram != null) { JsonObject miniProgramJson = new JsonObject(); - miniProgramJson.addProperty("appid", message.getMiniProgram().getAppid()); - miniProgramJson.addProperty("pagepath", message.getMiniProgram().getPagePath()); + miniProgramJson.addProperty("appid", miniProgram.getAppid()); + if (miniProgram.isUsePath()) { + miniProgramJson.addProperty("path", miniProgram.getPagePath()); + } else { + miniProgramJson.addProperty("pagepath", miniProgram.getPagePath()); + } messageJson.add("miniprogram", miniProgramJson); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserCumulateGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserCumulateGsonAdapter.java index 7897096e34..517d09880c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserCumulateGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserCumulateGsonAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java index e47d405fdf..910ae8c89f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java @@ -1,51 +1,58 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; -import com.google.gson.*; +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.mp.bean.result.WxMpUser; -import java.lang.reflect.Type; - public class WxMpUserGsonAdapter implements JsonDeserializer { @Override public WxMpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject o = json.getAsJsonObject(); - WxMpUser wxMpUser = new WxMpUser(); + WxMpUser user = new WxMpUser(); Integer subscribe = GsonHelper.getInteger(o, "subscribe"); if (subscribe != null) { - wxMpUser.setSubscribe(!new Integer(0).equals(subscribe)); + user.setSubscribe(!new Integer(0).equals(subscribe)); } - wxMpUser.setCity(GsonHelper.getString(o, "city")); - wxMpUser.setCountry(GsonHelper.getString(o, "country")); - wxMpUser.setHeadImgUrl(GsonHelper.getString(o, "headimgurl")); - wxMpUser.setLanguage(GsonHelper.getString(o, "language")); - wxMpUser.setNickname(GsonHelper.getString(o, "nickname")); - wxMpUser.setOpenId(GsonHelper.getString(o, "openid")); - wxMpUser.setProvince(GsonHelper.getString(o, "province")); - wxMpUser.setSubscribeTime(GsonHelper.getLong(o, "subscribe_time")); - wxMpUser.setUnionId(GsonHelper.getString(o, "unionid")); - Integer sexId = GsonHelper.getInteger(o, "sex"); - wxMpUser.setRemark(GsonHelper.getString(o, "remark")); - wxMpUser.setGroupId(GsonHelper.getInteger(o, "groupid")); - wxMpUser.setTagIds(GsonHelper.getLongArray(o, "tagid_list")); - wxMpUser.setSexId(sexId); - if (new Integer(1).equals(sexId)) { - wxMpUser.setSex("男"); - } else if (new Integer(2).equals(sexId)) { - wxMpUser.setSex("女"); - } else { - wxMpUser.setSex("未知"); + user.setCity(GsonHelper.getString(o, "city")); + user.setCountry(GsonHelper.getString(o, "country")); + user.setHeadImgUrl(GsonHelper.getString(o, "headimgurl")); + user.setLanguage(GsonHelper.getString(o, "language")); + user.setNickname(GsonHelper.getString(o, "nickname")); + user.setOpenId(GsonHelper.getString(o, "openid")); + user.setProvince(GsonHelper.getString(o, "province")); + user.setSubscribeTime(GsonHelper.getLong(o, "subscribe_time")); + user.setUnionId(GsonHelper.getString(o, "unionid")); + user.setRemark(GsonHelper.getString(o, "remark")); + user.setGroupId(GsonHelper.getInteger(o, "groupid")); + user.setTagIds(GsonHelper.getLongArray(o, "tagid_list")); + user.setPrivileges(GsonHelper.getStringArray(o, "privilege")); + user.setSubscribeScene(GsonHelper.getString(o, "subscribe_scene")); + user.setQrScene(GsonHelper.getString(o, "qr_scene")); + user.setQrSceneStr(GsonHelper.getString(o, "qr_scene_str")); + + Integer sex = GsonHelper.getInteger(o, "sex"); + if (sex != null) { + user.setSex(sex); + switch (sex) { + case 1: + user.setSexDesc("男"); + break; + case 2: + user.setSexDesc("女"); + break; + default: + user.setSexDesc("未知"); + } + } - return wxMpUser; + return user; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserSummaryGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserSummaryGsonAdapter.java index b101e01ff4..b5a2a782ac 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserSummaryGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserSummaryGsonAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxQrCodeTicketAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxQrCodeTicketAdapter.java index bd1bbd90ff..c052826bcf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxQrCodeTicketAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxQrCodeTicketAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; @@ -28,7 +20,7 @@ public WxMpQrCodeTicket deserialize(JsonElement json, Type typeOfT, JsonDeserial ticket.setTicket(GsonHelper.getAsString(ticketJsonObject.get("ticket"))); } if (ticketJsonObject.get("expire_seconds") != null && !ticketJsonObject.get("expire_seconds").isJsonNull()) { - ticket.setExpire_seconds(GsonHelper.getAsPrimitiveInt(ticketJsonObject.get("expire_seconds"))); + ticket.setExpireSeconds(GsonHelper.getAsPrimitiveInt(ticketJsonObject.get("expire_seconds"))); } if (ticketJsonObject.get("url") != null && !ticketJsonObject.get("url").isJsonNull()) { ticket.setUrl(GsonHelper.getAsString(ticketJsonObject.get("url"))); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java index e150a63fc5..619017e88d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java @@ -1,11 +1,3 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteApacheHttpRequestExecutor.java new file mode 100644 index 0000000000..72fcaf1b3f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteApacheHttpRequestExecutor.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialDeleteApacheHttpRequestExecutor extends MaterialDeleteRequestExecutor { + public MaterialDeleteApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public Boolean execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + + Map params = new HashMap<>(); + params.put("media_id", materialId); + httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return true; + } + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteJoddHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteJoddHttpRequestExecutor.java new file mode 100644 index 0000000000..afc99d62c0 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteJoddHttpRequestExecutor.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; + +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialDeleteJoddHttpRequestExecutor extends MaterialDeleteRequestExecutor { + public MaterialDeleteJoddHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public Boolean execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + HttpRequest request = HttpRequest.post(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + request.query("media_id", materialId); + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String responseContent = response.bodyText(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return true; + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteOkhttpRequestExecutor.java new file mode 100644 index 0000000000..87d8c3df93 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteOkhttpRequestExecutor.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import com.google.common.collect.ImmutableMap; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/5 + */ +public class MaterialDeleteOkhttpRequestExecutor extends MaterialDeleteRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public MaterialDeleteOkhttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) + throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + @Override + public Boolean execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + logger.debug("MaterialDeleteOkhttpRequestExecutor is running"); + + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), + WxGsonBuilder.create().toJson(ImmutableMap.of("media_id", materialId))); + Request request = new Request.Builder().url(uri).post(requestBody).build(); + Response response = requestHttp.getRequestHttpClient().newCall(request).execute(); + String responseContent = response.body().string(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return true; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteRequestExecutor.java new file mode 100644 index 0000000000..18e696938b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteRequestExecutor.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; + +public abstract class MaterialDeleteRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MaterialDeleteRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MaterialDeleteApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new MaterialDeleteJoddHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new MaterialDeleteOkhttpRequestExecutor(requestHttp); + default: + return null; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoApacheHttpRequestExecutor.java new file mode 100644 index 0000000000..d1326429df --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoApacheHttpRequestExecutor.java @@ -0,0 +1,59 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import com.google.common.collect.ImmutableMap; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * httpclient 实现的素材请求执行器. + * + * @author ecoolper + * @date 2017/5/5 + */ +public class MaterialNewsInfoApacheHttpRequestExecutor + extends MaterialNewsInfoRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public MaterialNewsInfoApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialNews execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + + httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(ImmutableMap.of("media_id", materialId)))); + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + this.logger.debug("响应原始数据:{}", responseContent); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpGsonBuilder.create().fromJson(responseContent, WxMpMaterialNews.class); + } + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoJoddHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoJoddHttpRequestExecutor.java new file mode 100644 index 0000000000..59f0710692 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoJoddHttpRequestExecutor.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import com.google.common.collect.ImmutableMap; +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialNewsInfoJoddHttpRequestExecutor extends MaterialNewsInfoRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + public MaterialNewsInfoJoddHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialNews execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + + HttpRequest request = HttpRequest.post(uri) + .withConnectionProvider(requestHttp.getRequestHttpClient()) + .body(WxGsonBuilder.create().toJson(ImmutableMap.of("media_id", materialId))); + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + + String responseContent = response.bodyText(); + this.logger.debug("响应原始数据:{}", responseContent); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpGsonBuilder.create().fromJson(responseContent, WxMpMaterialNews.class); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoOkhttpRequestExecutor.java new file mode 100644 index 0000000000..abee9055af --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoOkhttpRequestExecutor.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import com.google.common.collect.ImmutableMap; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import okhttp3.*; + +import java.io.IOException; + +/** + * . + * + * @author ecoolper + * @date 2017/5/5 + */ +@Slf4j +public class MaterialNewsInfoOkhttpRequestExecutor extends MaterialNewsInfoRequestExecutor { + public MaterialNewsInfoOkhttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialNews execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), + WxGsonBuilder.create().toJson(ImmutableMap.of("media_id", materialId))); + Request request = new Request.Builder().url(uri).post(requestBody).build(); + + Response response = requestHttp.getRequestHttpClient().newCall(request).execute(); + String responseContent = response.body().string(); + log.debug("响应原始数据:{}", responseContent); + + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpGsonBuilder.create().fromJson(responseContent, WxMpMaterialNews.class); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoRequestExecutor.java new file mode 100644 index 0000000000..41374809c6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoRequestExecutor.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; + +public abstract class MaterialNewsInfoRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MaterialNewsInfoRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MaterialNewsInfoApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new MaterialNewsInfoJoddHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new MaterialNewsInfoOkhttpRequestExecutor(requestHttp); + default: + //TODO 需要优化抛出异常 + return null; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java new file mode 100644 index 0000000000..a89687cd22 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java @@ -0,0 +1,77 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.material.WxMpMaterial; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; +import org.apache.http.Consts; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.StringBody; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Map; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialUploadApacheHttpRequestExecutor extends MaterialUploadRequestExecutor { + public MaterialUploadApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig response = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(response); + } + + if (material == null) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("非法请求,material参数为空").build()); + } + + File file = material.getFile(); + if (file == null || !file.exists()) { + throw new FileNotFoundException(); + } + + MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); + multipartEntityBuilder + .addBinaryBody("media", file) + .setMode(HttpMultipartMode.RFC6532); + Map form = material.getForm(); + if (material.getForm() != null) { + multipartEntityBuilder.addPart("description", + new StringBody(WxGsonBuilder.create().toJson(form), ContentType.create("text/plain", Consts.UTF_8))); + } + + httpPost.setEntity(multipartEntityBuilder.build()); + httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpMaterialUploadResult.fromJson(responseContent); + } + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadJoddHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadJoddHttpRequestExecutor.java new file mode 100644 index 0000000000..7699f2f202 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadJoddHttpRequestExecutor.java @@ -0,0 +1,62 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.material.WxMpMaterial; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Map; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialUploadJoddHttpRequestExecutor extends MaterialUploadRequestExecutor { + public MaterialUploadJoddHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material, WxType wxType) throws WxErrorException, IOException { + HttpRequest request = HttpRequest.post(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + if (material == null) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("非法请求,material参数为空").build()); + } + + File file = material.getFile(); + if (file == null || !file.exists()) { + throw new FileNotFoundException(); + } + request.form("media", file); + Map form = material.getForm(); + if (material.getForm() != null) { + request.form("description", WxGsonBuilder.create().toJson(form)); + } + + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String responseContent = response.bodyText(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpMaterialUploadResult.fromJson(responseContent); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadOkhttpRequestExecutor.java new file mode 100644 index 0000000000..f4654f9fb1 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadOkhttpRequestExecutor.java @@ -0,0 +1,64 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.material.WxMpMaterial; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; +import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Map; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialUploadOkhttpRequestExecutor extends MaterialUploadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public MaterialUploadOkhttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material, WxType wxType) throws WxErrorException, IOException { + logger.debug("MaterialUploadOkhttpRequestExecutor is running"); + if (material == null) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("非法请求,material参数为空").build()); + } + File file = material.getFile(); + if (file == null || !file.exists()) { + throw new FileNotFoundException(); + } + + OkHttpClient client = requestHttp.getRequestHttpClient(); + + MultipartBody.Builder bodyBuilder = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("media", + file.getName(), + RequestBody.create(MediaType.parse("application/octet-stream"), file)); + Map form = material.getForm(); + if (form != null) { + bodyBuilder.addFormDataPart("description", WxGsonBuilder.create().toJson(form)); + } + + Request request = new Request.Builder().url(uri).post(bodyBuilder.build()).build(); + Response response = client.newCall(request).execute(); + String responseContent = response.body().string(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpMaterialUploadResult.fromJson(responseContent); + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadRequestExecutor.java new file mode 100644 index 0000000000..9044d052a8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadRequestExecutor.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.mp.bean.material.WxMpMaterial; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; + +/** + * @author codepiano + */ +public abstract class MaterialUploadRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MaterialUploadRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, WxMpMaterial data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MaterialUploadApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new MaterialUploadJoddHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new MaterialUploadOkhttpRequestExecutor(requestHttp); + default: + return null; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVideoInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoApacheHttpRequestExecutor.java similarity index 50% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVideoInfoRequestExecutor.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoApacheHttpRequestExecutor.java index 4eb15e46ac..7034379fbe 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVideoInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoApacheHttpRequestExecutor.java @@ -1,9 +1,10 @@ -package me.chanjar.weixin.mp.util.http; +package me.chanjar.weixin.mp.util.requestexecuter.material; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; import org.apache.http.HttpHost; @@ -17,34 +18,35 @@ import java.util.HashMap; import java.util.Map; -public class MaterialVideoInfoRequestExecutor implements RequestExecutor { - - public MaterialVideoInfoRequestExecutor() { - super(); +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialVideoInfoApacheHttpRequestExecutor extends MaterialVideoInfoRequestExecutor { + public MaterialVideoInfoApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); } @Override - public WxMpMaterialVideoInfoResult execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String materialId) throws WxErrorException, IOException { + public WxMpMaterialVideoInfoResult execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); httpPost.setConfig(config); } Map params = new HashMap<>(); params.put("media_id", materialId); httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - try(CloseableHttpResponse response = httpclient.execute(httpPost)){ + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); + WxError error = WxError.fromJson(responseContent, WxType.MP); if (error.getErrorCode() != 0) { throw new WxErrorException(error); } else { return WxMpMaterialVideoInfoResult.fromJson(responseContent); } - }finally { + } finally { httpPost.releaseConnection(); } } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoJoddHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoJoddHttpRequestExecutor.java new file mode 100644 index 0000000000..f142c21788 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoJoddHttpRequestExecutor.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; + +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialVideoInfoJoddHttpRequestExecutor extends MaterialVideoInfoRequestExecutor { + public MaterialVideoInfoJoddHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialVideoInfoResult execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + HttpRequest request = HttpRequest.post(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + request.query("media_id", materialId); + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String responseContent = response.bodyText(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpMaterialVideoInfoResult.fromJson(responseContent); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoOkhttpRequestExecutor.java new file mode 100644 index 0000000000..2e38ab003b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoOkhttpRequestExecutor.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import com.google.common.collect.ImmutableMap; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; +import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialVideoInfoOkhttpRequestExecutor extends MaterialVideoInfoRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public MaterialVideoInfoOkhttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMpMaterialVideoInfoResult execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + logger.debug("MaterialVideoInfoOkhttpRequestExecutor is running"); + + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), + WxGsonBuilder.create().toJson(ImmutableMap.of("media_id", materialId))); + Request request = new Request.Builder().url(uri).post(requestBody).build(); + Response response = requestHttp.getRequestHttpClient().newCall(request).execute(); + String responseContent = response.body().string(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } else { + return WxMpMaterialVideoInfoResult.fromJson(responseContent); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoRequestExecutor.java new file mode 100644 index 0000000000..5ea6fae0b2 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoRequestExecutor.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + + +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; + +public abstract class MaterialVideoInfoRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MaterialVideoInfoRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MaterialVideoInfoApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new MaterialVideoInfoJoddHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new MaterialVideoInfoOkhttpRequestExecutor(requestHttp); + default: + return null; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadApacheHttpRequestExecutor.java similarity index 54% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadApacheHttpRequestExecutor.java index ceda65a687..3c08b1346c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadApacheHttpRequestExecutor.java @@ -1,12 +1,11 @@ -package me.chanjar.weixin.mp.util.http; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; +package me.chanjar.weixin.mp.util.requestexecuter.material; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; @@ -15,39 +14,38 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.http.InputStreamResponseHandler; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - -public class MaterialVoiceAndImageDownloadRequestExecutor implements RequestExecutor { - - - public MaterialVoiceAndImageDownloadRequestExecutor() { - super(); - } +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; - public MaterialVoiceAndImageDownloadRequestExecutor(File tmpDirFile) { - super(); +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialVoiceAndImageDownloadApacheHttpRequestExecutor extends MaterialVoiceAndImageDownloadRequestExecutor { + public MaterialVoiceAndImageDownloadApacheHttpRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + super(requestHttp, tmpDirFile); } @Override - public InputStream execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String materialId) throws WxErrorException, IOException { + public InputStream execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); httpPost.setConfig(config); } Map params = new HashMap<>(); params.put("media_id", materialId); httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); - try (CloseableHttpResponse response = httpclient.execute(httpPost); - InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);){ + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { // 下载媒体文件出错 byte[] responseContent = IOUtils.toByteArray(inputStream); - String responseContentString = new String(responseContent, "UTF-8"); + String responseContentString = new String(responseContent, StandardCharsets.UTF_8); if (responseContentString.length() < 100) { try { WxError wxError = WxGsonBuilder.create().fromJson(responseContentString, WxError.class); @@ -59,9 +57,8 @@ public InputStream execute(CloseableHttpClient httpclient, HttpHost httpProxy, S } } return new ByteArrayInputStream(responseContent); - }finally { + } finally { httpPost.releaseConnection(); } } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadJoddHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadJoddHttpRequestExecutor.java new file mode 100644 index 0000000000..1a4c25590c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadJoddHttpRequestExecutor.java @@ -0,0 +1,58 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.io.IOUtils; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialVoiceAndImageDownloadJoddHttpRequestExecutor extends MaterialVoiceAndImageDownloadRequestExecutor { + public MaterialVoiceAndImageDownloadJoddHttpRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + super(requestHttp, tmpDirFile); + } + + @Override + public InputStream execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + HttpRequest request = HttpRequest.post(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + request.query("media_id", materialId); + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + try (InputStream inputStream = new ByteArrayInputStream(response.bodyBytes())) { + // 下载媒体文件出错 + byte[] responseContent = IOUtils.toByteArray(inputStream); + String responseContentString = new String(responseContent, StandardCharsets.UTF_8); + if (responseContentString.length() < 100) { + try { + WxError wxError = WxGsonBuilder.create().fromJson(responseContentString, WxError.class); + if (wxError.getErrorCode() != 0) { + throw new WxErrorException(wxError); + } + } catch (com.google.gson.JsonSyntaxException ex) { + return new ByteArrayInputStream(responseContent); + } + } + return new ByteArrayInputStream(responseContent); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadOkhttpRequestExecutor.java new file mode 100644 index 0000000000..217ae3d215 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadOkhttpRequestExecutor.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import com.google.common.collect.ImmutableMap; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import okhttp3.*; +import okio.BufferedSink; +import okio.Okio; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class MaterialVoiceAndImageDownloadOkhttpRequestExecutor extends MaterialVoiceAndImageDownloadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public MaterialVoiceAndImageDownloadOkhttpRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + super(requestHttp, tmpDirFile); + } + + @Override + public InputStream execute(String uri, String materialId, WxType wxType) throws WxErrorException, IOException { + logger.debug("MaterialVoiceAndImageDownloadOkhttpRequestExecutor is running"); + OkHttpClient client = requestHttp.getRequestHttpClient(); + + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), + WxGsonBuilder.create().toJson(ImmutableMap.of("media_id", materialId))); + Request request = new Request.Builder().url(uri).get().post(requestBody).build(); + Response response = client.newCall(request).execute(); + String contentTypeHeader = response.header("Content-Type"); + if ("text/plain".equals(contentTypeHeader)) { + String responseContent = response.body().string(); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); + } + + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BufferedSink sink = Okio.buffer(Okio.sink(outputStream))) { + sink.writeAll(response.body().source()); + return new ByteArrayInputStream(outputStream.toByteArray()); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadRequestExecutor.java new file mode 100644 index 0000000000..b482ddbcd1 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadRequestExecutor.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.mp.util.requestexecuter.material; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; + +public abstract class MaterialVoiceAndImageDownloadRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + protected File tmpDirFile; + + public MaterialVoiceAndImageDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + this.requestHttp = requestHttp; + this.tmpDirFile = tmpDirFile; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp, File tmpDirFile) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MaterialVoiceAndImageDownloadApacheHttpRequestExecutor(requestHttp, tmpDirFile); + case JODD_HTTP: + return new MaterialVoiceAndImageDownloadJoddHttpRequestExecutor(requestHttp, tmpDirFile); + case OK_HTTP: + return new MaterialVoiceAndImageDownloadOkhttpRequestExecutor(requestHttp, tmpDirFile); + default: + return null; + } + } + + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaImgUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadApacheHttpRequestExecutor.java similarity index 50% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaImgUploadRequestExecutor.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadApacheHttpRequestExecutor.java index 2129e8a7a9..989e388632 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaImgUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadApacheHttpRequestExecutor.java @@ -1,9 +1,10 @@ -package me.chanjar.weixin.mp.util.http; +package me.chanjar.weixin.mp.util.requestexecuter.media; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -19,18 +20,24 @@ import java.io.IOException; /** - * @author miller + * Created by ecoolper on 2017/5/5. + * + * @author ecoolper */ -public class MediaImgUploadRequestExecutor implements RequestExecutor { +public class MediaImgUploadApacheHttpRequestExecutor extends MediaImgUploadRequestExecutor { + public MediaImgUploadApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + @Override - public WxMediaImgUploadResult execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, File data) throws WxErrorException, IOException { + public WxMediaImgUploadResult execute(String uri, File data, WxType wxType) throws WxErrorException, IOException { if (data == null) { - throw new WxErrorException(WxError.newBuilder().setErrorMsg("文件对象为空").build()); + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("文件对象为空").build()); } HttpPost httpPost = new HttpPost(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); httpPost.setConfig(config); } @@ -42,14 +49,16 @@ public WxMediaImgUploadResult execute(CloseableHttpClient httpclient, HttpHost h httpPost.setEntity(entity); httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); - try (CloseableHttpResponse response = httpclient.execute(httpPost)) { + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); - WxError error = WxError.fromJson(responseContent); + WxError error = WxError.fromJson(responseContent, WxType.MP); if (error.getErrorCode() != 0) { throw new WxErrorException(error); } return WxMediaImgUploadResult.fromJson(responseContent); + } finally { + httpPost.releaseConnection(); } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadHttpRequestExecutor.java new file mode 100644 index 0000000000..76c625141e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadHttpRequestExecutor.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.mp.util.requestexecuter.media; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.util.StringPool; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; + +import java.io.File; +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + * + * @author ecoolper + */ +public class MediaImgUploadHttpRequestExecutor extends MediaImgUploadRequestExecutor { + public MediaImgUploadHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaImgUploadResult execute(String uri, File data, WxType wxType) throws WxErrorException, IOException { + if (data == null) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("文件对象为空").build()); + } + + HttpRequest request = HttpRequest.post(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + request.form("media", data); + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String responseContent = response.bodyText(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return WxMediaImgUploadResult.fromJson(responseContent); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadOkhttpRequestExecutor.java new file mode 100644 index 0000000000..27677b74b4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadOkhttpRequestExecutor.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.mp.util.requestexecuter.media; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; +import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + * + * @author ecoolper + */ +public class MediaImgUploadOkhttpRequestExecutor extends MediaImgUploadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public MediaImgUploadOkhttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaImgUploadResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException { + logger.debug("MediaImgUploadOkhttpRequestExecutor is running"); + //得到httpClient + OkHttpClient client = requestHttp.getRequestHttpClient(); + + RequestBody body = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("media", + file.getName(), + RequestBody.create(MediaType.parse("application/octet-stream"), file)) + .build(); + + Request request = new Request.Builder().url(uri).post(body).build(); + Response response = client.newCall(request).execute(); + String responseContent = response.body().string(); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return WxMediaImgUploadResult.fromJson(responseContent); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadRequestExecutor.java new file mode 100644 index 0000000000..b5f42e0f8d --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadRequestExecutor.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.mp.util.requestexecuter.media; + +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; + +/** + * @author miller + */ +public abstract class MediaImgUploadRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MediaImgUploadRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, File data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MediaImgUploadApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new MediaImgUploadHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new MediaImgUploadOkhttpRequestExecutor(requestHttp); + default: + return null; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeApacheHttpRequestExecutor.java new file mode 100644 index 0000000000..2c8e5b5721 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeApacheHttpRequestExecutor.java @@ -0,0 +1,66 @@ +package me.chanjar.weixin.mp.util.requestexecuter.qrcode; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.UUID; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class QrCodeApacheHttpRequestExecutor extends QrCodeRequestExecutor { + public QrCodeApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public File execute(String uri, WxMpQrCodeTicket ticket, WxType wxType) throws WxErrorException, IOException { + if (ticket != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") + ? "ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8") + : "&ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8"); + } + + HttpGet httpGet = new HttpGet(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet); + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0) { + // 出错 + if (ContentType.TEXT_PLAIN.getMimeType() + .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); + } + } + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } finally { + httpGet.releaseConnection(); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeJoddHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeJoddHttpRequestExecutor.java new file mode 100644 index 0000000000..99621843db --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeJoddHttpRequestExecutor.java @@ -0,0 +1,60 @@ +package me.chanjar.weixin.mp.util.requestexecuter.qrcode; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.net.MimeTypes; +import jodd.util.StringPool; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.UUID; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class QrCodeJoddHttpRequestExecutor extends QrCodeRequestExecutor { + public QrCodeJoddHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public File execute(String uri, WxMpQrCodeTicket ticket, WxType wxType) throws WxErrorException, IOException { + if (ticket != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") + ? "ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8") + : "&ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8"); + } + + HttpRequest request = HttpRequest.get(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String contentTypeHeader = response.header("Content-Type"); + if (MimeTypes.MIME_TEXT_PLAIN.equals(contentTypeHeader)) { + String responseContent = response.bodyText(); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); + } + try (InputStream inputStream = new ByteArrayInputStream(response.bodyBytes())) { + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeOkhttpRequestExecutor.java new file mode 100644 index 0000000000..e6992e1e5e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeOkhttpRequestExecutor.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.mp.util.requestexecuter.qrcode; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.UUID; + +/** + * + * @author ecoolper + * @date 2017/5/5 + */ +public class QrCodeOkhttpRequestExecutor extends QrCodeRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public QrCodeOkhttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public File execute(String uri, WxMpQrCodeTicket ticket, WxType wxType) throws WxErrorException, IOException { + logger.debug("QrCodeOkhttpRequestExecutor is running"); + + if (ticket != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") + ? "ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8") + : "&ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8"); + } + + OkHttpClient client = requestHttp.getRequestHttpClient(); + Request request = new Request.Builder().url(uri).get().build(); + Response response = client.newCall(request).execute(); + String contentTypeHeader = response.header("Content-Type"); + if ("text/plain".equals(contentTypeHeader)) { + String responseContent = response.body().string(); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); + } + + try (InputStream inputStream = response.body().byteStream()) { + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } + + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeRequestExecutor.java new file mode 100644 index 0000000000..0a65d2abc7 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeRequestExecutor.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.mp.util.requestexecuter.qrcode; + +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; + +/** + * 获得QrCode图片 请求执行器. + * + * @author chanjarster + */ +public abstract class QrCodeRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public QrCodeRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, WxMpQrCodeTicket data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) throws WxErrorException { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new QrCodeApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new QrCodeJoddHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new QrCodeOkhttpRequestExecutor(requestHttp); + default: + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("不支持的http框架").build()); + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadApacheHttpRequestExecutor.java new file mode 100644 index 0000000000..3c733a126f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadApacheHttpRequestExecutor.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.mp.util.requestexecuter.voice; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; + +/** + *

    + *  Created by BinaryWang on 2018/6/9.
    + * 
    + * + * @author Binary Wang + */ +public class VoiceUploadApacheHttpRequestExecutor extends VoiceUploadRequestExecutor { + public VoiceUploadApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public Boolean execute(String uri, File data, WxType wxType) throws WxErrorException, IOException { + if (data == null) { + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("文件对象为空").build()); + } + + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + + HttpEntity entity = MultipartEntityBuilder + .create() + .addBinaryBody("media", data) + .setMode(HttpMultipartMode.RFC6532) + .build(); + httpPost.setEntity(entity); + httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent, WxType.MP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return true; + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadRequestExecutor.java new file mode 100644 index 0000000000..fa48c953f6 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadRequestExecutor.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.mp.util.requestexecuter.voice; + +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; + +/** + *
    + *  Created by BinaryWang on 2018/6/9.
    + * 
    + * + * @author Binary Wang + */ +public abstract class VoiceUploadRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public VoiceUploadRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, File data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new VoiceUploadApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + case OK_HTTP: + default: + return null; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java index e2c0e0ea00..ace711a236 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java @@ -1,11 +1,22 @@ package me.chanjar.weixin.mp.util.xml; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.thoughtworks.xstream.XStream; import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.mp.bean.message.*; - -import java.io.InputStream; -import java.util.*; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMusicMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVideoMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVoiceMessage; public class XStreamTransformer { private static final Map, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>(); @@ -22,7 +33,7 @@ public class XStreamTransformer { } /** - * xml -> pojo + * xml -> pojo. */ @SuppressWarnings("unchecked") public static T fromXml(Class clazz, String xml) { @@ -37,31 +48,33 @@ public static T fromXml(Class clazz, InputStream is) { } /** - * pojo -> xml + * pojo -> xml. */ public static String toXml(Class clazz, T object) { return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); } /** - * 注册扩展消息的解析器 + * 注册扩展消息的解析器. * * @param clz 类型 * @param xStream xml解析器 */ - private static void register(Class clz, XStream xStream) { + public static void register(Class clz, XStream xStream) { CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); } /** - * 会自动注册该类及其子类 + * 会自动注册该类及其子类. + * * @param clz 要注册的类 */ private static void registerClass(Class clz) { XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(clz); xstream.processAnnotations(getInnerClasses(clz)); - if(clz.equals(WxMpXmlMessage.class)){ + if (clz.equals(WxMpXmlMessage.class)) { // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId xstream.aliasField("MsgID", WxMpXmlMessage.class, "msgId"); } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java deleted file mode 100644 index c8b616fa96..0000000000 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.chanjar.weixin.mp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.mp.api.test.ApiTestModule; -import org.apache.commons.lang3.StringUtils; -import org.testng.*; -import org.testng.annotations.*; - -/** - * 基础API测试 - * - * @author chanjarster - */ -@Test(groups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxMpBaseAPITest { - - @Inject - protected WxMpService wxService; - - public void testRefreshAccessToken() throws WxErrorException { - WxMpConfigStorage configStorage = this.wxService.getWxMpConfigStorage(); - String before = configStorage.getAccessToken(); - this.wxService.getAccessToken(false); - - String after = configStorage.getAccessToken(); - Assert.assertNotEquals(before, after); - Assert.assertTrue(StringUtils.isNotBlank(after)); - } - -} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java index da6460878e..1f0a01b46e 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java @@ -1,9 +1,10 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl; import org.testng.annotations.*; import java.util.concurrent.ExecutionException; @@ -12,20 +13,19 @@ import java.util.concurrent.Future; @Test +@Slf4j public class WxMpBusyRetryTest { @DataProvider(name = "getService") public Object[][] getService() { - WxMpService service = new WxMpServiceImpl() { + WxMpService service = new WxMpServiceHttpClientImpl() { @Override - protected synchronized T executeInternal( + public synchronized T executeInternal( RequestExecutor executor, String uri, E data) throws WxErrorException { - this.log.info("Executed"); - WxError error = new WxError(); - error.setErrorCode(-1); - throw new WxErrorException(error); + log.info("Executed"); + throw new WxErrorException(WxError.builder().errorCode(-1).build()); } }; @@ -36,7 +36,7 @@ protected synchronized T executeInternal( @Test(dataProvider = "getService", expectedExceptions = RuntimeException.class) public void testRetry(WxMpService service) throws WxErrorException { - service.execute(null, null, null); + service.execute(null, (String)null, null); } @Test(dataProvider = "getService") @@ -49,7 +49,7 @@ public void run() { try { System.out.println("====================="); System.out.println(Thread.currentThread().getName() + ": testRetry"); - service.execute(null, null, null); + service.execute(null, (String)null, null); } catch (WxErrorException e) { throw new RuntimeException(e); } catch (RuntimeException e) { diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java index 9fd3227893..c9df2c8153 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java @@ -1,33 +1,26 @@ package me.chanjar.weixin.mp.api; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.crypto.SHA1; import me.chanjar.weixin.mp.api.test.ApiTestModule; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; /** * 测试jsapi ticket接口 * * @author chanjarster */ -@Test(groups = "jsAPI", dependsOnGroups = "baseAPI") +@Test @Guice(modules = ApiTestModule.class) public class WxMpJsAPITest { @Inject protected WxMpService wxService; - - public void testJsapiTicket() throws WxErrorException { - String jsapiTicket = this.wxService.getJsapiTicket(false); - System.out.println(jsapiTicket); - Assert.assertNotNull(jsapiTicket); - } - public void test() { - long timestamp = 1419835025l; + long timestamp = 1419835025L; String url = "http://omstest.vmall.com:23568/thirdparty/wechat/vcode/gotoshare?quantity=1&batchName=MATE7"; String noncestr = "82693e11-b9bc-448e-892f-f5289f46cd0f"; String jsapiTicket = "bxLdikRXVbTPdHSM05e5u4RbEYQn7pNQMPrfzl8lJNb1foLDa3HIwI3BRMkQmSO_5F64VFa75uURcq6Uz7QHgA"; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java index 4360440f04..b9424eb023 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java @@ -8,6 +8,8 @@ import org.testng.*; import org.testng.annotations.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.Map; /** @@ -23,22 +25,23 @@ public void prepare(boolean async, StringBuffer sb, WxMpMessageRouter router) { router .rule() .async(async) - .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1") + .msgType(WxConsts.XmlMsgType.TEXT).event(WxConsts.EventType.CLICK).eventKey("KEY_1").content("CONTENT_1") .handler(new WxEchoMpMessageHandler(sb, "COMBINE_4")) .end() .rule() .async(async) - .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1") + .msgType(WxConsts.XmlMsgType.TEXT).event(WxConsts.EventType.CLICK).eventKey("KEY_1") .handler(new WxEchoMpMessageHandler(sb, "COMBINE_3")) .end() .rule() .async(async) - .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK) + .msgType(WxConsts.XmlMsgType.TEXT).event(WxConsts.EventType.CLICK) .handler(new WxEchoMpMessageHandler(sb, "COMBINE_2")) .end() - .rule().async(async).msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoMpMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end() - .rule().async(async).event(WxConsts.EVT_CLICK).handler(new WxEchoMpMessageHandler(sb, WxConsts.EVT_CLICK)).end() + .rule().async(async).msgType(WxConsts.XmlMsgType.TEXT).handler(new WxEchoMpMessageHandler(sb, WxConsts.XmlMsgType.TEXT)).end() + .rule().async(async).event(WxConsts.EventType.CLICK).handler(new WxEchoMpMessageHandler(sb, WxConsts.EventType.CLICK)).end() .rule().async(async).eventKey("KEY_1").handler(new WxEchoMpMessageHandler(sb, "KEY_1")).end() + .rule().async(async).eventKeyRegex("KEY_1*").handler(new WxEchoMpMessageHandler(sb, "KEY_123")).end() .rule().async(async).content("CONTENT_1").handler(new WxEchoMpMessageHandler(sb, "CONTENT_1")).end() .rule().async(async).rContent(".*bc.*").handler(new WxEchoMpMessageHandler(sb, "abcd")).end() .rule().async(async).matcher(new WxMpMessageMatcher() { @@ -65,10 +68,24 @@ public void testAsync(WxMpXmlMessage message, String expected) throws Interrupte WxMpMessageRouter router = new WxMpMessageRouter(null); prepare(true, sb, router); router.route(message); - Thread.sleep(500l); + Thread.sleep(500); + router.shutDownExecutorService(); Assert.assertEquals(sb.toString(), expected); } + @Test(dataProvider = "messages-1") + public void testExternalExcutorService(WxMpXmlMessage message, String expected) throws InterruptedException { + StringBuffer sb = new StringBuffer(); + ExecutorService executorService = Executors.newFixedThreadPool(100); + WxMpMessageRouter router = new WxMpMessageRouter(null, executorService); + prepare(true, sb, router); + router.route(message); + Thread.sleep(500); + executorService.shutdown(); + Assert.assertEquals(sb.toString(), expected); + } + + public void testConcurrency() throws InterruptedException { final WxMpMessageRouter router = new WxMpMessageRouter(null); router.rule().handler(new WxMpMessageHandler() { @@ -85,7 +102,7 @@ public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map co public void run() { router.route(m); try { - Thread.sleep(1000l); + Thread.sleep(1000); } catch (InterruptedException e) { } } @@ -94,16 +111,16 @@ public void run() { new Thread(r).start(); } - Thread.sleep(1000l * 2); + Thread.sleep(2000); } @DataProvider(name = "messages-1") public Object[][] messages2() { WxMpXmlMessage message1 = new WxMpXmlMessage(); - message1.setMsgType(WxConsts.XML_MSG_TEXT); + message1.setMsgType(WxConsts.XmlMsgType.TEXT); WxMpXmlMessage message2 = new WxMpXmlMessage(); - message2.setEvent(WxConsts.EVT_CLICK); + message2.setEvent(WxConsts.EventType.CLICK); WxMpXmlMessage message3 = new WxMpXmlMessage(); message3.setEventKey("KEY_1"); @@ -121,23 +138,23 @@ public Object[][] messages2() { message7.setFormat("strangeformat"); WxMpXmlMessage c2 = new WxMpXmlMessage(); - c2.setMsgType(WxConsts.XML_MSG_TEXT); - c2.setEvent(WxConsts.EVT_CLICK); + c2.setMsgType(WxConsts.XmlMsgType.TEXT); + c2.setEvent(WxConsts.EventType.CLICK); WxMpXmlMessage c3 = new WxMpXmlMessage(); - c3.setMsgType(WxConsts.XML_MSG_TEXT); - c3.setEvent(WxConsts.EVT_CLICK); + c3.setMsgType(WxConsts.XmlMsgType.TEXT); + c3.setEvent(WxConsts.EventType.CLICK); c3.setEventKey("KEY_1"); WxMpXmlMessage c4 = new WxMpXmlMessage(); - c4.setMsgType(WxConsts.XML_MSG_TEXT); - c4.setEvent(WxConsts.EVT_CLICK); + c4.setMsgType(WxConsts.XmlMsgType.TEXT); + c4.setEvent(WxConsts.EventType.CLICK); c4.setEventKey("KEY_1"); c4.setContent("CONTENT_1"); return new Object[][]{ - new Object[]{message1, WxConsts.XML_MSG_TEXT + ","}, - new Object[]{message2, WxConsts.EVT_CLICK + ","}, + new Object[]{message1, WxConsts.XmlMsgType.TEXT + ","}, + new Object[]{message2, WxConsts.EventType.CLICK + ","}, new Object[]{message3, "KEY_1,"}, new Object[]{message4, "CONTENT_1,"}, new Object[]{message5, "ALL,"}, @@ -179,7 +196,7 @@ public void testSessionClean1(StandardSessionManager ism) throws InterruptedExce msg.setFromUser("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -199,7 +216,7 @@ public void testSessionClean2(StandardSessionManager ism) throws InterruptedExce msg.setFromUser("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } { @@ -213,7 +230,7 @@ public void testSessionClean2(StandardSessionManager ism) throws InterruptedExce msg.setFromUser("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -233,7 +250,7 @@ public void testSessionClean3(StandardSessionManager ism) throws InterruptedExce msg.setFromUser("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -252,7 +269,7 @@ public void testSessionClean4(StandardSessionManager ism) throws InterruptedExce msg.setFromUser("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } @@ -266,7 +283,7 @@ public void testSessionClean4(StandardSessionManager ism) throws InterruptedExce msg.setFromUser("abc"); router.route(msg); - Thread.sleep(2000l); + Thread.sleep(2000); Assert.assertEquals(ism.getActiveSessions(), 0); } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMiscAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMiscAPITest.java deleted file mode 100644 index 8c61a56059..0000000000 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMiscAPITest.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.chanjar.weixin.mp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.mp.api.test.ApiTestModule; -import org.testng.*; -import org.testng.annotations.*; - -import java.util.Arrays; - -/** - * @author chanjarster - */ -@Test(groups = "miscAPI") -@Guice(modules = ApiTestModule.class) -public class WxMpMiscAPITest { - - @Inject - protected WxMpService wxService; - - @Test - public void testGetCallbackIP() throws WxErrorException { - String[] ipArray = this.wxService.getCallbackIP(); - System.out.println(Arrays.toString(ipArray)); - Assert.assertNotNull(ipArray); - Assert.assertNotEquals(ipArray.length, 0); - } - -} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpShortUrlAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpShortUrlAPITest.java deleted file mode 100644 index 3e8a45c361..0000000000 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpShortUrlAPITest.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.chanjar.weixin.mp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.mp.api.test.ApiTestModule; -import org.testng.*; -import org.testng.annotations.*; - -/** - * 测试短连接 - * - * @author chanjarster - */ -@Test(groups = "shortURLAPI") -@Guice(modules = ApiTestModule.class) -public class WxMpShortUrlAPITest { - - @Inject - protected WxMpService wxService; - - public void testShortUrl() throws WxErrorException { - String shortUrl = this.wxService.shortUrl("www.baidu.com"); - Assert.assertNotNull(shortUrl); - } - -} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java new file mode 100644 index 0000000000..b20d3fe142 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java @@ -0,0 +1,410 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.common.collect.Sets; +import com.google.inject.Inject; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.bean.WxNetCheckResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; +import org.testng.Assert; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + *
    + *  Created by BinaryWang on 2019/3/29.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class BaseWxMpServiceImplTest { + @Inject + private WxMpService wxService; + + @Test + public void testSwitchover() { + assertTrue(this.wxService.switchover("another")); + assertThat(WxMpConfigStorageHolder.get()).isEqualTo("another"); + assertFalse(this.wxService.switchover("whatever")); + assertFalse(this.wxService.switchover("default")); + } + + @Test + public void testSwitchoverTo() throws WxErrorException { + assertThat(this.wxService.switchoverTo("another").getAccessToken()).isNotEmpty(); + assertThat(WxMpConfigStorageHolder.get()).isEqualTo("another"); + } + + @Test + public void testNetCheck() throws WxErrorException { + WxNetCheckResult result = this.wxService.netCheck(WxConsts.NetCheckArgs.ACTIONALL, WxConsts.NetCheckArgs.OPERATORDEFAULT); + Assert.assertNotNull(result); + + } + + @Test + public void testGetCallbackIP() throws WxErrorException { + String[] ipArray = this.wxService.getCallbackIP(); + System.out.println(Arrays.toString(ipArray)); + Assert.assertNotNull(ipArray); + Assert.assertNotEquals(ipArray.length, 0); + } + + public void testShortUrl() throws WxErrorException { + String shortUrl = this.wxService.shortUrl("http://www.baidu.com/test?access_token=123"); + assertThat(shortUrl).isNotEmpty(); + System.out.println(shortUrl); + } + + @Test(expectedExceptions = WxErrorException.class) + public void testShortUrl_with_exceptional_url() throws WxErrorException { + this.wxService.shortUrl("http://www.baidu.com/test?redirect_count=1&access_token=123"); + } + + @Test + public void refreshAccessTokenDuplicatelyTest() throws InterruptedException { + // 测试多线程刷新accessToken时是否重复刷新 + wxService.getWxMpConfigStorage().expireAccessToken(); + final Set set = Sets.newConcurrentHashSet(); + Runnable r = new Runnable() { + @Override + public void run() { + try { + String accessToken = wxService.getAccessToken(); + set.add(accessToken); + } catch (WxErrorException e) { + e.printStackTrace(); + } + } + }; + + final int threadNumber = 10; + ExecutorService executorService = Executors.newFixedThreadPool(threadNumber); + for ( int i = 0; i < threadNumber; i++ ) { + executorService.submit(r); + } + executorService.shutdown(); + boolean isTerminated = executorService.awaitTermination(15, TimeUnit.SECONDS); + System.out.println("isTerminated: " + isTerminated); + System.out.println("times of refreshing accessToken: " + set.size()); + + assertEquals(set.size(), 1); + + } + + @Test + public void testCheckSignature() { + } + + @Test + public void testGetTicket() { + } + + @Test + public void testTestGetTicket() { + } + + @Test + public void testGetJsapiTicket() { + } + + @Test + public void testTestGetJsapiTicket() { + } + + @Test + public void testCreateJsapiSignature() throws WxErrorException { + final WxJsapiSignature jsapiSignature = this.wxService.createJsapiSignature("http://www.baidu.com"); + assertThat(jsapiSignature).isNotNull(); + assertThat(jsapiSignature.getSignature()).isNotNull(); + System.out.println(jsapiSignature); + } + + @Test + public void testGetAccessToken() { + } + + @Test + public void testSemanticQuery() { + } + + @Test + public void testOauth2buildAuthorizationUrl() { + } + + @Test + public void testBuildQrConnectUrl() { + } + + @Test + public void testOauth2getAccessToken() { + } + + @Test + public void testOauth2refreshAccessToken() { + } + + @Test + public void testOauth2getUserInfo() { + } + + @Test + public void testOauth2validateAccessToken() { + } + + @Test + public void testGetCurrentAutoReplyInfo() { + } + + @Test + public void testClearQuota() { + } + + @Test + public void testGet() { + } + + @Test + public void testTestGet() { + } + + @Test + public void testPost() { + } + + @Test + public void testTestPost() { + } + + @Test + public void testExecute() { + } + + @Test + public void testTestExecute() { + } + + @Test + public void testExecuteInternal() { + } + + @Test + public void testGetWxMpConfigStorage() { + } + + @Test + public void testSetWxMpConfigStorage() { + } + + @Test + public void testSetMultiConfigStorages() { + } + + @Test + public void testTestSetMultiConfigStorages() { + } + + @Test + public void testAddConfigStorage() { + } + + @Test + public void testRemoveConfigStorage() { + } + + @Test + public void testSetRetrySleepMillis() { + } + + @Test + public void testSetMaxRetryTimes() { + } + + @Test + public void testGetKefuService() { + } + + @Test + public void testGetMaterialService() { + } + + @Test + public void testGetMenuService() { + } + + @Test + public void testGetUserService() { + } + + @Test + public void testGetUserTagService() { + } + + @Test + public void testGetQrcodeService() { + } + + @Test + public void testGetCardService() { + } + + @Test + public void testGetDataCubeService() { + } + + @Test + public void testGetBlackListService() { + } + + @Test + public void testGetStoreService() { + } + + @Test + public void testGetTemplateMsgService() { + } + + @Test + public void testGetSubscribeMsgService() { + } + + @Test + public void testGetDeviceService() { + } + + @Test + public void testGetShakeService() { + } + + @Test + public void testGetMemberCardService() { + } + + @Test + public void testGetRequestHttp() { + } + + @Test + public void testGetMassMessageService() { + } + + @Test + public void testSetKefuService() { + } + + @Test + public void testSetMaterialService() { + } + + @Test + public void testSetMenuService() { + } + + @Test + public void testSetUserService() { + } + + @Test + public void testSetTagService() { + } + + @Test + public void testSetQrCodeService() { + } + + @Test + public void testSetCardService() { + } + + @Test + public void testSetStoreService() { + } + + @Test + public void testSetDataCubeService() { + } + + @Test + public void testSetBlackListService() { + } + + @Test + public void testSetTemplateMsgService() { + } + + @Test + public void testSetDeviceService() { + } + + @Test + public void testSetShakeService() { + } + + @Test + public void testSetMemberCardService() { + } + + @Test + public void testSetMassMessageService() { + } + + @Test + public void testGetAiOpenService() { + } + + @Test + public void testSetAiOpenService() { + } + + @Test + public void testGetWifiService() { + } + + @Test + public void testGetOcrService() { + } + + @Test + public void testGetMarketingService() { + } + + @Test + public void testSetMarketingService() { + } + + @Test + public void testSetOcrService() { + } + + @Test + public void testGetCommentService() { + } + + @Test + public void testSetCommentService() { + } + + @Test + public void testGetImgProcService() { + } + + @Test + public void testSetImgProcService() { + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java new file mode 100644 index 0000000000..9cf770ac5c --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.mp.api.impl; + +import java.io.File; + +import org.testng.annotations.*; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.enums.AiLangType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *  Created by BinaryWang on 2018/6/10.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpAiOpenServiceImplTest { + @Inject + protected WxMpService wxService; + + @Test + public void testUploadVoice() throws WxErrorException { + String voiceId = System.currentTimeMillis() + "a"; + AiLangType lang = AiLangType.zh_CN; + this.wxService.getAiOpenService().uploadVoice(voiceId, lang, new File("d:\\t.mp3")); + } + + @Test + public void testRecogniseVoice() throws WxErrorException { + String voiceId = System.currentTimeMillis() + "a"; + AiLangType lang = AiLangType.zh_CN; + final String result = this.wxService.getAiOpenService().recogniseVoice(voiceId, lang, new File("d:\\t.mp3")); + assertThat(result).isNotEmpty(); + } + + @Test + public void testTranslate() throws WxErrorException { + final String result = this.wxService.getAiOpenService().translate(AiLangType.zh_CN, AiLangType.en_US, "微信文档很坑爹"); + assertThat(result).isNotEmpty(); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java index 3036013a7e..ecacc36de5 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java @@ -1,11 +1,14 @@ package me.chanjar.weixin.mp.api.impl; +import com.google.common.collect.Lists; import com.google.inject.Inject; import me.chanjar.weixin.common.bean.WxCardApiSignature; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; -import me.chanjar.weixin.mp.bean.result.WxMpCardResult; -import org.testng.annotations.*; +import me.chanjar.weixin.mp.bean.card.*; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; import static org.testng.AssertJUnit.*; @@ -91,4 +94,147 @@ public void testGetCardDetail() throws Exception { System.out.println(result); } + @Test + public void testUnavailableCardCode() throws Exception { + String cardId = "p2iQk1luzj50RHue6yeTPQpAx_Z4"; + String code = "134905347310"; + String reason = "换成新卡了"; + String result = this.wxService.getCardService().unavailableCardCode(cardId, code, reason); + assertNotNull(result); + System.out.println(result); + } + + @Test + public void testCreateGrouponCard() throws WxErrorException { + BaseInfo base = new BaseInfo(); + base.setLogoUrl("http://mmbiz.qpic.cn/mmbiz/iaL1LJM1mF9aRKPZJkmG8xXhiaHqkKSVMMWeN3hLut7X7hicFNjakmxibMLGWpXrEXB33367o7zHN0CwngnQY7zb7g/0"); + base.setBrandName("测试优惠券"); + base.setCodeType("CODE_TYPE_QRCODE"); + base.setTitle("测试标题"); + base.setColor("Color010"); + base.setNotice("测试Notice"); + base.setServicePhone("020-88888888"); + base.setDescription("不可与其他优惠同享\\n如需团购券发票,请在消费时向商户提出\\n店内均可使用,仅限堂食"); + DateInfo info = new DateInfo(); + info.setType("DATE_TYPE_FIX_TERM"); + info.setFixedBeginTerm(0); + info.setFixedTerm(30); + base.setDateInfo(info); + Sku sku = new Sku(); + sku.setQuantity(100); + base.setSku(sku); + base.setGetLimit(1); + base.setCanShare(true); + base.setCanGiveFriend(true); + base.setUseAllLocations(true); + base.setCenterTitle("顶部居中按钮"); + base.setCenterSubTitle("按钮下方的wording"); + base.setCenterUrl("www.qq.com"); + base.setCustomUrl("http://www.qq.com"); + base.setCustomUrlName("立即使用"); + base.setCustomUrlSubTitle("副标题tip"); + base.setPromotionUrlName("更多优惠"); + base.setPromotionUrl("http://www.qq.com"); + base.setLocationIdList(Lists.newArrayList("1234")); + + //团购券 + WxMpCardCreateRequest grouponMessage = new WxMpCardCreateRequest(); + GrouponCardCreateRequest grouponCardCreateRequest = new GrouponCardCreateRequest(); + GrouponCard grouponCard = new GrouponCard(); + grouponCard.setBaseInfo(base); + grouponCard.setDealDetail("deal detail"); + + grouponCardCreateRequest.setGroupon(grouponCard); + grouponMessage.setCardCreateRequest(grouponCardCreateRequest); + + System.out.println(this.wxService.getCardService().createCard(grouponMessage)); + + //现金券 + WxMpCardCreateRequest cashMessage = new WxMpCardCreateRequest(); + CashCardCreateRequest cashCardCreateRequest = new CashCardCreateRequest(); + CashCard cashCard = new CashCard(); + cashCard.setBaseInfo(base); + cashCard.setLeastCost(1000); + cashCard.setReduceCost(100); + + cashCardCreateRequest.setCash(cashCard); + cashMessage.setCardCreateRequest(cashCardCreateRequest); + + System.out.println(this.wxService.getCardService().createCard(cashMessage)); + + //折扣券 + WxMpCardCreateRequest discountMessage = new WxMpCardCreateRequest(); + DiscountCardCreateRequest discountCardCreateRequest = new DiscountCardCreateRequest(); + DiscountCard discountCard = new DiscountCard(); + discountCard.setBaseInfo(base); + discountCard.setDiscount(30); + + discountCardCreateRequest.setDiscount(discountCard); + discountMessage.setCardCreateRequest(discountCardCreateRequest); + + System.out.println(this.wxService.getCardService().createCard(discountMessage)); + + //兑换券 + WxMpCardCreateRequest giftMessage = new WxMpCardCreateRequest(); + GiftCardCreateRequest giftCardCreateRequest = new GiftCardCreateRequest(); + GiftCard giftCard = new GiftCard(); + giftCard.setBaseInfo(base); + giftCard.setGift("星巴克雪瑞纳咖啡大杯"); + + giftCardCreateRequest.setGift(giftCard); + giftMessage.setCardCreateRequest(giftCardCreateRequest); + System.out.println(this.wxService.getCardService().createCard(giftMessage)); + + //普通兑换券 + WxMpCardCreateRequest generalMessage = new WxMpCardCreateRequest(); + GeneralCouponCreateRequest generalCouponCreateRequest = new GeneralCouponCreateRequest(); + GeneralCoupon generalCoupon = new GeneralCoupon(); + generalCoupon.setBaseInfo(base); + generalCoupon.setDefaultDetail("音乐木盒"); + + generalCouponCreateRequest.setGeneralCoupon(generalCoupon); + generalMessage.setCardCreateRequest(generalCouponCreateRequest); + System.out.println(this.wxService.getCardService().createCard(generalMessage)); + } + + @Test + public void testDeleteCard() throws Exception { + String cardId = "pwkrWjtw7W4_l50kCQcZ1in1yS6g"; + WxMpCardDeleteResult result = this.wxService.getCardService().deleteCard(cardId); + assertTrue(result.isSuccess()); + System.out.println(result); + } + + @Test + public void testAddTestWhiteList() { + } + + @Test + public void testCreateCard() { + } + + @Test + public void testCreateQrcodeCard() { + } + + @Test + public void testCreateQrcodeCard1() { + } + + @Test + public void testCreateQrcodeCard2() { + } + + @Test + public void testCreateLandingPage() { + } + + @Test + public void testGetUserCardList() throws WxErrorException { + String openId = "ou7Gr5sJZgFGgj38sRCNQg5pc3Fc"; + String cardId = "pu7Gr5secJXPkxBeuYUhmp8TYsuY"; + WxUserCardListResult result = this.wxService.getCardService().getUserCardList(openId, cardId); + assertTrue(result.isSuccess()); + System.out.println(result); + } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImplTest.java new file mode 100644 index 0000000000..8efb70f9e3 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImplTest.java @@ -0,0 +1,100 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpCommentService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.bean.comment.WxMpCommentListVo; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2019-06-16 + */ + +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpCommentServiceImplTest { + @Inject + private WxMpService wxService; + + @Test + public void testOpen() throws WxErrorException { + this.wxService.getCommentService().open("1", null); + this.wxService.getCommentService().open("1", 0); + } + + @Test + public void testClose() throws WxErrorException { + this.wxService.getCommentService().close("1000000001", null); + this.wxService.getCommentService().close("1", 0); + } + + @Test + public void testList() throws WxErrorException { + String expectedResponse = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"total\": 1,\n" + + " \"comment\": [\n" + + " {\n" + + " \"user_comment_id\": 1,\n" + + " \"openid\": \"OPENID\",\n" + + " \"create_time\": \"CREATE_TIME\",\n" + + " \"content\": \"CONTENT\",\n" + + " \"comment_type\": 1,\n" + + " \"reply\": {\n" + + " \"content\": \"CONTENT\",\n" + + " \"create_time\": \"CREATE_TIME\"\n" + + " }\n" + + " }\n" + + " ]\n" + + "}"; + + wxService = spy(wxService); + WxMpCommentService commentService = new WxMpCommentServiceImpl(wxService); + doReturn(expectedResponse).when(wxService).post(anyString(), anyString()); + + final WxMpCommentListVo commentListVo = commentService.list("1", 1, 1, 1, 1); + assertThat(commentListVo).isNotNull(); + System.out.println(commentListVo); + assertThat(commentListVo.getTotal()).isEqualTo(1); + assertThat(commentListVo.getComment()).isNotEmpty(); + + assertThat(commentListVo.getComment().get(0).getReply()).isNotNull(); + } + + @Test + public void testMarkElect() throws WxErrorException { + this.wxService.getCommentService().markElect("1000000001", null, 1L); + } + + @Test + public void testUnmarkElect() throws WxErrorException { + this.wxService.getCommentService().unmarkElect("1000000001", null, 1L); + } + + @Test + public void testDelete() throws WxErrorException { + this.wxService.getCommentService().delete("1000000001", null, 1L); + } + + @Test + public void testReplyAdd() throws WxErrorException { + this.wxService.getCommentService().replyAdd("1000000001", null, 1L, "haha"); + } + + @Test + public void testReplyADelete() throws WxErrorException { + this.wxService.getCommentService().replyDelete("1000000001", null, 1L); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImplTest.java index 0f465d4a47..e5e0e22586 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImplTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.mp.api.impl; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.bean.datacube.*; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImplTest.java index d48161e354..2039a5f32b 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImplTest.java @@ -2,7 +2,7 @@ import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.bean.device.WxDeviceQrCodeResult; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpImgProcServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpImgProcServiceImplTest.java new file mode 100644 index 0000000000..4d2c21bce9 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpImgProcServiceImplTest.java @@ -0,0 +1,213 @@ +package me.chanjar.weixin.mp.api.impl; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.api.WxImgProcService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.api.test.TestConstants; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcAiCropResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcQrCodeResult; +import me.chanjar.weixin.common.bean.imgproc.WxImgProcSuperResolutionResult; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; + +import java.io.File; +import java.io.InputStream; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpImgProcServiceImplTest { + @Inject + private WxMpService mpService; + + @Test + public void testQrCode() throws WxErrorException { + final WxImgProcQrCodeResult result = this.mpService.getImgProcService().qrCode("https://gitee.com/binary/weixin-java-tools/raw/master/images/qrcodes/mp.png"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testQrCode2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxImgProcQrCodeResult result = this.mpService.getImgProcService().qrCode(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testSuperResolution() throws WxErrorException { + final WxImgProcSuperResolutionResult result = this.mpService.getImgProcService().superResolution("https://gitee.com/binary/weixin-java-tools/raw/master/images/qrcodes/mp.png"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testSuperResolution2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxImgProcSuperResolutionResult result = this.mpService.getImgProcService().superResolution(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testAiCrop() throws WxErrorException { + final WxImgProcAiCropResult result = this.mpService.getImgProcService().aiCrop("https://gitee.com/binary/weixin-java-tools/raw/master/images/qrcodes/mp.png"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testAiCrop2() throws WxErrorException { + final WxImgProcAiCropResult result = this.mpService.getImgProcService().aiCrop("https://gitee.com/binary/weixin-java-tools/raw/master/images/qrcodes/mp.png", "1,2.35"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testAiCrop3() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxImgProcAiCropResult result = this.mpService.getImgProcService().aiCrop(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testAiCrop4() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxImgProcAiCropResult result = this.mpService.getImgProcService().aiCrop(tempFile, "1,2.35,3.5"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + public static class mockTest { + private WxMpService wxService = mock(WxMpService.class); + + @Test + public void testQrCode() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"code_results\": [\n" + + " {\n" + + " \"type_name\": \"QR_CODE\", \n" + + " \"data\": \"https://www.qq.com\", \n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 585, \n" + + " \"y\": 378\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 828, \n" + + " \"y\": 378\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 828, \n" + + " \"y\": 618\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 585, \n" + + " \"y\": 618\n" + + " }\n" + + " }\n" + + " }, \n" + + " {\n" + + " \"type_name\": \"QR_CODE\", \n" + + " \"data\": \"https://mp.weixin.qq.com\", \n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 185, \n" + + " \"y\": 142\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 396, \n" + + " \"y\": 142\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 396, \n" + + " \"y\": 353\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 185, \n" + + " \"y\": 353\n" + + " }\n" + + " }\n" + + " }, \n" + + " {\n" + + " \"type_name\": \"EAN_13\", \n" + + " \"data\": \"5906789678957\"\n" + + " }, \n" + + " {\n" + + " \"type_name\": \"CODE_128\", \n" + + " \"data\": \"50090500019191\"\n" + + " }\n" + + " ], \n" + + " \"img_size\": {\n" + + " \"w\": 1000, \n" + + " \"h\": 900\n" + + " }\n" + + "}"; + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxImgProcService wxMpImgProcService = new WxMpImgProcServiceImpl(wxService); + final WxImgProcQrCodeResult result = wxMpImgProcService.qrCode("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testSuperResolution() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"media_id\": \"6WXsIXkG7lXuDLspD9xfm5dsvHzb0EFl0li6ySxi92ap8Vl3zZoD9DpOyNudeJGB\"\n" + + "}"; + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxImgProcService wxMpImgProcService = new WxMpImgProcServiceImpl(wxService); + final WxImgProcSuperResolutionResult result = wxMpImgProcService.superResolution("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testAiCrop() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"results\": [ //智能裁剪结果\n" + + " {\n" + + " \"crop_left\": 112, \n" + + " \"crop_top\": 0, \n" + + " \"crop_right\": 839, \n" + + " \"crop_bottom\": 727\n" + + " }, \n" + + " {\n" + + " \"crop_left\": 0, \n" + + " \"crop_top\": 205, \n" + + " \"crop_right\": 965, \n" + + " \"crop_bottom\": 615\n" + + " }\n" + + " ], \n" + + " \"img_size\": { //图片大小\n" + + " \"w\": 966, \n" + + " \"h\": 728\n" + + " }\n" + + "}"; + when(wxService.get(anyString(), anyString())).thenReturn(returnJson); + final WxImgProcService wxMpImgProcService = new WxMpImgProcServiceImpl(wxService); + final WxImgProcAiCropResult result = wxMpImgProcService.aiCrop("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImplTest.java index 2bf5de1dc4..712e887423 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpKefuServiceImplTest.java @@ -1,20 +1,28 @@ package me.chanjar.weixin.mp.api.impl; +import java.io.File; +import java.util.Date; + +import org.joda.time.DateTime; +import org.testng.annotations.*; + import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConfigStorage; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; -import me.chanjar.weixin.mp.bean.kefu.result.*; -import org.joda.time.DateTime; -import org.testng.*; -import org.testng.annotations.*; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfInfo; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; +import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; -import java.io.File; -import java.util.Date; +import static org.assertj.core.api.Assertions.assertThat; /** * 测试客服相关接口 @@ -29,53 +37,50 @@ public class WxMpKefuServiceImplTest { protected WxMpService wxService; public void testSendKefuMpNewsMessage() throws WxErrorException { - TestConfigStorage configStorage = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); + TestConfigStorage configStorage = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); WxMpKefuMessage message = new WxMpKefuMessage(); - message.setMsgType(WxConsts.CUSTOM_MSG_MPNEWS); + message.setMsgType(WxConsts.KefuMsgType.MPNEWS); message.setToUser(configStorage.getOpenid()); message.setMpNewsMediaId("52R6dL2FxDpM9N1rCY3sYBqHwq-L7K_lz1sPI71idMg"); - this.wxService.getKefuService().sendKefuMessage(message); + boolean result = this.wxService.getKefuService().sendKefuMessage(message); + assertThat(result).isTrue(); } public void testSendKefuMessage() throws WxErrorException { - TestConfigStorage configStorage = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); + TestConfigStorage configStorage = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); WxMpKefuMessage message = new WxMpKefuMessage(); - message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + message.setMsgType(WxConsts.KefuMsgType.TEXT); message.setToUser(configStorage.getOpenid()); - message.setContent( - "欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); - this.wxService.getKefuService().sendKefuMessage(message); + boolean result = this.wxService.getKefuService().sendKefuMessage(message); + assertThat(result).isTrue(); } public void testSendKefuMessageWithKfAccount() throws WxErrorException { - TestConfigStorage configStorage = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); + TestConfigStorage configStorage = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); WxMpKefuMessage message = new WxMpKefuMessage(); - message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + message.setMsgType(WxConsts.KefuMsgType.TEXT); message.setToUser(configStorage.getOpenid()); message.setKfAccount(configStorage.getKfAccount()); - message.setContent( - "欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); - this.wxService.getKefuService().sendKefuMessage(message); + boolean result = this.wxService.getKefuService().sendKefuMessage(message); + assertThat(result).isTrue(); } public void testKfList() throws WxErrorException { WxMpKfList kfList = this.wxService.getKefuService().kfList(); - Assert.assertNotNull(kfList); + assertThat(kfList).isNotNull(); for (WxMpKfInfo k : kfList.getKfList()) { System.err.println(k); } } public void testKfOnlineList() throws WxErrorException { - WxMpKfOnlineList kfOnlineList = this.wxService.getKefuService() - .kfOnlineList(); - Assert.assertNotNull(kfOnlineList); + WxMpKfOnlineList kfOnlineList = this.wxService.getKefuService().kfOnlineList(); + assertThat(kfOnlineList).isNotNull(); for (WxMpKfInfo k : kfOnlineList.getKfOnlineList()) { System.err.println(k); } @@ -83,8 +88,7 @@ public void testKfOnlineList() throws WxErrorException { @DataProvider public Object[][] getKfAccount() { - TestConfigStorage configStorage = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); + TestConfigStorage configStorage = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); return new Object[][]{{configStorage.getKfAccount()}}; } @@ -92,7 +96,7 @@ public Object[][] getKfAccount() { public void testKfAccountAdd(String kfAccount) throws WxErrorException { WxMpKfAccountRequest request = WxMpKfAccountRequest.builder() .kfAccount(kfAccount).nickName("我晕").build(); - Assert.assertTrue(this.wxService.getKefuService().kfAccountAdd(request)); + assertThat(this.wxService.getKefuService().kfAccountAdd(request)).isTrue(); } @Test(dependsOnMethods = { @@ -100,7 +104,7 @@ public void testKfAccountAdd(String kfAccount) throws WxErrorException { public void testKfAccountUpdate(String kfAccount) throws WxErrorException { WxMpKfAccountRequest request = WxMpKfAccountRequest.builder() .kfAccount(kfAccount).nickName("我晕").build(); - Assert.assertTrue(this.wxService.getKefuService().kfAccountUpdate(request)); + assertThat(this.wxService.getKefuService().kfAccountUpdate(request)).isTrue(); } @Test(dependsOnMethods = { @@ -108,71 +112,58 @@ public void testKfAccountUpdate(String kfAccount) throws WxErrorException { public void testKfAccountInviteWorker(String kfAccount) throws WxErrorException { WxMpKfAccountRequest request = WxMpKfAccountRequest.builder() .kfAccount(kfAccount).inviteWx(" ").build(); - Assert.assertTrue(this.wxService.getKefuService().kfAccountInviteWorker(request)); + assertThat(this.wxService.getKefuService().kfAccountInviteWorker(request)).isTrue(); } - @Test(dependsOnMethods = { - "testKfAccountUpdate"}, dataProvider = "getKfAccount") - public void testKfAccountUploadHeadImg(String kfAccount) - throws WxErrorException { + @Test(dependsOnMethods = {"testKfAccountUpdate", "testKfAccountAdd"}, dataProvider = "getKfAccount") + public void testKfAccountUploadHeadImg(String kfAccount) throws WxErrorException { File imgFile = new File("src\\test\\resources\\mm.jpeg"); - boolean result = this.wxService.getKefuService() - .kfAccountUploadHeadImg(kfAccount, imgFile); - Assert.assertTrue(result); + boolean result = this.wxService.getKefuService().kfAccountUploadHeadImg(kfAccount, imgFile); + assertThat(result).isTrue(); } @Test(dataProvider = "getKfAccount") public void testKfAccountDel(String kfAccount) throws WxErrorException { boolean result = this.wxService.getKefuService().kfAccountDel(kfAccount); - Assert.assertTrue(result); + assertThat(result).isTrue(); } @DataProvider public Object[][] getKfAccountAndOpenid() { - TestConfigStorage configStorage = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); - return new Object[][]{ - {configStorage.getKfAccount(), configStorage.getOpenid()}}; + TestConfigStorage configStorage = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); + return new Object[][]{{configStorage.getKfAccount(), configStorage.getOpenid()}}; } @Test(dataProvider = "getKfAccountAndOpenid") - public void testKfSessionCreate(String kfAccount, String openid) - throws WxErrorException { - boolean result = this.wxService.getKefuService().kfSessionCreate(openid, - kfAccount); - Assert.assertTrue(result); + public void testKfSessionCreate(String kfAccount, String openid) throws WxErrorException { + boolean result = this.wxService.getKefuService().kfSessionCreate(openid, kfAccount); + assertThat(result).isTrue(); } @Test(dataProvider = "getKfAccountAndOpenid") - public void testKfSessionClose(String kfAccount, String openid) - throws WxErrorException { - boolean result = this.wxService.getKefuService().kfSessionClose(openid, - kfAccount); - Assert.assertTrue(result); + public void testKfSessionClose(String kfAccount, String openid) throws WxErrorException { + boolean result = this.wxService.getKefuService().kfSessionClose(openid, kfAccount); + assertThat(result).isTrue(); } @Test(dataProvider = "getKfAccountAndOpenid") - public void testKfSessionGet(@SuppressWarnings("unused") String kfAccount, - String openid) throws WxErrorException { - WxMpKfSessionGetResult result = this.wxService.getKefuService() - .kfSessionGet(openid); - Assert.assertNotNull(result); + public void testKfSessionGet(@SuppressWarnings("unused") String kfAccount, String openid) throws WxErrorException { + WxMpKfSessionGetResult result = this.wxService.getKefuService().kfSessionGet(openid); + assertThat(result).isNotNull(); System.err.println(result); } @Test(dataProvider = "getKfAccount") public void testKfSessionList(String kfAccount) throws WxErrorException { - WxMpKfSessionList result = this.wxService.getKefuService() - .kfSessionList(kfAccount); - Assert.assertNotNull(result); + WxMpKfSessionList result = this.wxService.getKefuService().kfSessionList(kfAccount); + assertThat(result).isNotNull(); System.err.println(result); } @Test public void testKfSessionGetWaitCase() throws WxErrorException { - WxMpKfSessionWaitCaseList result = this.wxService.getKefuService() - .kfSessionGetWaitCase(); - Assert.assertNotNull(result); + WxMpKfSessionWaitCaseList result = this.wxService.getKefuService().kfSessionGetWaitCase(); + assertThat(result).isNotNull(); System.err.println(result); } @@ -181,7 +172,7 @@ public void testKfMsgList() throws WxErrorException { Date startTime = DateTime.now().minusDays(1).toDate(); Date endTime = DateTime.now().minusDays(0).toDate(); WxMpKfMsgList result = this.wxService.getKefuService().kfMsgList(startTime, endTime, 1L, 50); - Assert.assertNotNull(result); + assertThat(result).isNotNull(); System.err.println(result); } @@ -190,7 +181,14 @@ public void testKfMsgListAll() throws WxErrorException { Date startTime = DateTime.now().minusDays(1).toDate(); Date endTime = DateTime.now().minusDays(0).toDate(); WxMpKfMsgList result = this.wxService.getKefuService().kfMsgList(startTime, endTime); - Assert.assertNotNull(result); + assertThat(result).isNotNull(); System.err.println(result); } + + @Test + public void testSendKfTypingState() throws WxErrorException { + TestConfigStorage configStorage = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); + boolean result = this.wxService.getKefuService().sendKfTypingState(configStorage.getOpenid(), "Typing"); + assertThat(result).isTrue(); + } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImplTest.java new file mode 100644 index 0000000000..dd15496e16 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImplTest.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.bean.marketing.WxMpUserAction; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2019-07-14 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpMarketingServiceImplTest { + @Inject + protected WxMpService wxService; + + @Test + public void testAddUserActionSets() { + } + + @Test + public void testGetUserActionSets() { + } + + @Test + public void testAddUserAction() throws WxErrorException { + this.wxService.getMarketingService().addUserAction(Lists.newArrayList(new WxMpUserAction())); + } + + @Test + public void testGetAdLeads() { + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImplTest.java similarity index 55% rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImplTest.java index dd7541ff8c..b31cc748ea 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImplTest.java @@ -1,9 +1,10 @@ -package me.chanjar.weixin.mp.api; +package me.chanjar.weixin.mp.api.impl; import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConfigStorage; import me.chanjar.weixin.mp.api.test.TestConstants; @@ -11,84 +12,80 @@ import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage; import me.chanjar.weixin.mp.bean.WxMpMassTagMessage; import me.chanjar.weixin.mp.bean.WxMpMassVideo; +import me.chanjar.weixin.mp.bean.material.WxMpNewsArticle; import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; +import static org.testng.Assert.assertNotNull; + /** * 测试群发消息 * * @author chanjarster */ -@Test(groups = "massAPI", dependsOnGroups = {"baseAPI", "mediaAPI", "groupAPI"}) +@Test @Guice(modules = ApiTestModule.class) -public class WxMpMassMessageAPITest { - +public class WxMpMassMessageServiceImplTest { @Inject protected WxMpService wxService; @Test public void testTextMassOpenIdsMessageSend() throws WxErrorException { // 发送群发消息 - TestConfigStorage configProvider = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); + TestConfigStorage configProvider = (TestConfigStorage) this.wxService .getWxMpConfigStorage(); WxMpMassOpenIdsMessage massMessage = new WxMpMassOpenIdsMessage(); - massMessage.setMsgType(WxConsts.MASS_MSG_TEXT); - massMessage.setContent("测试群发消息\n欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + massMessage.setMsgType(WxConsts.MassMsgType.TEXT); + massMessage.setContent("测试群发消息\n欢迎欢迎\n换行测试\n超链接:Hello World"); massMessage.getToUsers().add(configProvider.getOpenid()); - WxMpMassSendResult massResult = this.wxService - .massOpenIdsMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + WxMpMassSendResult massResult = this.wxService.getMassMessageService().massOpenIdsMessageSend(massMessage); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @Test(dataProvider = "massMessages") public void testMediaMassOpenIdsMessageSend(String massMsgType, String mediaId) throws WxErrorException { // 发送群发消息 - TestConfigStorage configProvider = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); + TestConfigStorage configProvider = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); WxMpMassOpenIdsMessage massMessage = new WxMpMassOpenIdsMessage(); massMessage.setMsgType(massMsgType); massMessage.setMediaId(mediaId); massMessage.getToUsers().add(configProvider.getOpenid()); - WxMpMassSendResult massResult = this.wxService - .massOpenIdsMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + WxMpMassSendResult massResult = this.wxService.getMassMessageService().massOpenIdsMessageSend(massMessage); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @Test public void testTextMassGroupMessageSend() throws WxErrorException { WxMpMassTagMessage massMessage = new WxMpMassTagMessage(); - massMessage.setMsgType(WxConsts.MASS_MSG_TEXT); - massMessage.setContent("测试群发消息\n欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + massMessage.setMsgType(WxConsts.MassMsgType.TEXT); + massMessage.setContent("测试群发消息\n欢迎欢迎\n换行测试\n超链接:Hello World"); massMessage .setTagId(this.wxService.getUserTagService().tagGet().get(0).getId()); - WxMpMassSendResult massResult = this.wxService - .massGroupMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + WxMpMassSendResult massResult = this.wxService.getMassMessageService().massGroupMessageSend(massMessage); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @Test(dataProvider = "massMessages") - public void testMediaMassGroupMessageSend(String massMsgType, String mediaId) - throws WxErrorException { + public void testMediaMassGroupMessageSend(String massMsgType, String mediaId) throws WxErrorException { WxMpMassTagMessage massMessage = new WxMpMassTagMessage(); massMessage.setMsgType(massMsgType); massMessage.setMediaId(mediaId); massMessage.setTagId(this.wxService.getUserTagService().tagGet().get(0).getId()); - WxMpMassSendResult massResult = this.wxService - .massGroupMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + WxMpMassSendResult massResult = this.wxService.getMassMessageService().massGroupMessageSend(massMessage); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @DataProvider @@ -102,19 +99,19 @@ public Object[][] massMessages() throws WxErrorException, IOException { .getSystemResourceAsStream("mm.mp4")) { // 上传视频到媒体库 WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() - .mediaUpload(WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); + .mediaUpload(WxConsts.MediaFileType.VIDEO, TestConstants.FILE_MP4, inputStream); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); // 把视频变成可被群发的媒体 WxMpMassVideo video = new WxMpMassVideo(); video.setTitle("测试标题"); video.setDescription("测试描述"); video.setMediaId(uploadMediaRes.getMediaId()); - WxMpMassUploadResult uploadResult = this.wxService.massVideoUpload(video); - Assert.assertNotNull(uploadResult); - Assert.assertNotNull(uploadResult.getMediaId()); - messages[0] = new Object[]{WxConsts.MASS_MSG_VIDEO, uploadResult.getMediaId()}; + WxMpMassUploadResult uploadResult = this.wxService.getMassMessageService().massVideoUpload(video); + assertNotNull(uploadResult); + assertNotNull(uploadResult.getMediaId()); + messages[0] = new Object[]{WxConsts.MassMsgType.MPVIDEO, uploadResult.getMediaId()}; } /* @@ -123,10 +120,10 @@ public Object[][] massMessages() throws WxErrorException, IOException { try (InputStream inputStream = ClassLoader .getSystemResourceAsStream("mm.jpeg")) { WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() - .mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); - messages[1] = new Object[]{WxConsts.MASS_MSG_IMAGE, uploadMediaRes.getMediaId()}; + .mediaUpload(WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, inputStream); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); + messages[1] = new Object[]{WxConsts.MassMsgType.IMAGE, uploadMediaRes.getMediaId()}; } /* @@ -135,10 +132,10 @@ public Object[][] massMessages() throws WxErrorException, IOException { try (InputStream inputStream = ClassLoader .getSystemResourceAsStream("mm.mp3")) { WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() - .mediaUpload(WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); - messages[2] = new Object[]{WxConsts.MASS_MSG_VOICE, uploadMediaRes.getMediaId()}; + .mediaUpload(WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, inputStream); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); + messages[2] = new Object[]{WxConsts.MassMsgType.VOICE, uploadMediaRes.getMediaId()}; } /* @@ -148,21 +145,21 @@ public Object[][] massMessages() throws WxErrorException, IOException { .getSystemResourceAsStream("mm.jpeg")) { // 上传照片到媒体库 WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() - .mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); + .mediaUpload(WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, inputStream); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); // 上传图文消息 WxMpMassNews news = new WxMpMassNews(); - WxMpMassNews.WxMpMassNewsArticle article1 = new WxMpMassNews.WxMpMassNewsArticle(); + WxMpNewsArticle article1 = new WxMpNewsArticle(); article1.setTitle("标题1"); - article1.setContent("内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1"); + article1.setContent("内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1"); article1.setThumbMediaId(uploadMediaRes.getMediaId()); news.addArticle(article1); - WxMpMassNews.WxMpMassNewsArticle article2 = new WxMpMassNews.WxMpMassNewsArticle(); + WxMpNewsArticle article2 = new WxMpNewsArticle(); article2.setTitle("标题2"); - article2.setContent("内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2"); + article2.setContent("内容2内容2内容2内容2内容2内容2内容2内容2内2内容2内容2内容2内容2内容2内容2内容2内容2内容2"); article2.setThumbMediaId(uploadMediaRes.getMediaId()); article2.setShowCoverPic(true); article2.setAuthor("作者2"); @@ -170,14 +167,19 @@ public Object[][] massMessages() throws WxErrorException, IOException { article2.setDigest("摘要2"); news.addArticle(article2); - WxMpMassUploadResult massUploadResult = this.wxService + WxMpMassUploadResult massUploadResult = this.wxService.getMassMessageService() .massNewsUpload(news); - Assert.assertNotNull(massUploadResult); - Assert.assertNotNull(uploadMediaRes.getMediaId()); - messages[3] = new Object[]{WxConsts.MASS_MSG_NEWS, massUploadResult.getMediaId()}; + assertNotNull(massUploadResult); + assertNotNull(uploadMediaRes.getMediaId()); + messages[3] = new Object[]{WxConsts.MassMsgType.MPNEWS, massUploadResult.getMediaId()}; } return messages; } + @Test + public void testMassDelete() throws Exception { + this.wxService.getMassMessageService().delete(1L, 2); + } + } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java index 6bb1e58bff..707f1df311 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java @@ -3,20 +3,22 @@ import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConstants; import me.chanjar.weixin.mp.bean.material.*; -import org.testng.annotations.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.*; -import static org.junit.Assert.*; +import static org.testng.Assert.*; /** * 素材管理相关接口的测试 @@ -25,7 +27,7 @@ * @author codepiano * @author Binary Wang */ -@Test(groups = "materialAPI") +@Test @Guice(modules = ApiTestModule.class) public class WxMpMaterialServiceImplTest { @Inject @@ -42,14 +44,16 @@ public class WxMpMaterialServiceImplTest { private WxMpMaterialCountResult wxMaterialCountResultBeforeTest; // 以下为media接口的测试 private List mediaIdsToDownload = new ArrayList<>(); + // 以下为高清语音接口的测试 + private List voiceMediaIdsToDownload = new ArrayList<>(); @DataProvider public Object[][] mediaFiles() { return new Object[][]{ - new Object[]{WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, - new Object[]{WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, "mm.mp3"}, - new Object[]{WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, - new Object[]{WxConsts.MEDIA_THUMB, TestConstants.FILE_JPG, "mm.jpeg"} + new Object[]{WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, + new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"}, + new Object[]{WxConsts.MediaFileType.VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, + new Object[]{WxConsts.MediaFileType.THUMB, TestConstants.FILE_JPG, "mm.jpeg"} }; } @@ -67,7 +71,7 @@ public void testUploadMaterial(String mediaType, String fileType, String fileNam WxMpMaterial wxMaterial = new WxMpMaterial(); wxMaterial.setFile(tempFile); wxMaterial.setName(fileName); - if (WxConsts.MEDIA_VIDEO.equals(mediaType)) { + if (WxConsts.MediaFileType.VIDEO.equals(mediaType)) { wxMaterial.setVideoTitle("title"); wxMaterial.setVideoIntroduction("test video description"); } @@ -76,12 +80,12 @@ public void testUploadMaterial(String mediaType, String fileType, String fileNam .materialFileUpload(mediaType, wxMaterial); assertNotNull(res.getMediaId()); - if (WxConsts.MEDIA_IMAGE.equals(mediaType) - || WxConsts.MEDIA_THUMB.equals(mediaType)) { + if (WxConsts.MediaFileType.IMAGE.equals(mediaType) + || WxConsts.MediaFileType.THUMB.equals(mediaType)) { assertNotNull(res.getUrl()); } - if (WxConsts.MEDIA_THUMB.equals(mediaType)) { + if (WxConsts.MediaFileType.THUMB.equals(mediaType)) { this.thumbMediaId = res.getMediaId(); } @@ -99,7 +103,7 @@ public void testUploadMaterial(String mediaType, String fileType, String fileNam public void testAddNews() throws WxErrorException { // 单图文消息 WxMpMaterialNews wxMpMaterialNewsSingle = new WxMpMaterialNews(); - WxMpMaterialNews.WxMpMaterialNewsArticle article = new WxMpMaterialNews.WxMpMaterialNewsArticle(); + WxMpNewsArticle article = new WxMpNewsArticle(); article.setAuthor("author"); article.setThumbMediaId(this.thumbMediaId); article.setTitle("single title"); @@ -111,7 +115,7 @@ public void testAddNews() throws WxErrorException { // 多图文消息 WxMpMaterialNews wxMpMaterialNewsMultiple = new WxMpMaterialNews(); - WxMpMaterialNews.WxMpMaterialNewsArticle article1 = new WxMpMaterialNews.WxMpMaterialNewsArticle(); + WxMpNewsArticle article1 = new WxMpNewsArticle(); article1.setAuthor("author1"); article1.setThumbMediaId(this.thumbMediaId); article1.setTitle("multi title1"); @@ -120,7 +124,7 @@ public void testAddNews() throws WxErrorException { article1.setShowCoverPic(true); article1.setDigest(""); - WxMpMaterialNews.WxMpMaterialNewsArticle article2 = new WxMpMaterialNews.WxMpMaterialNewsArticle(); + WxMpNewsArticle article2 = new WxMpNewsArticle(); article2.setAuthor("author2"); article2.setThumbMediaId(this.thumbMediaId); article2.setTitle("multi title2"); @@ -173,7 +177,7 @@ public void testDownloadMaterial(String mediaId) throws WxErrorException, IOExce } } - @Test(dependsOnMethods = {"testAddNews"}) + @Test(dependsOnMethods = {"testAddNews", "testUploadMaterial"}) public void testGetNewsInfo() throws WxErrorException { WxMpMaterialNews wxMpMaterialNewsSingle = this.wxService .getMaterialService().materialNewsInfo(this.singleNewsMediaId); @@ -181,6 +185,9 @@ public void testGetNewsInfo() throws WxErrorException { .getMaterialService().materialNewsInfo(this.multiNewsMediaId); assertNotNull(wxMpMaterialNewsSingle); assertNotNull(wxMpMaterialNewsMultiple); + + System.out.println(wxMpMaterialNewsSingle); + System.out.println(wxMpMaterialNewsMultiple); } @Test(dependsOnMethods = {"testGetNewsInfo"}) @@ -189,7 +196,7 @@ public void testUpdateNewsInfo() throws WxErrorException { .getMaterialService().materialNewsInfo(this.singleNewsMediaId); assertNotNull(wxMpMaterialNewsSingle); WxMpMaterialArticleUpdate wxMpMaterialArticleUpdateSingle = new WxMpMaterialArticleUpdate(); - WxMpMaterialNews.WxMpMaterialNewsArticle articleSingle = wxMpMaterialNewsSingle.getArticles().get(0); + WxMpNewsArticle articleSingle = wxMpMaterialNewsSingle.getArticles().get(0); articleSingle.setContent("content single update"); wxMpMaterialArticleUpdateSingle.setMediaId(this.singleNewsMediaId); wxMpMaterialArticleUpdateSingle.setArticles(articleSingle); @@ -206,7 +213,7 @@ public void testUpdateNewsInfo() throws WxErrorException { .getMaterialService().materialNewsInfo(this.multiNewsMediaId); assertNotNull(wxMpMaterialNewsMultiple); WxMpMaterialArticleUpdate wxMpMaterialArticleUpdateMulti = new WxMpMaterialArticleUpdate(); - WxMpMaterialNews.WxMpMaterialNewsArticle articleMulti = wxMpMaterialNewsMultiple.getArticles().get(1); + WxMpNewsArticle articleMulti = wxMpMaterialNewsMultiple.getArticles().get(1); articleMulti.setContent("content 2 update"); wxMpMaterialArticleUpdateMulti.setMediaId(this.multiNewsMediaId); wxMpMaterialArticleUpdateMulti.setArticles(articleMulti); @@ -226,11 +233,11 @@ public void testMaterialNewsList() throws WxErrorException { assertNotNull(wxMpMaterialNewsBatchGetResult); } - @Test(dependsOnMethods = {"testMaterialNewsList"}) + @Test//(dependsOnMethods = {"testMaterialNewsList"}) public void testMaterialFileList() throws WxErrorException { - WxMpMaterialFileBatchGetResult wxMpMaterialVoiceBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MATERIAL_VOICE, 0, 20); - WxMpMaterialFileBatchGetResult wxMpMaterialVideoBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MATERIAL_VIDEO, 0, 20); - WxMpMaterialFileBatchGetResult wxMpMaterialImageBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MATERIAL_IMAGE, 0, 20); + WxMpMaterialFileBatchGetResult wxMpMaterialVoiceBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MaterialType.VOICE, 0, 20); + WxMpMaterialFileBatchGetResult wxMpMaterialVideoBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MaterialType.VIDEO, 0, 20); + WxMpMaterialFileBatchGetResult wxMpMaterialImageBatchGetResult = this.wxService.getMaterialService().materialFileBatchGet(WxConsts.MaterialType.IMAGE, 0, 20); assertNotNull(wxMpMaterialVoiceBatchGetResult); assertNotNull(wxMpMaterialVideoBatchGetResult); assertNotNull(wxMpMaterialImageBatchGetResult); @@ -238,6 +245,15 @@ public void testMaterialFileList() throws WxErrorException { @Test(dependsOnMethods = {"testMaterialFileList"}, dataProvider = "allTestMaterial") public void testDeleteMaterial(String mediaId) throws WxErrorException { + this.delete(mediaId); + } + + @Test + public void testDeleteMaterialDirectly() throws WxErrorException { + this.delete("abc"); + } + + public void delete(String mediaId) throws WxErrorException { boolean result = this.wxService.getMaterialService().materialDelete(mediaId); assertTrue(result); } @@ -272,9 +288,14 @@ public void testUploadMedia(String mediaType, String fileType, String fileName) assertNotNull(res.getCreatedAt()); assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null); - if (res.getMediaId() != null && !mediaType.equals(WxConsts.MEDIA_VIDEO)) { + if (res.getMediaId() != null && !mediaType.equals(WxConsts.MediaFileType.VIDEO)) { //video 不支持下载,所以不加入 this.mediaIdsToDownload.add(res.getMediaId()); + + // 音频media, 用于测试下载高清语音接口 + if (mediaType.equals(WxConsts.MediaFileType.VOICE)) { + this.voiceMediaIdsToDownload.add(res.getMediaId()); + } } if (res.getThumbMediaId() != null) { @@ -294,10 +315,26 @@ public Object[][] downloadMedia() { return params; } + @DataProvider + public Object[][] downloadJssdkMedia() { + Object[][] params = new Object[this.voiceMediaIdsToDownload.size()][]; + for (int i = 0; i < this.voiceMediaIdsToDownload.size(); i++) { + params[i] = new Object[]{this.voiceMediaIdsToDownload.get(i)}; + } + return params; + } + @Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadMedia") public void testDownloadMedia(String mediaId) throws WxErrorException { File file = this.wxService.getMaterialService().mediaDownload(mediaId); assertNotNull(file); System.out.println(file.getAbsolutePath()); } + + @Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadJssdkMedia") + public void testDownloadJssdkMedia(String mediaId) throws WxErrorException { + File file = this.wxService.getMaterialService().jssdkMediaDownload(mediaId); + assertNotNull(file); + System.out.println(file.getAbsolutePath()); + } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java new file mode 100644 index 0000000000..73dcb22a2a --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java @@ -0,0 +1,163 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.mp.api.WxMpCardService; +import me.chanjar.weixin.mp.api.WxMpMemberCardService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.bean.card.*; +import me.chanjar.weixin.mp.bean.card.enums.CardSceneType; +import me.chanjar.weixin.mp.bean.card.membercard.*; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.AssertJUnit.assertNotNull; + +/** + * 会员卡相关接口的测试类。 + * 数据均为测试数据,由于直接与调用微信的接口,需要填写真实数据进行测试才能通过。 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpMemberCardServiceImplTest { + + @Inject + protected WxMpService wxService; + private String cardId = "p4p-v1bKn9tiQHxyO79aKmuTIZlQ"; + private String code = "224765120681"; + private String openId = "o4p-v1TIemEIpBSrSrTprkCaG6Xc"; + + @Test + public void createMemberCard() throws Exception { +// String json = "{\"card\":{\"card_type\":\"MEMBER_CARD\",\"member_card\":{\"advanced_info\":{\"business_service\":\"BIZ_SERVICE_FREE_PARK,BIZ_SERVICE_WITH_PET,BIZ_SERVICE_FREE_WIFI\",\"text_image_list\":[{\"image_url\":\"http://mmbiz.qpic.cn/mmbiz_jpg/upuF1LhUF8LjCLCFcQicgEiazFeonwDllGkENppDhyqhR8bz5BiaJkPT7e6bPVcfBx5cAOLro2N3U989n8WJltkjQ/0\",\"text\":\"8月8日随机免单\"}]},\"auto_activate\":false,\"background_pic_url\":\"http://mmbiz.qpic.cn/mmbiz_jpg/upuF1LhUF8LjCLCFcQicgEiazFeonwDllGl6ibk4v5iaJDAbs7dGJU7iclOJ6nh7Hnz6ZsfDL8tGEeQVJyuhKsMFxUQ/0\",\"base_info\":{\"bind_openid\":false,\"brand_name\":\"商户名称\",\"can_give_friend\":false,\"can_share\":false,\"center_sub_title\":\"点击进入\",\"center_title\":\"商城首页\",\"center_url\":\"http://www.baidu.com\",\"code_type\":\"CODE_TYPE_QRCODE\",\"color\":\"Color090\",\"date_info\":{\"type\":\"DATE_TYPE_PERMANENT\"},\"description\":\"使用须知\",\"need_push_on_view\":false,\"notice\":\"测试会员卡\",\"service_phone\":\"4008803016\",\"title\":\"终生铂金卡\",\"use_all_locations\":true,\"use_custom_code\":false},\"prerogative\":\"享有特权说明\",\"supply_balance\":true,\"supply_bonus\":true,\"wx_activate\":false}}}"; +// WxMpMemberCardCreateMessage createMessage = WxMpMemberCardCreateMessage.fromJson(json); + + //基本卡券创建 + WxMpMemberCardCreateMessage createMessage = new WxMpMemberCardCreateMessage(); + MemberCardCreateRequest cardCreateRequest = new MemberCardCreateRequest(); + MemberCard memberCard = new MemberCard(); + memberCard.setPrerogative("特权说明"); + //激活方式 + memberCard.setAutoActivate(true);//自动激活 +// memberCard.setActivateUrl("http://www.qq.com"); +// memberCard.setWxActivate(false);//微信激活 + memberCard.setSupplyBonus(true); + memberCard.setSupplyBalance(false); + memberCard.setBackgroundPicUrl("http://mmbiz.qpic.cn/mmbiz_jpg/upuF1LhUF8LjCLCFcQicgEiazFeonwDllGl6ibk4v5iaJDAbs7dGJU7iclOJ6nh7Hnz6ZsfDL8tGEeQVJyuhKsMFxUQ/0"); + memberCard.setDiscount(0); + + BaseInfo baseInfo = new BaseInfo(); + baseInfo.setLogoUrl("http://wx.qlogo.cn/mmopen/A6hCic476picOEWOJ7NsL7uWhRuh1LibrMC6byhCO6TV1lelyK9iaXbn8nAgFREibPJQTWDeKpicePt88ZHRc8wuicEM0qOllsMXic6O/0"); + baseInfo.setCodeType("CODE_TYPE_QRCODE"); + baseInfo.setBrandName("信舟科技"); + baseInfo.setTitle("铂金用户贵宾卡"); + baseInfo.setColor("Color010"); + baseInfo.setNotice("卡券使用提醒"); + baseInfo.setDescription("卡券使用说明"); + baseInfo.setServicePhone("4008803016"); + //商品信息 + Sku sku = new Sku(); + baseInfo.setSku(sku); + //使用日期 + DateInfo dateInfo = new DateInfo(); + baseInfo.setDateInfo(dateInfo); + + memberCard.setBaseInfo(baseInfo); + + cardCreateRequest.setMemberCard(memberCard); + createMessage.setCardCreateRequest(cardCreateRequest); + + WxMpCardCreateResult response = this.wxService.getMemberCardService().createMemberCard(createMessage); + assertNotNull(response); + System.out.println(response); + } + + @Test + public void testActivateMemberCard() throws Exception { + WxMpMemberCardActivatedMessage activatedMessage = new WxMpMemberCardActivatedMessage(); + activatedMessage.setMembershipNumber(openId); +// activatedMessage.setCode(code); + activatedMessage.setCardId(cardId); + activatedMessage.setInitBonus(2000); + activatedMessage.setInitBonusRecord("测试激活送积分"); + String response = this.wxService.getMemberCardService().activateMemberCard(activatedMessage); + assertNotNull(response); + System.out.println(response); + } + + @Test + public void testGetUserInfo() throws Exception { + WxMpMemberCardUserInfoResult result = this.wxService.getMemberCardService().getUserInfo(cardId, code); + assertNotNull(result); + System.out.println(result); + } + + @Test + public void testUpdateUserMemberCard() throws Exception { + WxMpMemberCardUpdateMessage updateMessage = new WxMpMemberCardUpdateMessage(); + updateMessage.setBonus(1000); + updateMessage.setCardId(cardId); + updateMessage.setCode(code); + WxMpMemberCardUpdateResult result = this.wxService.getMemberCardService().updateUserMemberCard(updateMessage); + assertNotNull(result); + System.out.println(result); + } + + /** + * 测试添加测试openid白名单 + * + * @throws Exception + */ + @Test + public void testAddTestWhiteList() throws Exception { + WxMpCardService cardService = this.wxService.getCardService(); + String response = cardService.addTestWhiteList(openId); + System.out.println(response); + } + + /** + * 测试创建会员卡投放二维码 + * + * @throws Exception + */ + @Test + public void testCreateQrcodeMemberCard() throws Exception { + WxMpCardService cardService = this.wxService.getCardService(); + WxMpCardQrcodeCreateResult response = cardService.createQrcodeCard(cardId, "test"); + System.out.println(response); + } + + /** + * 测试创建货架接口 + * + * @throws Exception + */ + @Test + public void testCreateLandingPage() throws Exception { + WxMpCardService cardService = this.wxService.getCardService(); + WxMpCardLandingPageCreateRequest request = new WxMpCardLandingPageCreateRequest(); + request.setBanner("http://mmbiz.qpic.cn/mmbiz_jpg/upuF1LhUF8LjCLCFcQicgEiazFeonwDllGl6ibk4v5iaJDAbs7dGJU7iclOJ6nh7Hnz6ZsfDL8tGEeQVJyuhKsMFxUQ/0"); + request.setTitle("会员卡1"); + request.setScene(CardSceneType.SCENE_H5.name()); + request.addCard(cardId, "http://mmbiz.qpic.cn/mmbiz_jpg/upuF1LhUF8LjCLCFcQicgEiazFeonwDllGl6ibk4v5iaJDAbs7dGJU7iclOJ6nh7Hnz6ZsfDL8tGEeQVJyuhKsMFxUQ/0"); + WxMpCardLandingPageCreateResult response = cardService.createLandingPage(request); + System.out.println(response); + } + + @Test + public void testGetActivateUrl() throws Exception { + WxMpMemberCardService memberCardService = this.wxService.getMemberCardService(); + ActivatePluginParam response = memberCardService.getActivatePluginParam(cardId, "test"); + System.out.println(response); + } + + @Test + public void testGetActivateTempInfo() throws Exception { + String activateTicket = "fDZv9eMQAFfrNr3XBoqhb8eUX67DFb6h8yXDelGSMDLfg2OAIGQcU7mEKecnWZBK%2B%2Bvm%2FtZxZJrbRkdJB%2FUmpVoJkEsbeH%2BOefcntAsYDKA%3D"; + WxMpMemberCardService memberCardService = this.wxService.getMemberCardService(); + WxMpMemberCardActivateTempInfoResult result = memberCardService.getActivateTempInfo(activateTicket); + assertNotNull(result); + System.out.println(result); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java index dac37f266f..45c5e8af99 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java @@ -4,13 +4,16 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.bean.menu.WxMenuButton; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; import me.chanjar.weixin.mp.bean.menu.WxMpMenu; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; /** * 测试菜单 @@ -18,7 +21,7 @@ * @author chanjarster * @author Binary Wang */ -@Test(groups = "menuAPI") +@Test @Guice(modules = ApiTestModule.class) public class WxMpMenuServiceImplTest { @@ -87,6 +90,56 @@ public void testCreateConditionalMenu() throws WxErrorException { System.out.println(this.menuId); } + @Test + public void testMultiCreateConditionalMenu() throws WxErrorException { + String json = "{\n" + + " \"button\":[\n" + + " { \n" + + " \"type\":\"click\",\n" + + " \"name\":\"今日歌曲\",\n" + + " \"key\":\"V1001_TODAY_MUSIC\" \n" + + " },\n" + + " { \n" + + " \"name\":\"菜单\",\n" + + " \"sub_button\":[\n" + + " { \n" + + " \"type\":\"view\",\n" + + " \"name\":\"搜索\",\n" + + " \"url\":\"http://www.soso.com/\"\n" + + " },\n" + + " {\n" + + " \"type\":\"view\",\n" + + " \"name\":\"视频\",\n" + + " \"url\":\"http://v.qq.com/\"\n" + + " },\n" + + " {\n" + + " \"type\":\"click\",\n" + + " \"name\":\"赞一下我们\",\n" + + " \"key\":\"V1001_GOOD\"\n" + + " }]\n" + + " }],\n" + + "\"matchrule\":{\n" + + " \"tag_id\":\"2\",\n" + + " \"sex\":\"1\",\n" + + " \"country\":\"中国\",\n" + + " \"province\":\"广东\",\n" + + " \"city\":\"广州\",\n" + + " \"client_platform_type\":\"2\",\n" + + " \"language\":\"zh_CN\"\n" + + " }\n" + + "}"; + this.menuId = this.wxService.getMenuService().menuCreate(json); + System.out.println(this.menuId); + } + + @Test(dependsOnMethods = {"testCreateConditionalMenu"}) + public void testMenuGet_AfterCreateConditionalMenu() throws WxErrorException { + WxMpMenu wxMenu = this.wxService.getMenuService().menuGet(); + assertNotNull(wxMenu); + System.out.println(wxMenu.toJson()); + assertNotNull(wxMenu.getConditionalMenu().get(0).getRule().getTagId()); + } + @Test(dependsOnMethods = {"testCreateConditionalMenu"}) public void testDeleteConditionalMenu() throws WxErrorException { this.wxService.getMenuService().menuDelete(menuId); @@ -134,11 +187,11 @@ public void testCreateMenu_by_json() throws WxErrorException { @Test(dependsOnMethods = {"testMenuCreate"}) public void testMenuGet() throws WxErrorException { WxMpMenu wxMenu = this.wxService.getMenuService().menuGet(); - Assert.assertNotNull(wxMenu); + assertNotNull(wxMenu); System.out.println(wxMenu.toJson()); } - @Test(dependsOnMethods = {"testMenuGet"}) + @Test(dependsOnMethods = {"testMenuGet", "testMenuCreate"}) public void testMenuDelete() throws WxErrorException { this.wxService.getMenuService().menuDelete(); } @@ -147,36 +200,36 @@ public void testMenuDelete() throws WxErrorException { public Object[][] getMenu() { WxMenu menu = new WxMenu(); WxMenuButton button1 = new WxMenuButton(); - button1.setType(WxConsts.BUTTON_CLICK); + button1.setType(WxConsts.MenuButtonType.CLICK); button1.setName("今日歌曲"); button1.setKey("V1001_TODAY_MUSIC"); - WxMenuButton button2 = new WxMenuButton(); - button2.setType(WxConsts.BUTTON_MINIPROGRAM); - button2.setName("小程序"); - button2.setAppId("wx286b93c14bbf93aa"); - button2.setPagePath("pages/lunar/index.html"); - button2.setUrl("http://mp.weixin.qq.com"); +// WxMenuButton button2 = new WxMenuButton(); +// button2.setType(WxConsts.MenuButtonType.MINIPROGRAM); +// button2.setName("小程序"); +// button2.setAppId("wx286b93c14bbf93aa"); +// button2.setPagePath("pages/lunar/index.html"); +// button2.setUrl("http://mp.weixin.qq.com"); WxMenuButton button3 = new WxMenuButton(); button3.setName("菜单"); menu.getButtons().add(button1); - menu.getButtons().add(button2); +// menu.getButtons().add(button2); menu.getButtons().add(button3); WxMenuButton button31 = new WxMenuButton(); - button31.setType(WxConsts.BUTTON_VIEW); + button31.setType(WxConsts.MenuButtonType.VIEW); button31.setName("搜索"); button31.setUrl("http://www.soso.com/"); WxMenuButton button32 = new WxMenuButton(); - button32.setType(WxConsts.BUTTON_VIEW); + button32.setType(WxConsts.MenuButtonType.VIEW); button32.setName("视频"); button32.setUrl("http://v.qq.com/"); WxMenuButton button33 = new WxMenuButton(); - button33.setType(WxConsts.BUTTON_CLICK); + button33.setType(WxConsts.MenuButtonType.CLICK); button33.setName("赞一下我们"); button33.setKey("V1001_GOOD"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpOcrServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpOcrServiceImplTest.java new file mode 100644 index 0000000000..b3f45eb8d8 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpOcrServiceImplTest.java @@ -0,0 +1,394 @@ +package me.chanjar.weixin.mp.api.impl; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.api.test.TestConstants; +import me.chanjar.weixin.common.bean.ocr.WxOcrBankCardResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrBizLicenseResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrCommResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrDrivingLicenseResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrDrivingResult; +import me.chanjar.weixin.common.bean.ocr.WxOcrIdCardResult; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2019-06-22 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpOcrServiceImplTest { + @Inject + private WxMpService service; + + @Test + public void testIdCard() throws WxErrorException { + final WxOcrIdCardResult result = this.service.getOcrService().idCard( + "https://res.wx.qq.com/op_res/E_oqdHqP4ETOJsT46sQnXz1HbeHOpqDQTuhkYeaLaJTf-JKld7de3091dwxCQwa6"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testIdCard2() throws Exception { + InputStream inputStream = this.getImageStream("https://res.wx.qq.com/op_res/E_oqdHqP4ETOJsT46sQnXz1HbeHOpqDQTuhkYeaLaJTf-JKld7de3091dwxCQwa6"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrIdCardResult result = this.service.getOcrService().idCard(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBankCard() throws WxErrorException { + final WxOcrBankCardResult result = this.service.getOcrService().bankCard("https://res.wx.qq.com/op_res/eP7PObYbBJj-_19EbGBL4PWe_zQ1NwET5NXSugjEWc-4ayns4Q-HFJrp-AOog8ih"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBankCard2() throws Exception { + InputStream inputStream = this.getImageStream("https://res.wx.qq.com/op_res/eP7PObYbBJj-_19EbGBL4PWe_zQ1NwET5NXSugjEWc-4ayns4Q-HFJrp-AOog8ih"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrBankCardResult result = this.service.getOcrService().bankCard(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDriving() throws WxErrorException { + final WxOcrDrivingResult result = this.service.getOcrService().driving("https://res.wx.qq.com/op_res/T051P5uWvh9gSJ9j78tWib53WiNi2pHSSZhoO8wnY3Av-djpsA4kA9whbtt6_Tb6"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDriving2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrDrivingResult result = this.service.getOcrService().driving(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDrivingLicense() throws WxErrorException { + final WxOcrDrivingLicenseResult result = this.service.getOcrService().drivingLicense("https://res.wx.qq.com/op_res/kD4YXjYVAW1eaQqn9uTA0rrOFoZRvVINitNDSGo5gJ7SzTCezNq_ZDDmU1I08kGn"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDrivingLicense2() throws Exception { + InputStream inputStream = this.getImageStream("https://res.wx.qq.com/op_res/kD4YXjYVAW1eaQqn9uTA0rrOFoZRvVINitNDSGo5gJ7SzTCezNq_ZDDmU1I08kGn"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrDrivingLicenseResult result = this.service.getOcrService().drivingLicense(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBizLicense() throws WxErrorException { + final WxOcrBizLicenseResult result = this.service.getOcrService().bizLicense("https://res.wx.qq.com/op_res/apCy0YbnEdjYsa_cjW6x3FlpCc20uQ-2BYE7aXnFsrB-ALHZNgdKXhzIUcrRnDoL"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBizLicense2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrBizLicenseResult result = this.service.getOcrService().bizLicense(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testComm() throws WxErrorException { + final WxOcrCommResult result = this.service.getOcrService().comm("https://res.wx.qq.com/op_res/apCy0YbnEdjYsa_cjW6x3FlpCc20uQ-2BYE7aXnFsrB-ALHZNgdKXhzIUcrRnDoL"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testComm2() throws Exception { + InputStream inputStream = ClassLoader.getSystemResourceAsStream("mm.jpeg"); + File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), TestConstants.FILE_JPG); + final WxOcrCommResult result = this.service.getOcrService().comm(tempFile); + assertThat(result).isNotNull(); + System.out.println(result); + } + + private InputStream getImageStream(String url) { + try { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setReadTimeout(5000); + connection.setConnectTimeout(5000); + connection.setRequestMethod("GET"); + if (HttpURLConnection.HTTP_OK == connection.getResponseCode()) { + return connection.getInputStream(); + } + } catch (IOException e) { + System.out.println("获取网络图片出现异常,图片路径为:" + url); + } + return null; + } + + public static class MockTest { + private final WxMpService wxService = mock(WxMpService.class); + + @Test + public void testIdCard() throws Exception { + String returnJson = "{\"type\":\"Back\",\"name\":\"张三\",\"id\":\"110101199909090099\",\"valid_date\":\"20110101-20210201\"}"; + + when(wxService.post(anyString(), anyString())).thenReturn(returnJson); + final WxMpOcrServiceImpl wxMpOcrService = new WxMpOcrServiceImpl(wxService); + + final WxOcrIdCardResult result = wxMpOcrService.idCard("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBankCard() throws Exception { + String returnJson = "{\"number\":\"24234234345234\"}"; + + when(wxService.post(anyString(), anyString())).thenReturn(returnJson); + final WxMpOcrServiceImpl wxMpOcrService = new WxMpOcrServiceImpl(wxService); + + final WxOcrBankCardResult result = wxMpOcrService.bankCard("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDriving() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"plate_num\": \"粤xxxxx\", //车牌号码\n" + + " \"vehicle_type\": \"小型普通客车\", //车辆类型\n" + + " \"owner\": \"东莞市xxxxx机械厂\", //所有人\n" + + " \"addr\": \"广东省东莞市xxxxx号\", //住址\n" + + " \"use_character\": \"非营运\", //使用性质\n" + + " \"model\": \"江淮牌HFCxxxxxxx\", //品牌型号\n" + + " \"vin\": \"LJ166xxxxxxxx51\", //车辆识别代号\n" + + " \"engine_num\": \"J3xxxxx3\", //发动机号码\n" + + " \"register_date\": \"2018-07-06\", //注册日期\n" + + " \"issue_date\": \"2018-07-01\", //发证日期\n" + + " \"plate_num_b\": \"粤xxxxx\", //车牌号码\n" + + " \"record\": \"441xxxxxx3\", //号牌\n" + + " \"passengers_num\": \"7人\", //核定载人数\n" + + " \"total_quality\": \"2700kg\", //总质量\n" + + " \"prepare_quality\": \"1995kg\", //整备质量\n" + + " \"overall_size\": \"4582x1795x1458mm\", //外廓尺寸\n" + + " \"card_position_front\": {//卡片正面位置(检测到卡片正面才会返回)\n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 119, \n" + + " \"y\": 2925\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 1435, \n" + + " \"y\": 2887\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 1435, \n" + + " \"y\": 3793\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 119, \n" + + " \"y\": 3831\n" + + " }\n" + + " }\n" + + " }, \n" + + " \"card_position_back\": {//卡片反面位置(检测到卡片反面才会返回)\n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 1523, \n" + + " \"y\": 2849\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 2898, \n" + + " \"y\": 2887\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 2927, \n" + + " \"y\": 3831\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 1523, \n" + + " \"y\": 3831\n" + + " }\n" + + " }\n" + + " }, \n" + + " \"img_size\": {//图片大小\n" + + " \"w\": 3120, \n" + + " \"h\": 4208\n" + + " }\n" + + "}"; + + when(wxService.post(anyString(), anyString())).thenReturn(returnJson); + final WxMpOcrServiceImpl wxMpOcrService = new WxMpOcrServiceImpl(wxService); + + final WxOcrDrivingResult result = wxMpOcrService.driving("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testDrivingLicense() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"id_num\": \"660601xxxxxxxx1234\", //证号\n" + + " \"name\": \"张三\", //姓名\n" + + " \"sex\": \"男\", //性别\n" + + " \"nationality\": \"中国\", //国籍\n" + + " \"address\": \"广东省东莞市xxxxx号\", //住址\n" + + " \"birth_date\": \"1990-12-21\", //出生日期\n" + + " \"issue_date\": \"2012-12-21\", //初次领证日期\n" + + " \"car_class\": \"C1\", //准驾车型\n" + + " \"valid_from\": \"2018-07-06\", //有效期限起始日\n" + + " \"valid_to\": \"2020-07-01\", //有效期限终止日\n" + + " \"official_seal\": \"xx市公安局公安交通管理局\" //印章文字\n" + + "}"; + when(wxService.post(anyString(), anyString())).thenReturn(returnJson); + final WxMpOcrServiceImpl wxMpOcrService = new WxMpOcrServiceImpl(wxService); + + final WxOcrDrivingLicenseResult result = wxMpOcrService.drivingLicense("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testBizLicense() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"reg_num\": \"123123\",//注册号\n" + + " \"serial\": \"123123\",//编号\n" + + " \"legal_representative\": \"张三\", //法定代表人姓名\n" + + " \"enterprise_name\": \"XX饮食店\", //企业名称\n" + + " \"type_of_organization\": \"个人经营\", //组成形式\n" + + " \"address\": \"XX市XX区XX路XX号\", //经营场所/企业住所\n" + + " \"type_of_enterprise\": \"xxx\", //公司类型\n" + + " \"business_scope\": \"中型餐馆(不含凉菜、不含裱花蛋糕,不含生食海产品)。\", //经营范围\n" + + " \"registered_capital\": \"200万\", //注册资本\n" + + " \"paid_in_capital\": \"200万\", //实收资本\n" + + " \"valid_period\": \"2019年1月1日\", //营业期限\n" + + " \"registered_date\": \"2018年1月1日\", //注册日期/成立日期\n" + + " \"cert_position\": { //营业执照位置\n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 155, \n" + + " \"y\": 191\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 725, \n" + + " \"y\": 157\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 743, \n" + + " \"y\": 512\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 164, \n" + + " \"y\": 525\n" + + " }\n" + + " }\n" + + " }, \n" + + " \"img_size\": { //图片大小\n" + + " \"w\": 966, \n" + + " \"h\": 728\n" + + " }\n" + + "}"; + when(wxService.post(anyString(), anyString())).thenReturn(returnJson); + final WxMpOcrServiceImpl wxMpOcrService = new WxMpOcrServiceImpl(wxService); + + final WxOcrBizLicenseResult result = wxMpOcrService.bizLicense("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testComm() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"items\": [ //识别结果\n" + + " {\n" + + " \"text\": \"腾讯\", \n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 575, \n" + + " \"y\": 519\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 744, \n" + + " \"y\": 519\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 744, \n" + + " \"y\": 532\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 573, \n" + + " \"y\": 532\n" + + " }\n" + + " }\n" + + " }, \n" + + " {\n" + + " \"text\": \"微信团队\", \n" + + " \"pos\": {\n" + + " \"left_top\": {\n" + + " \"x\": 670, \n" + + " \"y\": 516\n" + + " }, \n" + + " \"right_top\": {\n" + + " \"x\": 762, \n" + + " \"y\": 517\n" + + " }, \n" + + " \"right_bottom\": {\n" + + " \"x\": 762, \n" + + " \"y\": 532\n" + + " }, \n" + + " \"left_bottom\": {\n" + + " \"x\": 670, \n" + + " \"y\": 531\n" + + " }\n" + + " }\n" + + " }\n" + + " ], \n" + + " \"img_size\": { //图片大小\n" + + " \"w\": 1280, \n" + + " \"h\": 720\n" + + " }\n" + + "}"; + when(wxService.post(anyString(), anyString())).thenReturn(returnJson); + final WxMpOcrServiceImpl wxMpOcrService = new WxMpOcrServiceImpl(wxService); + + final WxOcrCommResult result = wxMpOcrService.comm("abc"); + assertThat(result).isNotNull(); + System.out.println(result); + } + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java index c61e38339c..833671e4b0 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java @@ -1,14 +1,17 @@ package me.chanjar.weixin.mp.api.impl; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.testng.*; import org.testng.annotations.*; import java.io.File; +import java.io.IOException; /** * 测试用户相关的接口 @@ -26,12 +29,27 @@ public Object[][] sceneIds() { return new Object[][]{{-1}, {0}, {1}, {200000}}; } + @DataProvider + public Object[][] sceneStrs() { + return new Object[][]{{null}, {""}, {"test"}, {RandomStringUtils.randomAlphanumeric(100)}}; + } + @Test(dataProvider = "sceneIds") public void testQrCodeCreateTmpTicket(int sceneId) throws WxErrorException { WxMpQrCodeTicket ticket = this.wxService.getQrcodeService().qrCodeCreateTmpTicket(sceneId, null); Assert.assertNotNull(ticket.getUrl()); Assert.assertNotNull(ticket.getTicket()); - Assert.assertTrue(ticket.getExpire_seconds() != -1); + Assert.assertTrue(ticket.getExpireSeconds() != -1); + System.out.println(ticket); + } + + + @Test(dataProvider = "sceneStrs") + public void testQrCodeCreateTmpTicketWithSceneStr(String sceneStr) throws WxErrorException { + WxMpQrCodeTicket ticket = this.wxService.getQrcodeService().qrCodeCreateTmpTicket(sceneStr, null); + Assert.assertNotNull(ticket.getUrl()); + Assert.assertNotNull(ticket.getTicket()); + Assert.assertTrue(ticket.getExpireSeconds() != -1); System.out.println(ticket); } @@ -40,7 +58,7 @@ public void testQrCodeCreateLastTicket(int sceneId) throws WxErrorException { WxMpQrCodeTicket ticket = this.wxService.getQrcodeService().qrCodeCreateLastTicket(sceneId); Assert.assertNotNull(ticket.getUrl()); Assert.assertNotNull(ticket.getTicket()); - Assert.assertTrue(ticket.getExpire_seconds() == -1); + Assert.assertTrue(ticket.getExpireSeconds() == -1); System.out.println(ticket); } @@ -49,6 +67,12 @@ public void testQrCodePicture() throws WxErrorException { File file = this.wxService.getQrcodeService().qrCodePicture(ticket); Assert.assertNotNull(file); System.out.println(file.getAbsolutePath()); + + try { + FileUtils.copyFile(file,new File("d:\\t.jpg")); + } catch (IOException e) { + e.printStackTrace(); + } } public void testQrCodePictureUrl() throws WxErrorException { diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java index d5bf621287..c450775d25 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java @@ -1,152 +1,62 @@ package me.chanjar.weixin.mp.api.impl; +import org.apache.commons.lang3.StringUtils; +import org.testng.*; +import org.testng.annotations.*; + import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConfigStorage; -import org.testng.*; -import org.testng.annotations.*; +import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; +import me.chanjar.weixin.common.enums.TicketType; + +import static org.testng.Assert.*; @Test @Guice(modules = ApiTestModule.class) public class WxMpServiceImplTest { - @Inject private WxMpService wxService; @Test - public void testCheckSignature() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testGetAccessToken() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testGetAccessTokenBoolean() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testGetJsapiTicket() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testGetJsapiTicketBoolean() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testCreateJsapiSignature() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testCustomMessageSend() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testMassNewsUpload() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testMassVideoUpload() { - Assert.fail("Not yet implemented"); - } + public void testGetCurrentAutoReplyInfo() throws WxErrorException { + WxMpCurrentAutoReplyInfo autoReplyInfo = this.wxService.getCurrentAutoReplyInfo(); - @Test - public void testMassGroupMessageSend() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testMassOpenIdsMessageSend() { - Assert.fail("Not yet implemented"); + assertNotNull(autoReplyInfo); + System.out.println(autoReplyInfo); } @Test - public void testMassMessagePreview() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testShortUrl() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testSetIndustry() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testGetIndustry() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testSemanticQuery() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testOauth2buildAuthorizationUrl() { - Assert.fail("Not yet implemented"); + public void testClearQuota() throws WxErrorException { + this.wxService.clearQuota(wxService.getWxMpConfigStorage().getAppId()); } @Test public void testBuildQrConnectUrl() { String qrconnectRedirectUrl = ((TestConfigStorage) this.wxService.getWxMpConfigStorage()).getQrconnectRedirectUrl(); String qrConnectUrl = this.wxService.buildQrConnectUrl(qrconnectRedirectUrl, - WxConsts.QRCONNECT_SCOPE_SNSAPI_LOGIN, null); + WxConsts.QrConnectScope.SNSAPI_LOGIN, null); Assert.assertNotNull(qrConnectUrl); System.out.println(qrConnectUrl); } - @Test - public void testOauth2getAccessToken() { - Assert.fail("Not yet implemented"); + public void testGetTicket() throws WxErrorException { + String ticket = this.wxService.getTicket(TicketType.SDK, false); + System.out.println(ticket); + Assert.assertNotNull(ticket); } - @Test - public void testOauth2refreshAccessToken() { - Assert.fail("Not yet implemented"); - } + public void testRefreshAccessToken() throws WxErrorException { + WxMpConfigStorage configStorage = this.wxService.getWxMpConfigStorage(); + String before = configStorage.getAccessToken(); + this.wxService.getAccessToken(false); - @Test - public void testOauth2getUserInfo() { - Assert.fail("Not yet implemented"); + String after = configStorage.getAccessToken(); + Assert.assertNotEquals(before, after); + Assert.assertTrue(StringUtils.isNotBlank(after)); } - - @Test - public void testOauth2validateAccessToken() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testGetCallbackIP() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testGet() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testPost() { - Assert.fail("Not yet implemented"); - } - - @Test - public void testExecute() { - Assert.fail("Not yet implemented"); - } - } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImplTest.java new file mode 100644 index 0000000000..f608cd19b0 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImplTest.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.bean.WxMpShakeInfoResult; +import me.chanjar.weixin.mp.bean.WxMpShakeQuery; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + * 测试摇一摇周边相关的接口 + * + * @author rememberber + */ +@Test(groups = "userAPI") +@Guice(modules = ApiTestModule.class) +public class WxMpShakeServiceImplTest { + @Inject + private WxMpService wxService; + + public void testGetShakeInfo() throws Exception { + WxMpShakeQuery wxMpShakeQuery = new WxMpShakeQuery(); + wxMpShakeQuery.setTicket("b87db7df490e5cbe4f598272f77f46be"); + wxMpShakeQuery.setNeedPoi(1); + WxMpShakeInfoResult wxMpShakeInfoResult = this.wxService.getShakeService().getShakeInfo(wxMpShakeQuery); + + System.out.println(); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java index e4edca7491..564259c664 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.mp.api.impl; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.bean.store.WxMpStoreBaseInfo; @@ -12,11 +12,12 @@ import java.math.BigDecimal; import java.util.List; -import static org.junit.Assert.assertNotNull; +import static org.testng.AssertJUnit.*; /** - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016-09-23. + * Created by Binary Wang on 2016-09-23. + * + * @author Binary Wang */ @Test @Guice(modules = ApiTestModule.class) @@ -25,25 +26,36 @@ public class WxMpStoreServiceImplTest { private WxMpService wxMpService; /** - * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpStoreServiceImpl#add(me.chanjar.weixin.mp.bean.store.WxMpStoreBaseInfo)}. - * - * @throws WxErrorException + * Test method for {@link WxMpStoreServiceImpl#add(WxMpStoreBaseInfo)}. */ public void testAdd() throws WxErrorException { - this.wxMpService.getStoreService().add(WxMpStoreBaseInfo.builder().build()); this.wxMpService.getStoreService() - .add(WxMpStoreBaseInfo.builder().businessName("haha").branchName("abc") - .province("aaa").district("aaa").telephone("122").address("abc").categories(new String[]{"美食,江浙菜"}) + .add(WxMpStoreBaseInfo.builder() + .businessName("haha") + .branchName("abc") + .province("aaa") + .district("aaa") + .telephone("122") + .address("abc") + .categories(new String[]{"美食,江浙菜"}) .longitude(new BigDecimal("115.32375")) - .latitude(new BigDecimal("25.097486")).city("aaa").offsetType(1) + .latitude(new BigDecimal("25.097486")) + .city("aaa") .build()); + // 以下运行会抛异常 + this.wxMpService.getStoreService().add(WxMpStoreBaseInfo.builder().build()); } public void testUpdate() throws WxErrorException { this.wxMpService.getStoreService() - .update(WxMpStoreBaseInfo.builder().poiId("291503654").telephone("020-12345678") - .sid("aaa").avgPrice(35).openTime("8:00-20:00").special("免费wifi,外卖服务") - .introduction("麦当劳是全球大型跨国连锁餐厅,1940 年创立于美国,在世界上大约拥有3 万间分店。主要售卖汉堡包,以及薯条、炸鸡、汽水、冰品、沙拉、水果等快餐食品").offsetType(1) + .update(WxMpStoreBaseInfo.builder() + .poiId("291503654") + .telephone("020-12345678") + .sid("aaa") + .avgPrice(35) + .openTime("8:00-20:00") + .special("免费wifi,外卖服务") + .introduction("麦当劳是全球大型跨国连锁餐厅,1940 年创立于美国,在世界上大约拥有3 万间分店。主要售卖汉堡包,以及薯条、炸鸡、汽水、冰品、沙拉、水果等快餐食品") .build()); } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpSubscribeMsgServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpSubscribeMsgServiceImplTest.java new file mode 100644 index 0000000000..77a661ee58 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpSubscribeMsgServiceImplTest.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.api.test.TestConfigStorage; +import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage; +import org.testng.Assert; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + * @author Mklaus + * @date 2018-01-22 下午2:02 + */ +@Guice(modules = ApiTestModule.class) +public class WxMpSubscribeMsgServiceImplTest { + + @Inject + protected WxMpService wxService; + + @Test + public void testSendSubscribeMessage() throws WxErrorException { + TestConfigStorage configStorage = (TestConfigStorage) this.wxService + .getWxMpConfigStorage(); + + WxMpSubscribeMessage message = WxMpSubscribeMessage.builder() + .title("weixin test") + .toUser(configStorage.getOpenid()) + .scene("1000") + .contentColor("#FF0000") + .contentValue("Send subscribe message test") + .build(); + + try { + boolean send = this.wxService.getSubscribeMsgService().sendSubscribeMessage(message); + Assert.assertTrue(send); + } catch (WxErrorException e) { + // 当用户没有授权,获取之前的授权已使用。微信会返回错误代码 {"errcode":43101,"errmsg":"user refuse to accept the msg hint: [xxxxxxxxxxx]"} + if (e.getError().getErrorCode() != 43101) { + throw e; + } + } + + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java index 2c348702ca..884e41b59d 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java @@ -1,17 +1,15 @@ package me.chanjar.weixin.mp.api.impl; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConfigStorage; -import me.chanjar.weixin.mp.bean.template.WxMpTemplate; -import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; -import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; -import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; +import me.chanjar.weixin.mp.bean.template.*; import org.apache.commons.lang3.RandomStringUtils; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; import java.text.SimpleDateFormat; import java.util.Date; @@ -20,8 +18,9 @@ /** *
      * Created by Binary Wang on 2016-10-14.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ @Guice(modules = ApiTestModule.class) public class WxMpTemplateMsgServiceImplTest { @@ -30,18 +29,16 @@ public class WxMpTemplateMsgServiceImplTest { @Test(invocationCount = 5, threadPoolSize = 3) public void testSendTemplateMsg() throws WxErrorException { - SimpleDateFormat dateFormat = new SimpleDateFormat( - "yyyy-MM-dd HH:mm:ss.SSS"); - TestConfigStorage configStorage = (TestConfigStorage) this.wxService - .getWxMpConfigStorage(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + TestConfigStorage configStorage = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() .toUser(configStorage.getOpenid()) - .templateId(configStorage.getTemplateId()).build(); - templateMessage.addWxMpTemplateData( - new WxMpTemplateData("first", dateFormat.format(new Date()), "#FF00FF")); - templateMessage.addWxMpTemplateData( - new WxMpTemplateData("remark", RandomStringUtils.randomAlphanumeric(100), "#FF00FF")); - templateMessage.setUrl(" "); + .templateId(configStorage.getTemplateId()) + .url(" ") + .build(); + + templateMessage.addData(new WxMpTemplateData("first", dateFormat.format(new Date()), "#FF00FF")) + .addData(new WxMpTemplateData("remark", RandomStringUtils.randomAlphanumeric(100), "#FF00FF")); String msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage); Assert.assertNotNull(msgId); System.out.println(msgId); @@ -56,8 +53,8 @@ public void testGetIndustry() throws Exception { @Test public void testSetIndustry() throws Exception { - WxMpTemplateIndustry industry = new WxMpTemplateIndustry(new WxMpTemplateIndustry.Industry("1"), - new WxMpTemplateIndustry.Industry("04")); + WxMpTemplateIndustry industry = new WxMpTemplateIndustry(WxMpTemplateIndustryEnum.findByCode(29), + WxMpTemplateIndustryEnum.findByCode(8)); boolean result = this.wxService.getTemplateMsgService().setIndustry(industry); Assert.assertTrue(result); } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java index 27f117916e..2d27e4dac7 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java @@ -1,18 +1,28 @@ package me.chanjar.weixin.mp.api.impl; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConfigStorage; import me.chanjar.weixin.mp.bean.WxMpUserQuery; +import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid; import me.chanjar.weixin.mp.bean.result.WxMpUser; import me.chanjar.weixin.mp.bean.result.WxMpUserList; -import org.testng.*; -import org.testng.annotations.*; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.User.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * 测试用户相关的接口 @@ -20,7 +30,7 @@ * @author chanjarster * @author Binary Wang */ -@Test(groups = "userAPI") +@Test @Guice(modules = ApiTestModule.class) public class WxMpUserServiceImplTest { @@ -68,10 +78,55 @@ public void testUserInfoListByWxMpUserQuery() throws WxErrorException { public void testUserList() throws WxErrorException { WxMpUserList wxMpUserList = this.wxService.getUserService().userList(null); Assert.assertNotNull(wxMpUserList); - Assert.assertFalse(wxMpUserList.getCount() == -1); - Assert.assertFalse(wxMpUserList.getTotal() == -1); - Assert.assertFalse(wxMpUserList.getOpenids().size() == -1); + Assert.assertNotEquals(-1, wxMpUserList.getCount()); + Assert.assertNotEquals(-1, wxMpUserList.getTotal()); + Assert.assertNotEquals(-1, wxMpUserList.getOpenids().size()); System.out.println(wxMpUserList); } + public void testChangeOpenid() throws WxErrorException { + List openids = new ArrayList<>(); + openids.add(this.configProvider.getOpenid()); + List wxMpChangeOpenidList = this.wxService.getUserService().changeOpenid("原公众号appid", openids); + Assert.assertNotNull(wxMpChangeOpenidList); + Assert.assertEquals(1, wxMpChangeOpenidList.size()); + WxMpChangeOpenid wxMpChangeOpenid = wxMpChangeOpenidList.get(0); + Assert.assertNotNull(wxMpChangeOpenid); + Assert.assertEquals(this.configProvider.getOpenid(), wxMpChangeOpenid.getOriOpenid()); + System.out.println(wxMpChangeOpenid); + } + + public static class MockTest { + private WxMpService wxService = mock(WxMpService.class); + + @Test + public void testMockChangeOpenid() throws WxErrorException { + List openids = new ArrayList<>(); + openids.add("oEmYbwN-n24jxvk4Sox81qedINkQ"); + openids.add("oEmYbwH9uVd4RKJk7ZZg6SzL6tTo"); + String fromAppid = "old_appid"; + Map map = new HashMap<>(); + map.put("from_appid", fromAppid); + map.put("openid_list", openids); + + String returnJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"result_list\": [{\"ori_openid\": \"oEmYbwN-n24jxvk4Sox81qedINkQ\",\"new_openid\": \"o2FwqwI9xCsVadFah_HtpPfaR-X4\",\"err_msg\": \"ok\"},{\"ori_openid\": \"oEmYbwH9uVd4RKJk7ZZg6SzL6tTo\",\"err_msg\": \"ori_openid error\"}]}"; + when(wxService.post(USER_CHANGE_OPENID_URL, WxMpGsonBuilder.create().toJson(map))).thenReturn(returnJson); + List wxMpChangeOpenidList = this.wxService.getUserService().changeOpenid(fromAppid, openids); + Assert.assertNotNull(wxMpChangeOpenidList); + Assert.assertEquals(2, wxMpChangeOpenidList.size()); + WxMpChangeOpenid wxMpChangeOpenid = wxMpChangeOpenidList.get(0); + Assert.assertNotNull(wxMpChangeOpenid); + Assert.assertEquals("oEmYbwN-n24jxvk4Sox81qedINkQ", wxMpChangeOpenid.getOriOpenid()); + Assert.assertEquals("o2FwqwI9xCsVadFah_HtpPfaR-X4", wxMpChangeOpenid.getNewOpenid()); + Assert.assertEquals("ok", wxMpChangeOpenid.getErrMsg()); + wxMpChangeOpenid = wxMpChangeOpenidList.get(1); + Assert.assertNotNull(wxMpChangeOpenid); + Assert.assertEquals("oEmYbwH9uVd4RKJk7ZZg6SzL6tTo", wxMpChangeOpenid.getOriOpenid()); + Assert.assertNull(wxMpChangeOpenid.getNewOpenid()); + Assert.assertEquals("ori_openid error", wxMpChangeOpenid.getErrMsg()); + System.out.println(wxMpChangeOpenid); + } + + } + } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImplTest.java index cc8676d477..1a96ba510a 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserTagServiceImplTest.java @@ -12,8 +12,9 @@ import java.util.List; /** - * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016/9/2. + * Created by Binary Wang on 2016/9/2. + * + * @author Binary Wang */ @Test @Guice(modules = ApiTestModule.class) diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpWifiServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpWifiServiceImplTest.java new file mode 100644 index 0000000000..d9225c7bc5 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpWifiServiceImplTest.java @@ -0,0 +1,97 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.bean.wifi.WxMpWifiShopDataResult; +import me.chanjar.weixin.mp.bean.wifi.WxMpWifiShopListResult; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Wifi.BIZWIFI_SHOP_GET; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + *
    + *  Created by BinaryWang on 2018/6/10.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpWifiServiceImplTest { + @Inject + private WxMpService wxService; + + @Test + public void testListShop() throws WxErrorException { + final WxMpWifiShopListResult result = this.wxService.getWifiService().listShop(1, 2); + assertThat(result).isNotNull(); + System.out.println(result); + } + + @Test + public void testGetShopWifiInfo() throws WxErrorException { + final WxMpWifiShopDataResult wifiInfo = this.wxService.getWifiService().getShopWifiInfo(123); + assertThat(wifiInfo).isNotNull(); + System.out.println(wifiInfo); + } + + @Test + public void testUpdateShopWifiInfo() throws WxErrorException { + final boolean result = this.wxService.getWifiService() + .updateShopWifiInfo(123, "123", "345", null); + assertThat(result).isTrue(); + } + + public static class MockTest { + private WxMpService wxService = mock(WxMpService.class); + + @Test + public void testGetShopWifiInfo() throws Exception { + String returnJson = "{\n" + + " \"errcode\": 0,\n" + + " \"data\": {\n" + + " \"shop_name\": \"南山店\",\n" + + " \"ssid\": \" WX123\",\n" + + " \"ssid_list\": [\n" + + " \"WX123\",\n" + + " \"WX456\"\n" + + " ],\n" + + " \"ssid_password_list\": [\n" + + " {\n" + + " \"ssid\": \"WX123\",\n" + + " \"password\": \"123456789\"\n" + + " },\n" + + " {\n" + + " \"ssid\": \"WX456\",\n" + + " \"password\": \"21332465dge\"\n" + + " }\n" + + " ],\n" + + " \"password\": \"123456789\",\n" + + " \"protocol_type\": 4,\n" + + " \"ap_count\": 2,\n" + + " \"template_id\": 1,\n" + + " \"homepage_url\": \"http://www.weixin.qq.com/\",\n" + + " \"bar_type\": 1,\n" + + " \"sid\":\"\",\n" + + " \"poi_id\":\"285633617\"\n" + + " }\n" + + "}"; + + when(wxService.post(eq(BIZWIFI_SHOP_GET), anyString())).thenReturn(returnJson); + when(wxService.getWifiService()).thenReturn(new WxMpWifiServiceImpl(wxService)); + + final WxMpWifiShopDataResult wifiInfo = this.wxService.getWifiService().getShopWifiInfo(123); + assertThat(wifiInfo).isNotNull(); + System.out.println(wifiInfo); + + } + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxOAuth2ServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxOAuth2ServiceImplTest.java new file mode 100644 index 0000000000..8729f99d2f --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxOAuth2ServiceImplTest.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.mp.api.impl; + +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2020-08-09 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxOAuth2ServiceImplTest { + @Inject + private WxMpService mpService; + + @Test + public void testBuildAuthorizationUrl() { + final String url = this.mpService.getOAuth2Service().buildAuthorizationUrl("http://www.baidu.com", "test", "GOD"); + assertThat(url).isEqualTo("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + + this.mpService.getWxMpConfigStorage().getAppId() + + "&redirect_uri=http%3A%2F%2Fwww.baidu.com&response_type=code&scope=test&state=GOD&connect_redirect=1#wechat_redirect"); + } + + @Test + public void testGetAccessToken() { + } + + @Test + public void testRefreshAccessToken() { + } + + @Test + public void testGetUserInfo() { + } + + @Test + public void testValidateAccessToken() { + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java index 31ad47b6f7..cc964e80fd 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java @@ -1,31 +1,42 @@ package me.chanjar.weixin.mp.api.test; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.locks.ReentrantLock; + +import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.inject.Binder; import com.google.inject.Module; import com.thoughtworks.xstream.XStream; import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.locks.ReentrantLock; public class ApiTestModule implements Module { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + private static final String TEST_CONFIG_XML = "test-config.xml"; @Override public void configure(Binder binder) { - try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { - TestConfigStorage config = this.fromXml(TestConfigStorage.class, is1); + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(TEST_CONFIG_XML)) { + if (inputStream == null) { + throw new RuntimeException("测试配置文件【" + TEST_CONFIG_XML + "】未找到,请参照test-config-sample.xml文件生成"); + } + + TestConfigStorage config = this.fromXml(TestConfigStorage.class, inputStream); config.setAccessTokenLock(new ReentrantLock()); - WxMpService wxService = new WxMpServiceImpl(); - wxService.setWxMpConfigStorage(config); + WxMpService mpService = new WxMpServiceHttpClientImpl(); + + mpService.setWxMpConfigStorage(config); + mpService.addConfigStorage("another", config); - binder.bind(WxMpService.class).toInstance(wxService); binder.bind(WxMpConfigStorage.class).toInstance(config); + binder.bind(WxMpService.class).toInstance(mpService); } catch (IOException e) { - e.printStackTrace(); + this.log.error(e.getMessage(), e); } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java index 6400f81dee..5266beb237 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java @@ -1,13 +1,13 @@ package me.chanjar.weixin.mp.api.test; import com.thoughtworks.xstream.annotations.XStreamAlias; -import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; import org.apache.commons.lang3.builder.ToStringBuilder; import java.util.concurrent.locks.Lock; @XStreamAlias("xml") -public class TestConfigStorage extends WxMpInMemoryConfigStorage { +public class TestConfigStorage extends WxMpDefaultConfigImpl { private String openid; private String kfAccount; @@ -52,10 +52,12 @@ public void setQrconnectRedirectUrl(String qrconnectRedirectUrl) { this.qrconnectRedirectUrl = qrconnectRedirectUrl; } + @Override public String getTemplateId() { return this.templateId; } + @Override public void setTemplateId(String templateId) { this.templateId = templateId; } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResultTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResultTest.java new file mode 100644 index 0000000000..f414967557 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResultTest.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * + * @author YuJian + * @version 2017/7/15 + */ +public class WxMpMemberCardUpdateResultTest { + + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"result_bonus\": 100,\n" + + " \"result_balance\": 200,\n" + + " \"openid\": \"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA\"\n" + + "}"; + + WxMpMemberCardUpdateResult result = WxMpMemberCardUpdateResult.fromJson(json); + + assertNotNull(result); + assertTrue(result.getErrorCode().equalsIgnoreCase("0")); + + System.out.println(result); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResultTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResultTest.java new file mode 100644 index 0000000000..714ec97280 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResultTest.java @@ -0,0 +1,80 @@ +package me.chanjar.weixin.mp.bean.card.membercard; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * + * @author YuJian + * @version 2017/7/15 + */ +public class WxMpMemberCardUserInfoResultTest { + + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"openid\": \"obLatjjwDolFj******wNqRXw\",\n" + + " \"nickname\": \"*******\",\n" + + " \"membership_number\": \"658*****445\",\n" + + " \"bonus\": 995,\n" + + " \"sex\": \"MALE\",\n" + + " \"user_info\": {\n" + + " \"common_field_list\": [\n" + + " {\n" + + " \"name\": \"USER_FORM_INFO_FLAG_MOBILE\",\n" + + " \"value\": \"15*****518\"\n" + + " },\n" + + " {\n" + + " \"name\": \"USER_FORM_INFO_FLAG_NAME\",\n" + + " \"value\": \"HK\"\n" + + " },\n" + + " {\n" + + " \"name\": \"USER_FORM_INFO_FLAG_EDUCATION_BACKGROUND\",\n" + + " \"value\": \"研究生\"\n" + + " }\n" + + " ],\n" + + " \"custom_field_list\": [\n" + + " {\n" + + " \"name\": \"兴趣\",\n" + + " \"value\": \"钢琴\",\n" + + " \"value_list\": []\n" + + " },\n" + + " {\n" + + " \"name\": \"喜好\",\n" + + " \"value\": \"郭敬明\",\n" + + " \"value_list\": []\n" + + " },\n" + + " {\n" + + " \"name\": \"职业\",\n" + + " \"value\": \"\",\n" + + " \"value_list\": [\n" + + " \"赛车手\",\n" + + " \"旅行家\"\n" + + " ]\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"user_card_status\": \"NORMAL\",\n" + + " \"has_active\": false\n" + + "}"; + + + WxMpMemberCardUserInfoResult userInfoResult = WxMpMemberCardUserInfoResult.fromJson(json); + + assertNotNull(userInfoResult); + assertFalse(userInfoResult.getHasActive()); + assertTrue(userInfoResult.getSex().equalsIgnoreCase("MALE")); + assertNotNull(userInfoResult.getUserInfo()); + assertNotNull(userInfoResult.getUserInfo().getCommonFieldList()); + assertNotNull(userInfoResult.getUserInfo().getCustomFieldList()); + assertTrue(userInfoResult.getUserInfo().getCommonFieldList().length == 3); + assertTrue(userInfoResult.getUserInfo().getCustomFieldList()[2].getValueList()[0].equalsIgnoreCase("赛车手")); + + System.out.println(userInfoResult); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java index c25c502495..ec754875da 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java @@ -2,8 +2,8 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage.WxArticle; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Test; @Test public class WxMpKefuMessageTest { @@ -11,68 +11,78 @@ public class WxMpKefuMessageTest { public void testTextReply() { WxMpKefuMessage reply = new WxMpKefuMessage(); reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + reply.setMsgType(WxConsts.KefuMsgType.TEXT); reply.setContent("sfsfdsdf"); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + Assert + .assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); } public void testTextBuild() { WxMpKefuMessage reply = WxMpKefuMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build(); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + Assert + .assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); } public void testImageReply() { WxMpKefuMessage reply = new WxMpKefuMessage(); reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); + reply.setMsgType(WxConsts.KefuMsgType.IMAGE); reply.setMediaId("MEDIA_ID"); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); } public void testImageBuild() { WxMpKefuMessage reply = WxMpKefuMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build(); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); } public void testVoiceReply() { WxMpKefuMessage reply = new WxMpKefuMessage(); reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_VOICE); + reply.setMsgType(WxConsts.KefuMsgType.VOICE); reply.setMediaId("MEDIA_ID"); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); } public void testVoiceBuild() { WxMpKefuMessage reply = WxMpKefuMessage.VOICE().toUser("OPENID").mediaId("MEDIA_ID").build(); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); } public void testVideoReply() { WxMpKefuMessage reply = new WxMpKefuMessage(); reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_VIDEO); + reply.setMsgType(WxConsts.KefuMsgType.VIDEO); reply.setMediaId("MEDIA_ID"); reply.setThumbMediaId("MEDIA_ID"); reply.setTitle("TITLE"); reply.setDescription("DESCRIPTION"); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); } public void testVideoBuild() { - WxMpKefuMessage reply = WxMpKefuMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); + WxMpKefuMessage reply = WxMpKefuMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID") + .thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); } public void testMusicReply() { WxMpKefuMessage reply = new WxMpKefuMessage(); reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_MUSIC); + reply.setMsgType(WxConsts.KefuMsgType.MUSIC); reply.setThumbMediaId("MEDIA_ID"); reply.setDescription("DESCRIPTION"); reply.setTitle("TITLE"); reply.setMusicUrl("MUSIC_URL"); reply.setHqMusicUrl("HQ_MUSIC_URL"); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"music\",\"music\":{\"title\":\"TITLE\",\"description\":\"DESCRIPTION\",\"thumb_media_id\":\"MEDIA_ID\",\"musicurl\":\"MUSIC_URL\",\"hqmusicurl\":\"HQ_MUSIC_URL\"}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"music\",\"music\":{\"title\":\"TITLE\",\"description\":\"DESCRIPTION\",\"thumb_media_id\":\"MEDIA_ID\",\"musicurl\":\"MUSIC_URL\",\"hqmusicurl\":\"HQ_MUSIC_URL\"}}"); } public void testMusicBuild() { @@ -84,13 +94,14 @@ public void testMusicBuild() { .musicUrl("MUSIC_URL") .hqMusicUrl("HQ_MUSIC_URL") .build(); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"music\",\"music\":{\"title\":\"TITLE\",\"description\":\"DESCRIPTION\",\"thumb_media_id\":\"MEDIA_ID\",\"musicurl\":\"MUSIC_URL\",\"hqmusicurl\":\"HQ_MUSIC_URL\"}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"music\",\"music\":{\"title\":\"TITLE\",\"description\":\"DESCRIPTION\",\"thumb_media_id\":\"MEDIA_ID\",\"musicurl\":\"MUSIC_URL\",\"hqmusicurl\":\"HQ_MUSIC_URL\"}}"); } public void testNewsReply() { WxMpKefuMessage reply = new WxMpKefuMessage(); reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_NEWS); + reply.setMsgType(WxConsts.KefuMsgType.NEWS); WxArticle article1 = new WxArticle(); article1.setUrl("URL"); @@ -106,8 +117,8 @@ public void testNewsReply() { article2.setTitle("Happy Day"); reply.getArticles().add(article2); - - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); } public void testNewsBuild() { @@ -125,7 +136,34 @@ public void testNewsBuild() { WxMpKefuMessage reply = WxMpKefuMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); - Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + } + + public void testMiniProgramPageBuild() { + WxMpKefuMessage reply = WxMpKefuMessage.MINIPROGRAMPAGE() + .toUser("OPENID") + .title("title") + .appId("appid") + .pagePath("pagepath") + .thumbMediaId("thumb_media_id") + .build(); + + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"miniprogrampage\",\"miniprogrampage\":{\"title\":\"title\",\"appid\":\"appid\",\"pagepath\":\"pagepath\",\"thumb_media_id\":\"thumb_media_id\"}}"); + } + + public void testMsgMenuBuild() { + WxMpKefuMessage reply = WxMpKefuMessage.MSGMENU() + .toUser("OPENID") + .addMenus(new WxMpKefuMessage.MsgMenu("101", "msgmenu1"), + new WxMpKefuMessage.MsgMenu("102", "msgmenu2")) + .headContent("head_content") + .tailContent("tail_content") + .build(); + + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"msgmenu\",\"msgmenu\":{\"head_content\":\"head_content\",\"list\":[{\"id\":\"101\",\"content\":\"msgmenu1\"},{\"id\":\"102\",\"content\":\"msgmenu2\"}],\"tail_content\":\"tail_content\"}}"); } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionTest.java new file mode 100644 index 0000000000..3d18b1ebbc --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionTest.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.common.collect.Lists; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 老板加点注释吧. + * + * @author Binary Wang + * @date 2019-07-14 + */ +public class WxMpUserActionTest { + + @Test + public void testListToJson() { + assertThat(WxMpUserAction.listToJson(Lists.newArrayList(WxMpUserAction.builder() + .actionParam(1) + .actionTime(122) + .actionType("haha") + .clickId("111") + .url("1222") + .userActionSetId(111L) + .build() + ))).isEqualTo("{\"actions\":[{\"user_action_set_id\":111,\"url\":\"1222\",\"action_time\":122,\"action_type\":\"haha\",\"trace\":{\"click_id\":\"111\"},\"action_param\":{\"value\":1}}]}"); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResultTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResultTest.java new file mode 100644 index 0000000000..0b80ed083e --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResultTest.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.mp.bean.menu; + +import org.testng.annotations.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Binary Wang + * @date 2019-08-05 + */ +public class WxMpGetSelfMenuInfoResultTest { + + @Test + public void testFromJson() { + String json = "{\"is_menu_open\":1,\"selfmenu_info\":{\"button\":[{\"name\":\"学院\",\"sub_button\":{\"list\":[{\"type\":\"miniprogram\",\"name\":\"成语答题王\",\"url\":\"http:\\/\\/host\",\"appid\":\"wxf4dc5b4e7b35dcd1\",\"pagepath\":\"pages\\/index\\/index\"},{\"type\":\"miniprogram\",\"name\":\"大师课程\",\"url\":\"https:\\/\\/host\\/course\\/tutorial\",\"appid\":\"wxfd6acd566482c6cb\",\"pagepath\":\"pages\\/tutorialDetail\\/tutorialDetail\"}]}},{\"type\":\"miniprogram\",\"name\":\"学科商城\",\"url\":\"https:\\/\\/host\\/-dAEuY\",\"appid\":\"wx720f9f1301595564\",\"pagepath\":\"pages\\/home\\/dashboard\\/index\"}]}}"; + final WxMpGetSelfMenuInfoResult selfMenuInfoResult = WxMpGetSelfMenuInfoResult.fromJson(json); + assertThat(selfMenuInfoResult).isNotNull(); + assertThat(selfMenuInfoResult.getIsMenuOpen()).isEqualTo(1); + assertThat(selfMenuInfoResult.getSelfMenuInfo()).isNotNull(); + final List buttons = selfMenuInfoResult.getSelfMenuInfo().getButtons(); + assertThat(buttons.size()).isEqualTo(2); + assertThat(buttons.get(1).getAppId()).isEqualTo("wx720f9f1301595564"); + assertThat(buttons.get(1).getPagePath()).isEqualTo("pages/home/dashboard/index"); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java index bbb8ce83ee..b8d8843624 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java @@ -1,8 +1,13 @@ package me.chanjar.weixin.mp.bean.message; import me.chanjar.weixin.common.api.WxConsts; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; @Test public class WxMpXmlMessageTest { @@ -38,7 +43,7 @@ public void testFromXml() { + " " + "" + "" - + " 1\n" + + " 1" + " " + " " + " " @@ -46,46 +51,66 @@ public void testFromXml() { + " " + "" + "" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" + + " " + + " " + + " " + + " " + + " " + "" + + "" + + "" + + "" + + "" + + "" + + "1" + + "2" + + "" + + "" + + "" + ""; WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml); - Assert.assertEquals(wxMessage.getToUser(), "toUser"); - Assert.assertEquals(wxMessage.getFromUser(), "fromUser"); - Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); - Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); - Assert.assertEquals(wxMessage.getContent(), "this is a test"); - Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); - Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); - Assert.assertEquals(wxMessage.getMediaId(), "media_id"); - Assert.assertEquals(wxMessage.getFormat(), "Format"); - Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); - Assert.assertEquals(wxMessage.getLocationX(), 23.134521d); - Assert.assertEquals(wxMessage.getLocationY(), 113.358803d); - Assert.assertEquals(wxMessage.getScale(), 20d); - Assert.assertEquals(wxMessage.getLabel(), "位置信息"); - Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getUrl(), "url"); - Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getEvent(), "subscribe"); - Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); - Assert.assertEquals(wxMessage.getTicket(), "TICKET"); - Assert.assertEquals(wxMessage.getLatitude(), 23.137466); - Assert.assertEquals(wxMessage.getLongitude(), 113.352425); - Assert.assertEquals(wxMessage.getPrecision(), 119.385040); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); - Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); - Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); + assertEquals(wxMessage.getToUser(), "toUser"); + assertEquals(wxMessage.getFromUser(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getFormat(), "Format"); + assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); + assertEquals(wxMessage.getLocationX().doubleValue(), 23.134521d); + assertEquals(wxMessage.getLocationY().doubleValue(), 113.358803d); + assertEquals(wxMessage.getScale().doubleValue(), 20d); + assertEquals(wxMessage.getLabel(), "位置信息"); + assertEquals(wxMessage.getDescription(), "公众平台官网链接"); + assertEquals(wxMessage.getUrl(), "url"); + assertEquals(wxMessage.getTitle(), "公众平台官网链接"); + assertEquals(wxMessage.getEvent(), "subscribe"); + assertEquals(wxMessage.getEventKey(), "qrscene_123123"); + assertEquals(wxMessage.getTicket(), "TICKET"); + assertEquals(wxMessage.getLatitude().doubleValue(), 23.137466); + assertEquals(wxMessage.getLongitude().doubleValue(), 113.352425); + assertEquals(wxMessage.getPrecision().doubleValue(), 119.385040); + assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); + assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); + assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); + assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); + assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); + assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); + assertEquals(wxMessage.getSendLocationInfo().getPoiName(), "wo de poi"); + assertEquals(wxMessage.getKeyStandard(), "ean13"); + assertEquals(wxMessage.getKeyStr(), "6901481811083"); + assertEquals(wxMessage.getCountry(), "中国"); + assertEquals(wxMessage.getProvince(), "广东"); + assertEquals(wxMessage.getCity(), "揭阳"); + assertEquals(wxMessage.getSex(), "1"); + assertEquals(wxMessage.getScene(), "2"); + assertEquals(wxMessage.getExtInfo(), "123"); + assertEquals(wxMessage.getRegionCode(), "440105"); + assertEquals(wxMessage.getReasonMsg(), ""); } public void testFromXml2() { @@ -135,38 +160,120 @@ public void testFromXml2() { + "" + ""; WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml); - Assert.assertEquals(wxMessage.getToUser(), "toUser"); - Assert.assertEquals(wxMessage.getFromUser(), "fromUser"); - Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); - Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); - Assert.assertEquals(wxMessage.getContent(), "this is a test"); - Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); - Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); - Assert.assertEquals(wxMessage.getMediaId(), "media_id"); - Assert.assertEquals(wxMessage.getFormat(), "Format"); - Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); - Assert.assertEquals(wxMessage.getLocationX(), 23.134521d); - Assert.assertEquals(wxMessage.getLocationY(), 113.358803d); - Assert.assertEquals(wxMessage.getScale(), 20d); - Assert.assertEquals(wxMessage.getLabel(), "位置信息"); - Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getUrl(), "url"); - Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getEvent(), "subscribe"); - Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); - Assert.assertEquals(wxMessage.getTicket(), "TICKET"); - Assert.assertEquals(wxMessage.getLatitude(), 23.137466); - Assert.assertEquals(wxMessage.getLongitude(), 113.352425); - Assert.assertEquals(wxMessage.getPrecision(), 119.385040); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); - Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); - Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); + assertEquals(wxMessage.getToUser(), "toUser"); + assertEquals(wxMessage.getFromUser(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getFormat(), "Format"); + assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); + assertEquals(wxMessage.getLocationX().doubleValue(), 23.134521d); + assertEquals(wxMessage.getLocationY().doubleValue(), 113.358803d); + assertEquals(wxMessage.getScale().doubleValue(), 20d); + assertEquals(wxMessage.getLabel(), "位置信息"); + assertEquals(wxMessage.getDescription(), "公众平台官网链接"); + assertEquals(wxMessage.getUrl(), "url"); + assertEquals(wxMessage.getTitle(), "公众平台官网链接"); + assertEquals(wxMessage.getEvent(), "subscribe"); + assertEquals(wxMessage.getEventKey(), "qrscene_123123"); + assertEquals(wxMessage.getTicket(), "TICKET"); + assertEquals(wxMessage.getLatitude().doubleValue(), 23.137466); + assertEquals(wxMessage.getLongitude().doubleValue(), 113.352425); + assertEquals(wxMessage.getPrecision().doubleValue(), 119.385040); + assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); + assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); + assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); + assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); + assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); + assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); + assertEquals(wxMessage.getSendLocationInfo().getPoiName(), "wo de poi"); } + public void testFromXml_MASSSENDJOBFINISH() { + //xml样例来自 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1481187827_i0l21 + String xml = "\n" + + "\n" + + "\n" + + "1481013459\n" + + "\n" + + "\n" + + "1000001625\n" + + "\n" + + "0\n" + + "0\n" + + "0\n" + + "0\n" + + "\n" + + "2\n" + + "\n" + + "\n" + + "1\n" + + "0\n" + + "2\n" + + "\n" + + "1\n" + + "1\n" + + "1\n" + + "1\n" + + "\n" + + "\n" + + "2\n" + + "0\n" + + "2\n" + + "\n" + + "1\n" + + "1\n" + + "1\n" + + "1\n" + + "\n" + + "\n" + + "2\n" + + "\n" + + ""; + WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUser(), "gh_4d00ed8d6399"); + assertEquals(wxMessage.getFromUser(), "oV5CrjpxgaGXNHIQigzNlgLTnwic"); + assertEquals(wxMessage.getCreateTime(), new Long(1481013459)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), "MASSSENDJOBFINISH"); + assertEquals(wxMessage.getMsgId(), new Long(1000001625L)); + assertEquals(wxMessage.getStatus(), "err(30003)"); + assertEquals(wxMessage.getTotalCount(), new Integer(0)); + assertEquals(wxMessage.getFilterCount(), new Integer(0)); + assertEquals(wxMessage.getSentCount(), new Integer(0)); + assertEquals(wxMessage.getErrorCount(), new Integer(0)); + + final Map allFields = wxMessage.getAllFieldsMap(); + assertThat(allFields).isNotNull(); + final Map copyrightCheckResult = (Map) allFields.get("CopyrightCheckResult"); + List> resultList = (List>) ((Map) copyrightCheckResult + .get("ResultList")).get("item"); + assertThat(copyrightCheckResult).isNotNull(); + + assertThat(copyrightCheckResult.get("Count")).isEqualTo("2"); + assertThat(copyrightCheckResult.get("CheckState")).isEqualTo("2"); + + assertThat(resultList.get(0).get("ArticleIdx")).isEqualTo("1"); + assertThat(resultList.get(0).get("UserDeclareState")).isEqualTo("0"); + assertThat(resultList.get(0).get("AuditState")).isEqualTo("2"); + assertThat(resultList.get(0).get("OriginalArticleUrl")).isEqualTo("Url_1"); + assertThat(resultList.get(0).get("OriginalArticleType")).isEqualTo("1"); + assertThat(resultList.get(0).get("CanReprint")).isEqualTo("1"); + assertThat(resultList.get(0).get("NeedReplaceContent")).isEqualTo("1"); + assertThat(resultList.get(0).get("NeedShowReprintSource")).isEqualTo("1"); + + assertThat(resultList.get(1).get("ArticleIdx")).isEqualTo("2"); + assertThat(resultList.get(1).get("UserDeclareState")).isEqualTo("0"); + assertThat(resultList.get(1).get("AuditState")).isEqualTo("2"); + assertThat(resultList.get(1).get("OriginalArticleUrl")).isEqualTo("Url_2"); + assertThat(resultList.get(1).get("OriginalArticleType")).isEqualTo("1"); + assertThat(resultList.get(1).get("CanReprint")).isEqualTo("1"); + assertThat(resultList.get(1).get("NeedReplaceContent")).isEqualTo("1"); + assertThat(resultList.get(1).get("NeedShowReprintSource")).isEqualTo("1"); + } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessageTest.java index ab732c315c..8aaa3b2879 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessageTest.java @@ -9,7 +9,7 @@ public class WxMpXmlOutImageMessageTest { public void test() { WxMpXmlOutImageMessage m = new WxMpXmlOutImageMessage(); m.setMediaId("ddfefesfsdfef"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("from"); m.setToUserName("to"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessageTest.java index f75e6321e6..592a6a50ed 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessageTest.java @@ -13,7 +13,7 @@ public void test() { m.setHqMusicUrl("hQMusicUrl"); m.setMusicUrl("musicUrl"); m.setThumbMediaId("thumbMediaId"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("fromUser"); m.setToUserName("toUser"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java index c9a1f98264..26eb06a2b8 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java @@ -8,7 +8,7 @@ public class WxMpXmlOutNewsMessageTest { public void test() { WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("fromUser"); m.setToUserName("toUser"); @@ -54,15 +54,13 @@ public void testBuild() { WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS() .fromUser("fromUser") .toUser("toUser") - .addArticle(item) - .addArticle(item) + .addArticle(item,item) .build(); String expected = "" + "" + "" + "1122" + "" - + " 2" + " " + " " + " <![CDATA[title]]>" @@ -77,6 +75,7 @@ public void testBuild() { + " " + " " + " " + + " 2" + ""; System.out.println(m.toXml()); Assert.assertEquals( diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessageTest.java index c005c26107..605f4b2563 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessageTest.java @@ -9,7 +9,7 @@ public class WxMpXmlOutTextMessageTest { public void test() { WxMpXmlOutTextMessage m = new WxMpXmlOutTextMessage(); m.setContent("content"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("from"); m.setToUserName("to"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessageTest.java index 2ba9f549de..a00cdc3282 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessageTest.java @@ -11,7 +11,7 @@ public void test() { m.setMediaId("media_id"); m.setTitle("title"); m.setDescription("ddfff"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("fromUser"); m.setToUserName("toUser"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessageTest.java index d3f1346fec..d426cbe31c 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessageTest.java @@ -9,7 +9,7 @@ public class WxMpXmlOutVoiceMessageTest { public void test() { WxMpXmlOutVoiceMessage m = new WxMpXmlOutVoiceMessage(); m.setMediaId("ddfefesfsdfef"); - m.setCreateTime(1122l); + m.setCreateTime(1122L); m.setFromUserName("from"); m.setToUserName("to"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpAdLeadResultTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpAdLeadResultTest.java new file mode 100644 index 0000000000..faf354f11d --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpAdLeadResultTest.java @@ -0,0 +1,75 @@ +package me.chanjar.weixin.mp.bean.result; + +import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * @author 007 + */ +public class WxMpAdLeadResultTest { + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + "\t\"data\": {\n" + + "\t\t\"page_info\": {\n" + + "\t\t\t\"total_number\": 39,\n" + + "\t\t\t\"page\": 1,\n" + + "\t\t\t\"page_size\": 100,\n" + + "\t\t\t\"total_page\": 1\n" + + "\t\t},\n" + + "\t\t\"list\": [{\n" + + "\t\t\t\"click_id\": \"\",\n" + + "\t\t\t\"adgroup_name\": \"\",\n" + + "\t\t\t\"campaign_id\": 1800000001,\n" + + "\t\t\t\"leads_info\": [{\n" + + "\t\t\t\t\"value\": \"13800138000\",\n" + + "\t\t\t\t\"key\": \"电话号码\"\n" + + "\t\t\t}, {\n" + + "\t\t\t\t\"value\": \"2019-03-14 00:54:34\",\n" + + "\t\t\t\t\"key\": \"提交时间\"\n" + + "\t\t\t}, {\n" + + "\t\t\t\t\"value\": \"123\",\n" + + "\t\t\t\t\"key\": \"自定义问题\"\n" + + "\t\t\t}],\n" + + "\t\t\t\"agency_name\": \"\",\n" + + "\t\t\t\"agency_id\": \"\",\n" + + "\t\t\t\"campaign_name\": \"\",\n" + + "\t\t\t\"adgroup_id\": 1800000002\n" + + "\t\t}, {\n" + + "\t\t\t\"click_id\": \"\",\n" + + "\t\t\t\"adgroup_name\": \"\",\n" + + "\t\t\t\"campaign_id\": 1800000001,\n" + + "\t\t\t\"leads_info\": [{\n" + + "\t\t\t\t\"value\": \"13800138001\",\n" + + "\t\t\t\t\"key\": \"电话号码\"\n" + + "\t\t\t}, {\n" + + "\t\t\t\t\"value\": \"2019-03-14 02:10:39\",\n" + + "\t\t\t\t\"key\": \"提交时间\"\n" + + "\t\t\t}, {\n" + + "\t\t\t\t\"value\": \"321\",\n" + + "\t\t\t\t\"key\": \"自定义问题\"\n" + + "\t\t\t}],\n" + + "\t\t\t\"agency_name\": \"\",\n" + + "\t\t\t\"agency_id\": \"\",\n" + + "\t\t\t\"campaign_name\": \"\",\n" + + "\t\t\t\"adgroup_id\": 1800000002\n" + + "\t\t}]\n" + + "\t},\n" + + "\t\"errcode\": 0,\n" + + "\t\"errmsg\": \"\"\n" + + "}"; + + WxMpAdLeadResult adLeadResult = WxMpAdLeadResult.fromJson(json); + + assertNotNull(adLeadResult); + assertNotNull(adLeadResult.getPageInfo()); + assertNotNull(adLeadResult.getAdLeads()); + assertTrue(adLeadResult.getAdLeads().size() > 0); + + System.out.println(adLeadResult); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfoTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfoTest.java new file mode 100644 index 0000000000..dbed60bac9 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfoTest.java @@ -0,0 +1,157 @@ +package me.chanjar.weixin.mp.bean.result; + +import org.testng.annotations.*; + +import static org.testng.Assert.*; + +/** + *
    + * Created by Binary Wang on 2017-7-8.
    + * @author Binary Wang
    + * 
    + */ +public class WxMpCurrentAutoReplyInfoTest { + @Test + public void testFromJson() throws Exception { + String json = "{ \n" + + " \"is_add_friend_reply_open\": 1, \n" + + " \"is_autoreply_open\": 1, \n" + + " \"add_friend_autoreply_info\": { \n" + + " \"type\": \"text\", \n" + + " \"content\": \"Thanks for your attention!\"\n" + + " }, \n" + + " \"message_default_autoreply_info\": { \n" + + " \"type\": \"text\", \n" + + " \"content\": \"Hello, this is autoreply!\"\n" + + " }, \n" + + " \"keyword_autoreply_info\": { \n" + + " \"list\": [ \n" + + " { \n" + + " \"rule_name\": \"autoreply-news\", \n" + + " \"create_time\": 1423028166, \n" + + " \"reply_mode\": \"reply_all\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"contain\", \n" + + " \"content\": \"news测试\"//此处content即为关键词内容\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"news\", \n" + + " \"news_info\": { \n" + + " \"list\": [ \n" + + " { \n" + + " \"title\": \"it's news\", \n" + + " \"author\": \"jim\", \n" + + " \"digest\": \"it's digest\", \n" + + " \"show_cover\": 1, \"cover_url\": \"http://mmbiz.qpic.cn/mmbiz/GE7et87vE9vicuCibqXsX9GPPLuEtBfXfKbE8sWdt2DDcL0dMfQWJWTVn1N8DxI0gcRmrtqBOuwQH\n" + + " euPKmFLK0ZQ/0\", \n" + + " \"content_url\": \"http://mp.weixin.qq.com/s?__biz=MjM5ODUwNTM3Ng==&mid=203929886&idx=1&sn=628f964cf0c6d84c026881b6959aea8b#rd\", \n" + + " \"source_url\": \"http://www.url.com\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " }, \n" + + " { \n" + + " \"type\": \"news\",\n" + + " \"content\":\"KQb_w_Tiz-nSdVLoTV35Psmty8hGBulGhEdbb9SKs-o\", \n" + + " \"news_info\": { \n" + + " \"list\": [ \n" + + " { \n" + + " \"title\": \"MULTI_NEWS\", \n" + + " \"author\": \"JIMZHENG\", \n" + + " \"digest\": \"text\", \n" + + " \"show_cover\": 0, \n" + + " \"cover_url\": \"http://mmbiz.qpic.cn/mmbiz/GE7et87vE9vicuCibqXsX9GPPLuEtBfXfK0HKuBIa1A1cypS0uY1wickv70iaY1gf3I1DTszuJoS3lAVLv\n" + + "hTcm9sDA/0\", \n" + + " \"content_url\": \"http://mp.weixin.qq.com/s?__biz=MjM5ODUwNTM3Ng==&mid=204013432&idx=1&sn=80ce6d9abcb832237bf86c87e50fda15#rd\", \n" + + " \"source_url\": \"\"\n" + + " },\n" + + " { \n" + + " \"title\": \"MULTI_NEWS4\", \n" + + " \"author\": \"JIMZHENG\", \n" + + " \"digest\": \"MULTI_NEWSMULTI_NEWSMULTI_NEWSMULTI_NEWSMULTI_NEWSMULT\", \n" + + " \"show_cover\": 1, \n" + + "\"cover_url\": \"http://mmbiz.qpic.cn/mmbiz/GE7et87vE9vicuCibqXsX9GPPLuEtBfXfKbE8sWdt2DDcL0dMfQWJWTVn1N8DxI0gcRmrtqBOuwQ\n" + + "HeuPKmFLK0ZQ/0\", \n" + + " \"content_url\": \"http://mp.weixin.qq.com/s?__biz=MjM5ODUwNTM3Ng==&mid=204013432&idx=5&sn=b4ef73a915e7c2265e437096582774af#rd\", \n" + + " \"source_url\": \"\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + " ]\n" + + " }, \n" + + " { \n" + + " \"rule_name\": \"autoreply-voice\", \n" + + " \"create_time\": 1423027971, \n" + + " \"reply_mode\": \"random_one\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"contain\", \n" + + " \"content\": \"voice测试\"\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"voice\", \n" + + " \"content\": \"NESsxgHEvAcg3egJTtYj4uG1PTL6iPhratdWKDLAXYErhN6oEEfMdVyblWtBY5vp\"\n" + + " }\n" + + " ]\n" + + " }, \n" + + " { \n" + + " \"rule_name\": \"autoreply-text\", \n" + + " \"create_time\": 1423027926, \n" + + " \"reply_mode\": \"random_one\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"contain\", \n" + + " \"content\": \"text测试\"\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"content\": \"hello!text!\"\n" + + " }\n" + + " ]\n" + + " }, \n" + + " { \n" + + " \"rule_name\": \"autoreply-video\", \n" + + " \"create_time\": 1423027801, \n" + + " \"reply_mode\": \"random_one\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"equal\", \n" + + " \"content\": \"video测试\"\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"video\", \n" + + "\"content\": \"http://61.182.133.153/vweixinp.tc.qq.com/1007_114bcede9a2244eeb5ab7f76d951df5f.f10.mp4?vkey=7183E5C952B16C3AB1991BA8138673DE1037CB82A29801A504B64A77F691BF9DF7AD054A9B7FE683&sha=0&save=1\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + + WxMpCurrentAutoReplyInfo autoReplyInfo = WxMpCurrentAutoReplyInfo.fromJson(json); + + assertNotNull(autoReplyInfo); + assertTrue(autoReplyInfo.getIsAddFriendReplyOpen()); + assertTrue(autoReplyInfo.getIsAutoReplyOpen()); + assertNotNull(autoReplyInfo.getAddFriendAutoReplyInfo()); + assertNotNull(autoReplyInfo.getMessageDefaultAutoReplyInfo()); + assertTrue(autoReplyInfo.getKeywordAutoReplyInfo().getList().size() > 0); + + System.out.println(autoReplyInfo); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessageTest.java new file mode 100644 index 0000000000..61d3d6fa6f --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessageTest.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.mp.bean.subscribe; + +import org.testng.annotations.*; + +import static org.testng.AssertJUnit.*; + +/** + * @author Mklaus + * @date 2018-01-22 下午1:41 + */ +public class WxMpSubscribeMessageTest { + @Test + public void testToJson() { + String actual = "{" + + "\"touser\":\"OPENID\"," + + "\"template_id\":\"TEMPLATE_ID\"," + + "\"url\":\"URL\"," + + "\"miniprogram\":{" + + "\"appid\":\"xiaochengxuappid12345\"," + + "\"pagepath\":\"index?foo=bar\"" + + "}," + + "\"scene\":\"SCENE\"," + + "\"title\":\"TITLE\"," + + "\"data\":{" + + "\"content\":{" + + "\"value\":\"VALUE\"," + + "\"color\":\"COLOR\"" + + "}" + + "}" + + "}"; + + WxMpSubscribeMessage message = WxMpSubscribeMessage.builder() + .toUser("OPENID") + .templateId("TEMPLATE_ID") + .url("URL") + .miniProgram(new WxMpSubscribeMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar",false)) + .scene("SCENE") + .title("TITLE") + .contentValue("VALUE") + .contentColor("COLOR") + .build(); + + assertEquals(message.toJson(), actual); + + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryTest.java new file mode 100644 index 0000000000..c2ae722977 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryTest.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.template; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2020-02-29 + */ +public class WxMpTemplateIndustryTest { + + @Test + public void testFromJson() { + String json = "{\"primary_industry\":{\"first_class\":\"IT科技\",\"second_class\":\"互联网|电子商务\"}," + + "\"secondary_industry\":{\"first_class\":\"房地产\",\"second_class\":\"房地产|建筑\"}}"; + final WxMpTemplateIndustry industry = WxMpTemplateIndustry.fromJson(json); + assertThat(industry).isNotNull(); + System.out.println(industry); + } + + @Test + public void testFromJson_another_example() { + String json = "{\"primary_industry\":{\"first_class\":\"金融业\",\"second_class\":\"基金理财信托\"}," + + "\"secondary_industry\":{\"first_class\":\"房地产\",\"second_class\":\"建筑\"}}"; + final WxMpTemplateIndustry industry = WxMpTemplateIndustry.fromJson(json); + assertThat(industry).isNotNull(); + System.out.println(industry); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java index ac415ff454..5a3f67fb1d 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java @@ -1,28 +1,29 @@ package me.chanjar.weixin.mp.bean.template; -import org.testng.annotations.*; +import org.testng.annotations.Test; -import static org.testng.AssertJUnit.*; +import static org.testng.AssertJUnit.assertEquals; /** *
      * Created by Binary Wang on 2017-3-30.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ public class WxMpTemplateMessageTest { @Test - public void testToJson() throws Exception { + public void testToJson() { WxMpTemplateMessage tm = WxMpTemplateMessage.builder() .toUser("OPENID") .templateId("ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY") - .miniProgram(new WxMpTemplateMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar")) + .miniProgram(new WxMpTemplateMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar",true)) .url("http://weixin.qq.com/download") .build(); - tm.addWxMpTemplateData( + tm.addData( new WxMpTemplateData("first", "haahah", "#FF00FF")); - tm.addWxMpTemplateData( + tm.addData( new WxMpTemplateData("remark", "heihei", "#FF00FF")); assertEquals(tm.toJson(), "{\"touser\":\"OPENID\",\"template_id\":\"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY\",\"url\":\"http://weixin.qq.com/download\",\"miniprogram\":{\"appid\":\"xiaochengxuappid12345\",\"pagepath\":\"index?foo=bar\"},\"data\":{\"first\":{\"value\":\"haahah\",\"color\":\"#FF00FF\"},\"remark\":{\"value\":\"heihei\",\"color\":\"#FF00FF\"}}}"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoGuessNumberHandler.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoGuessNumberHandler.java index 1743b1f955..0e04972350 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoGuessNumberHandler.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoGuessNumberHandler.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.demo; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSession; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java index 2390071b6c..22b58ccf16 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java @@ -2,7 +2,7 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; @@ -19,7 +19,7 @@ public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map co WxMpService wxMpService, WxSessionManager sessionManager) { try { WxMediaUploadResult wxMediaUploadResult = wxMpService.getMaterialService() - .mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg")); + .mediaUpload(WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg")); WxMpXmlOutImageMessage m = WxMpXmlOutMessage .IMAGE() diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoOAuth2Handler.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoOAuth2Handler.java index 6460f851d0..ce23512e29 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoOAuth2Handler.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoOAuth2Handler.java @@ -17,9 +17,9 @@ public class DemoOAuth2Handler implements WxMpMessageHandler { public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService, WxSessionManager sessionManager) { - String href = "测试oauth2"; + WxConsts.OAuth2Scope.SNSAPI_USERINFO, null) + "\">测试oauth2"; return WxMpXmlOutMessage.TEXT().content(href) .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .build(); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java index e42a192b1f..535123bdd7 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java @@ -1,28 +1,28 @@ package me.chanjar.weixin.mp.demo; +import java.io.InputStream; +import java.util.concurrent.locks.ReentrantLock; + import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; - -import java.io.InputStream; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; /** * @author Daniel Qian */ @XStreamAlias("xml") -class WxMpDemoInMemoryConfigStorage extends WxMpInMemoryConfigStorage { +class WxMpDemoInMemoryConfigStorage extends WxMpDefaultConfigImpl { + private static final long serialVersionUID = -3706236839197109704L; public static WxMpDemoInMemoryConfigStorage fromXml(InputStream is) { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(WxMpDemoInMemoryConfigStorage.class); - return (WxMpDemoInMemoryConfigStorage) xstream.fromXML(is); - } - - @Override - public String toString() { - return "SimpleWxConfigProvider [appId=" + this.appId + ", secret=" + this.secret + ", accessToken=" + this.accessToken - + ", expiresTime=" + this.expiresTime + ", token=" + this.token + ", aesKey=" + this.aesKey + "]"; + WxMpDemoInMemoryConfigStorage wxMpDemoInMemoryConfigStorage = (WxMpDemoInMemoryConfigStorage) xstream.fromXML(is); + wxMpDemoInMemoryConfigStorage.accessTokenLock = new ReentrantLock(); + wxMpDemoInMemoryConfigStorage.cardApiTicketLock = new ReentrantLock(); + wxMpDemoInMemoryConfigStorage.jsapiTicketLock = new ReentrantLock(); + return wxMpDemoInMemoryConfigStorage; } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java index 1c1893762f..36f81ae4cc 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java @@ -1,11 +1,11 @@ package me.chanjar.weixin.mp.demo; import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpMessageRouter; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -27,13 +27,11 @@ public static void main(String[] args) throws Exception { ServletHandler servletHandler = new ServletHandler(); server.setHandler(servletHandler); - ServletHolder endpointServletHolder = new ServletHolder( - new WxMpEndpointServlet(wxMpConfigStorage, wxMpService, - wxMpMessageRouter)); + ServletHolder endpointServletHolder = new ServletHolder(new WxMpEndpointServlet(wxMpConfigStorage, wxMpService, + wxMpMessageRouter)); servletHandler.addServletWithMapping(endpointServletHolder, "/*"); - ServletHolder oauthServletHolder = new ServletHolder( - new WxMpOAuth2Servlet(wxMpService)); + ServletHolder oauthServletHolder = new ServletHolder(new WxMpOAuth2Servlet(wxMpService)); servletHandler.addServletWithMapping(oauthServletHolder, "/oauth2/*"); server.start(); @@ -41,13 +39,11 @@ public static void main(String[] args) throws Exception { } private static void initWeixin() { - try (InputStream is1 = ClassLoader - .getSystemResourceAsStream("test-config.xml")) { - WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage - .fromXml(is1); + try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { + WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1); wxMpConfigStorage = config; - wxMpService = new WxMpServiceImpl(); + wxMpService = new WxMpServiceHttpClientImpl(); wxMpService.setWxMpConfigStorage(config); WxMpMessageHandler logHandler = new DemoLogHandler(); @@ -58,7 +54,7 @@ private static void initWeixin() { wxMpMessageRouter = new WxMpMessageRouter(wxMpService); wxMpMessageRouter.rule().handler(logHandler).next().rule() - .msgType(WxConsts.XML_MSG_TEXT).matcher(guessNumberHandler) + .msgType(WxConsts.XmlMsgType.TEXT).matcher(guessNumberHandler) .handler(guessNumberHandler).end().rule().async(false).content("哈哈") .handler(textHandler).end().rule().async(false).content("图片") .handler(imageHandler).end().rule().async(false).content("oauth") diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java index 3f32d9c94a..e835e6375c 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.demo; -import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpMessageRouter; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java index e739a93689..476a56a656 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java @@ -1,6 +1,6 @@ package me.chanjar.weixin.mp.demo; -import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; import me.chanjar.weixin.mp.bean.result.WxMpUser; @@ -31,15 +31,15 @@ protected void service(HttpServletRequest request, HttpServletResponse response) response.getWriter().println("

    code

    "); response.getWriter().println(code); - WxMpOAuth2AccessToken wxMpOAuth2AccessToken = this.wxMpService.oauth2getAccessToken(code); + WxMpOAuth2AccessToken wxMpOAuth2AccessToken = this.wxMpService.getOAuth2Service().getAccessToken(code); response.getWriter().println("

    access token

    "); response.getWriter().println(wxMpOAuth2AccessToken.toString()); - WxMpUser wxMpUser = this.wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null); + WxMpUser wxMpUser = this.wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null); response.getWriter().println("

    user info

    "); response.getWriter().println(wxMpUser.toString()); - wxMpOAuth2AccessToken = this.wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken()); + wxMpOAuth2AccessToken = this.wxMpService.getOAuth2Service().refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken()); response.getWriter().println("

    after refresh

    "); response.getWriter().println(wxMpOAuth2AccessToken.toString()); diff --git a/weixin-java-mp/src/test/resources/logback-test.xml b/weixin-java-mp/src/test/resources/logback-test.xml index ed59510129..e4a33acd88 100644 --- a/weixin-java-mp/src/test/resources/logback-test.xml +++ b/weixin-java-mp/src/test/resources/logback-test.xml @@ -1,16 +1,13 @@ - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n - - + + - diff --git a/weixin-java-mp/src/test/resources/mm.jpeg b/weixin-java-mp/src/test/resources/mm.jpeg index 183699e96d..e1a0cea70d 100644 Binary files a/weixin-java-mp/src/test/resources/mm.jpeg and b/weixin-java-mp/src/test/resources/mm.jpeg differ diff --git a/weixin-java-mp/src/test/resources/testng.xml b/weixin-java-mp/src/test/resources/testng.xml index a358cf1ceb..ea264e6717 100644 --- a/weixin-java-mp/src/test/resources/testng.xml +++ b/weixin-java-mp/src/test/resources/testng.xml @@ -1,30 +1,30 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/weixin-java-open/README.md b/weixin-java-open/README.md new file mode 100644 index 0000000000..dd69161849 --- /dev/null +++ b/weixin-java-open/README.md @@ -0,0 +1,89 @@ +消息机制未实现,下面为通知回调中设置的代码部分 + +以下代码可通过腾讯全网发布测试用例 + +```Java +@RestController +@RequestMapping("notify") +public class NotifyController extends WechatThridBaseController { + @Autowired + protected WxOpenServiceDemo wxOpenService; + @RequestMapping("receive_ticket") + public Object receiveTicket(@RequestBody(required = false) String requestBody, @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, @RequestParam("signature") String signature, + @RequestParam(name = "encrypt_type", required = false) String encType, + @RequestParam(name = "msg_signature", required = false) String msgSignature) { + this.logger.info( + "\n接收微信请求:[signature=[{}], encType=[{}], msgSignature=[{}]," + + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", + signature, encType, msgSignature, timestamp, nonce, requestBody); + + if (!StringUtils.equalsIgnoreCase("aes", encType) || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { + throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); + } + + // aes加密的消息 + WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody, wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); + this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString()); + String out = null; + try { + out = wxOpenService.getWxOpenComponentService().route(inMessage); + } catch (WxErrorException e) { + throw new ResponseException(ErrorCodeEnum.ERROR, e); + } + + this.logger.debug("\n组装回复信息:{}", out); + + return out; + } + @RequestMapping("{appId}/callback") + public Object callback(@RequestBody(required = false)String requestBody, + @PathVariable ("appId") String appId, + @RequestParam("signature") String signature, + @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + @RequestParam("openid") String openid, + @RequestParam("encrypt_type") String encType, + @RequestParam("msg_signature") String msgSignature) { + this.logger.info( + "\n接收微信请求:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}]," + + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", + appId, openid, signature, encType, msgSignature, timestamp, nonce, requestBody); + logger.info("query:"+getHttpServletRequest().getQueryString()+"\nbody:"+requestBody); + if (!StringUtils.equalsIgnoreCase("aes", encType) || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { + throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); + } + + String out = ""; + // aes加密的消息 + WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(requestBody, wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); + this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString()); + // 全网发布测试用例 + if (StringUtils.equalsAnyIgnoreCase(appId, "wxd101a85aa106f53e", "wx570bc396a51b8ff8")) { + try { + if (StringUtils.equals(inMessage.getMsgType(), "text")) { + if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) { + out = new WxOpenCryptUtil(wxOpenService.getWxOpenConfigStorage()).encrypt( + WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback") + .fromUser(inMessage.getToUser()) + .toUser(inMessage.getFromUser()) + .build() + .toXml() + ); + } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) { + String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api"; + WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build(); + wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); + } + } else if (StringUtils.equals(inMessage.getMsgType(), "event")) { + WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build(); + wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); + } + } catch (WxErrorException e) { + logger.error("callback", e); + } + } + return out; + } +} +``` diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml new file mode 100644 index 0000000000..cf743a4042 --- /dev/null +++ b/weixin-java-open/pom.xml @@ -0,0 +1,141 @@ + + + 4.0.0 + + com.github.binarywang + wx-java + 3.9.0 + + + weixin-java-open + WxJava - Open Java SDK + 微信开放平台 Java SDK + + + + 007 + 007gzs@gmail.com + https://github.com/007gzs + + + + + com.github.binarywang + weixin-java-common + ${project.version} + + + com.github.binarywang + weixin-java-mp + ${project.version} + + + com.github.binarywang + weixin-java-miniapp + ${project.version} + + + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + + + + org.testng + testng + test + + + com.google.inject + guice + test + + + org.eclipse.jetty + jetty-server + test + + + org.eclipse.jetty + jetty-servlet + test + + + joda-time + joda-time + test + + + redis.clients + jedis + + + ch.qos.logback + logback-classic + test + + + org.projectlombok + lombok + + + org.redisson + redisson + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + src/test/resources/testng.xml + + + + + + + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + + diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java new file mode 100644 index 0000000000..68dfb3d60b --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java @@ -0,0 +1,490 @@ +package me.chanjar.weixin.open.api; + +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.open.bean.WxOpenCreateResult; +import me.chanjar.weixin.open.bean.WxOpenGetResult; +import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate; +import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; +import me.chanjar.weixin.open.bean.result.*; + +import java.util.List; + +/** + * . + * + * @author 007 + */ +public interface WxOpenComponentService { + /** + * The constant API_COMPONENT_TOKEN_URL. + */ + String API_COMPONENT_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"; + /** + * The constant API_CREATE_PREAUTHCODE_URL. + */ + String API_CREATE_PREAUTHCODE_URL = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode"; + /** + * The constant API_QUERY_AUTH_URL. + */ + String API_QUERY_AUTH_URL = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth"; + /** + * The constant API_AUTHORIZER_TOKEN_URL. + */ + String API_AUTHORIZER_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token"; + /** + * The constant API_GET_AUTHORIZER_INFO_URL. + */ + String API_GET_AUTHORIZER_INFO_URL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info"; + /** + * The constant API_GET_AUTHORIZER_OPTION_URL. + */ + String API_GET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option"; + /** + * The constant API_SET_AUTHORIZER_OPTION_URL. + */ + String API_SET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_set_authorizer_option"; + /** + * The constant API_GET_AUTHORIZER_LIST. + */ + String API_GET_AUTHORIZER_LIST = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list"; + + /** + * The constant COMPONENT_LOGIN_PAGE_URL. + */ + String COMPONENT_LOGIN_PAGE_URL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=xxx&biz_appid=xxx"; + + /** + * 手机端打开授权链接. + */ + String COMPONENT_MOBILE_LOGIN_PAGE_URL = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&no_scan=1&auth_type=3&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=xxx&biz_appid=xxx#wechat_redirect"; + /** + * The constant CONNECT_OAUTH2_AUTHORIZE_URL. + */ + String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&component_appid=%s#wechat_redirect"; + + /** + * 用code换取oauth2的access token. + */ + String OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/component/access_token?appid=%s&code=%s&grant_type=authorization_code&component_appid=%s"; + /** + * 刷新oauth2的access token. + */ + String OAUTH2_REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/component/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s&component_appid=%s"; + + /** + * The constant MINIAPP_JSCODE_2_SESSION. + */ + String MINIAPP_JSCODE_2_SESSION = "https://api.weixin.qq.com/sns/component/jscode2session?appid=%s&js_code=%s&grant_type=authorization_code&component_appid=%s"; + + /** + * The constant CREATE_OPEN_URL. + */ + String CREATE_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/create"; + + /** + * The constant BIND_OPEN_URL. + */ + String BIND_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/bind"; + + /** + * The constant UNBIND_OPEN_URL. + */ + String UNBIND_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/unbind"; + + /** + * The constant GET_OPEN_URL. + */ + String GET_OPEN_URL = "https://api.weixin.qq.com/cgi-bin/open/get"; + + /** + * 快速创建小程序接口. + */ + String FAST_REGISTER_WEAPP_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=create"; + /** + * The constant FAST_REGISTER_WEAPP_SEARCH_URL. + */ + String FAST_REGISTER_WEAPP_SEARCH_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=search"; + + /** + * 代小程序实现业务. + * 小程序代码模版库管理:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1506504150_nMMh6&token=&lang=zh_CN + * access_token 为 component_access_token + */ + String GET_TEMPLATE_DRAFT_LIST_URL = "https://api.weixin.qq.com/wxa/gettemplatedraftlist"; + /** + * The constant GET_TEMPLATE_LIST_URL. + */ + String GET_TEMPLATE_LIST_URL = "https://api.weixin.qq.com/wxa/gettemplatelist"; + /** + * The constant ADD_TO_TEMPLATE_URL. + */ + String ADD_TO_TEMPLATE_URL = "https://api.weixin.qq.com/wxa/addtotemplate"; + /** + * The constant DELETE_TEMPLATE_URL. + */ + String DELETE_TEMPLATE_URL = "https://api.weixin.qq.com/wxa/deletetemplate"; + + /** + * Gets wx mp service by appid. + * + * @param appid the appid + * @return the wx mp service by appid + */ + WxMpService getWxMpServiceByAppid(String appid); + + /** + * 获取指定appid的开放平台小程序服务(继承一般小程序服务能力). + * + * @param appid . + * @return . wx ma service by appid + */ + WxOpenMaService getWxMaServiceByAppid(String appid); + + /** + * 获取指定appid的快速创建的小程序服务. + * + * @param appid . + * @return . wx fast ma service by appid + */ + WxOpenFastMaService getWxFastMaServiceByAppid(String appid); + + /** + * Gets wx open config storage. + * + * @return the wx open config storage + */ + WxOpenConfigStorage getWxOpenConfigStorage(); + + /** + * Check signature boolean. + * + * @param timestamp the timestamp + * @param nonce the nonce + * @param signature the signature + * @return the boolean + */ + boolean checkSignature(String timestamp, String nonce, String signature); + + /** + * Gets component access token. + * + * @param forceRefresh the force refresh + * @return the component access token + * @throws WxErrorException the wx error exception + */ + String getComponentAccessToken(boolean forceRefresh) throws WxErrorException; + + /** + * Post string. + * + * @param uri the uri + * @param postData the post data + * @return the string + * @throws WxErrorException the wx error exception + */ + String post(String uri, String postData) throws WxErrorException; + + /** + * Post string. + * + * @param uri the uri + * @param postData the post data + * @param accessTokenKey the access token key + * @return the string + * @throws WxErrorException the wx error exception + */ + String post(String uri, String postData, String accessTokenKey) throws WxErrorException; + + /** + * Get string. + * + * @param uri the uri + * @return the string + * @throws WxErrorException the wx error exception + */ + String get(String uri) throws WxErrorException; + + /** + * Get string. + * + * @param uri the uri + * @param accessTokenKey the access token key + * @return the string + * @throws WxErrorException the wx error exception + */ + String get(String uri, String accessTokenKey) throws WxErrorException; + + /** + * 获取用户授权页URL(来路URL和成功跳转URL 的域名都需要为三方平台设置的 登录授权的发起页域名). + * + * @param redirectUri the redirect uri + * @return the pre auth url + * @throws WxErrorException the wx error exception + */ + String getPreAuthUrl(String redirectUri) throws WxErrorException; + + /** + * . + * + * @param redirectUri the redirect uri + * @param authType 要授权的帐号类型:1则商户点击链接后,手机端仅展示公众号、2表示仅展示小程序,3表示公众号和小程序都展示。如果为未指定,则默认小程序和公众号都展示。第三方平台开发者可以使用本字段来控制授权的帐号类型。 + * @param bizAppid 指定授权唯一的小程序或公众号 注:authType、bizAppid 互斥。 + * @return the pre auth url + * @throws WxErrorException the wx error exception + */ + String getPreAuthUrl(String redirectUri, String authType, String bizAppid) throws WxErrorException; + + /** + * 获取预授权链接(手机端预授权). + * + * @param redirectUri . + * @return . mobile pre auth url + * @throws WxErrorException . + */ + String getMobilePreAuthUrl(String redirectUri) throws WxErrorException; + + /** + * 获取预授权链接(手机端预授权). + * + * @param redirectUri . + * @param authType . + * @param bizAppid . + * @return . mobile pre auth url + * @throws WxErrorException . + */ + String getMobilePreAuthUrl(String redirectUri, String authType, String bizAppid) throws WxErrorException; + + /** + * Route string. + * + * @param wxMessage the wx message + * @return the string + * @throws WxErrorException the wx error exception + */ + String route(WxOpenXmlMessage wxMessage) throws WxErrorException; + + /** + * 使用授权码换取公众号或小程序的接口调用凭据和授权信息. + * + * @param authorizationCode the authorization code + * @return the query auth + * @throws WxErrorException the wx error exception + */ + WxOpenQueryAuthResult getQueryAuth(String authorizationCode) throws WxErrorException; + + /** + * 获取授权方的帐号基本信息. + * + * @param authorizerAppid the authorizer appid + * @return the authorizer info + * @throws WxErrorException the wx error exception + */ + WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException; + + /** + * 获取授权方的选项设置信息. + * + * @param authorizerAppid the authorizer appid + * @param optionName the option name + * @return the authorizer option + * @throws WxErrorException the wx error exception + */ + WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException; + + /** + * 获取所有授权方列表. + * + * @param begin the begin + * @param len the len + * @return the authorizer list + * @throws WxErrorException the wx error exception + */ + WxOpenAuthorizerListResult getAuthorizerList(int begin, int len) throws WxErrorException; + + /** + * 设置授权方的选项信息. + * + * @param authorizerAppid the authorizer appid + * @param optionName the option name + * @param optionValue the option value + * @throws WxErrorException the wx error exception + */ + void setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException; + + /** + * Gets authorizer access token. + * + * @param appid the appid + * @param forceRefresh the force refresh + * @return the authorizer access token + * @throws WxErrorException the wx error exception + */ + String getAuthorizerAccessToken(String appid, boolean forceRefresh) throws WxErrorException; + + /** + * Oauth 2 get access token wx mp o auth 2 access token. + * + * @param appid the appid + * @param code the code + * @return the wx mp o auth 2 access token + * @throws WxErrorException the wx error exception + */ + WxMpOAuth2AccessToken oauth2getAccessToken(String appid, String code) throws WxErrorException; + + /** + * Check signature boolean. + * + * @param appId the app id + * @param timestamp the timestamp + * @param nonce the nonce + * @param signature the signature + * @return the boolean + */ + boolean checkSignature(String appId, String timestamp, String nonce, String signature); + + /** + * Oauth 2 refresh access token wx mp o auth 2 access token. + * + * @param appid the appid + * @param refreshToken the refresh token + * @return the wx mp o auth 2 access token + * @throws WxErrorException the wx error exception + */ + WxMpOAuth2AccessToken oauth2refreshAccessToken(String appid, String refreshToken) throws WxErrorException; + + /** + * Oauth 2 build authorization url string. + * + * @param appid the appid + * @param redirectUri the redirect uri + * @param scope the scope + * @param state the state + * @return the string + */ + String oauth2buildAuthorizationUrl(String appid, String redirectUri, String scope, String state); + + /** + * Miniapp jscode 2 session wx ma jscode 2 session result. + * + * @param appId the app id + * @param jsCode the js code + * @return the wx ma jscode 2 session result + * @throws WxErrorException the wx error exception + */ + WxMaJscode2SessionResult miniappJscode2Session(String appId, String jsCode) throws WxErrorException; + + /** + * 获取草稿箱内的所有临时代码草稿. + * + * @return 草稿箱代码模板列表 (draftId) + * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 + */ + List getTemplateDraftList() throws WxErrorException; + + /** + * 获取代码模版库中的所有小程序代码模版. + * + * @return 小程序代码模版列表 (templateId) + * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 + */ + List getTemplateList() throws WxErrorException; + + /** + * 将草稿箱的草稿选为小程序代码模版. + * + * @param draftId 草稿ID,本字段可通过“获取草稿箱内的所有临时代码草稿”接口获得 + * @throws WxErrorException 操作失败时抛出,具体错误码请看此接口的注释文档 + * @see #getTemplateDraftList #getTemplateDraftList + */ + void addToTemplate(long draftId) throws WxErrorException; + + /** + * 删除指定小程序代码模版. + * + * @param templateId 要删除的模版ID + * @throws WxErrorException 操作失败时抛出,具体错误码请看此接口的注释文档 + * @see #getTemplateList #getTemplateList + */ + void deleteTemplate(long templateId) throws WxErrorException; + + /** + * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1498704199_1bcax&token=6df5e3650041eff2cd3ec3662425ad8d7beec8d9&lang=zh_CN + * 创建 开放平台帐号并绑定公众号/小程序. + * https://api.weixin.qq.com/cgi-bin/open/create + * + * @param appId 公众号/小程序的appId + * @param appIdType appId类型 me.chanjar.weixin.common.api.WxConsts.AppIdType mp-公众号 mini-小程序 + * @return . wx open create result + * @throws WxErrorException . + */ + WxOpenCreateResult createOpenAccount(String appId, String appIdType) throws WxErrorException; + + /** + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/bind.html + * 将公众号/小程序绑定到开放平台帐号下 + * + * @param appId 公众号/小程序的appId + * @param appIdType appId类型 me.chanjar.weixin.common.api.WxConsts.AppIdType mp-公众号 mini-小程序 + * @param openAppid 开放平台帐号 appid,由创建开发平台帐号接口返回 + * @return the boolean + * @throws WxErrorException the wx error exception + */ + Boolean bindOpenAccount(String appId, String appIdType, String openAppid) throws WxErrorException; + + /** + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/unbind.html + * 将公众号/小程序从开放平台帐号下解绑 + * + * @param appId 公众号/小程序的appId + * @param appIdType appId类型 me.chanjar.weixin.common.api.WxConsts.AppIdType mp-公众号 mini-小程序 + * @param openAppid 开放平台帐号 appid,由创建开发平台帐号接口返回 + * @return the boolean + * @throws WxErrorException the wx error exception + */ + Boolean unbindOpenAccount(String appId, String appIdType, String openAppid) throws WxErrorException; + + /** + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/get.html + * 获取公众号/小程序所绑定的开放平台帐号 + * + * @param appId 公众号/小程序的appId + * @param appIdType appId类型 me.chanjar.weixin.common.api.WxConsts.AppIdType mp-公众号 mini-小程序 + * @return 开放平台帐号 appid,由创建开发平台帐号接口返回 + * @throws WxErrorException the wx error exception + */ + WxOpenGetResult getOpenAccount(String appId, String appIdType) throws WxErrorException; + + /** + * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN + * 第三方平台快速创建小程序. + * 注意:创建任务逻辑串行,单次任务结束后才可以使用相同信息下发第二次任务,请注意规避任务阻塞 + * + * @param name 企业名(需与工商部门登记信息一致) + * @param code 企业代码 + * @param codeType 企业代码类型 1:统一社会信用代码(18位) 2:组织机构代码(9位xxxxxxxx-x) 3:营业执照注册号(15位) + * @param legalPersonaWechat 法人微信号 + * @param legalPersonaName 法人姓名(绑定银行卡) + * @param componentPhone 第三方联系电话(方便法人与第三方联系) + * @return . wx open result + * @throws WxErrorException . + */ + WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException; + + /** + * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN + * 查询第三方平台快速创建小程序的任务状态 + * 注意:该接口只提供当下任务结果查询,不建议过分依赖该接口查询所创建小程序。 + * 小程序的成功状态可在第三方服务器中自行对账、查询。 + * 不要频繁调用search接口,消息接收需通过服务器查看。调用search接口会消耗接口整体调用quato + * + * @param name 企业名(需与工商部门登记信息一致) + * @param legalPersonaWechat 法人微信号 + * @param legalPersonaName 法人姓名(绑定银行卡) + * @return the wx open result + * @throws WxErrorException . + */ + WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java new file mode 100644 index 0000000000..4b496b30d3 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java @@ -0,0 +1,332 @@ +package me.chanjar.weixin.open.api; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; + +import java.util.concurrent.locks.Lock; + +/** + * The interface Wx open config storage. + * + * @author 007 + */ +public interface WxOpenConfigStorage { + + /** + * Gets component app id. + * + * @return the component app id + */ + String getComponentAppId(); + + /** + * Sets component app id. + * + * @param componentAppId the component app id + */ + void setComponentAppId(String componentAppId); + + /** + * Gets component app secret. + * + * @return the component app secret + */ + String getComponentAppSecret(); + + /** + * Sets component app secret. + * + * @param componentAppSecret the component app secret + */ + void setComponentAppSecret(String componentAppSecret); + + /** + * Gets component token. + * + * @return the component token + */ + String getComponentToken(); + + /** + * Sets component token. + * + * @param componentToken the component token + */ + void setComponentToken(String componentToken); + + /** + * Gets component aes key. + * + * @return the component aes key + */ + String getComponentAesKey(); + + /** + * Sets component aes key. + * + * @param componentAesKey the component aes key + */ + void setComponentAesKey(String componentAesKey); + + /** + * Gets component verify ticket. + * + * @return the component verify ticket + */ + String getComponentVerifyTicket(); + + /** + * Sets component verify ticket. + * + * @param componentVerifyTicket the component verify ticket + */ + void setComponentVerifyTicket(String componentVerifyTicket); + + /** + * Gets component access token. + * + * @return the component access token + */ + String getComponentAccessToken(); + + /** + * Is component access token expired boolean. + * + * @return the boolean + */ + boolean isComponentAccessTokenExpired(); + + /** + * Expire component access token. + */ + void expireComponentAccessToken(); + + /** + * Update component access token. + * + * @param componentAccessToken the component access token + */ + void updateComponentAccessToken(WxOpenComponentAccessToken componentAccessToken); + + /** + * Gets http proxy host. + * + * @return the http proxy host + */ + String getHttpProxyHost(); + + /** + * Gets http proxy port. + * + * @return the http proxy port + */ + int getHttpProxyPort(); + + /** + * Gets http proxy username. + * + * @return the http proxy username + */ + String getHttpProxyUsername(); + + /** + * Gets http proxy password. + * + * @return the http proxy password + */ + String getHttpProxyPassword(); + + /** + * Gets apache http client builder. + * + * @return the apache http client builder + */ + ApacheHttpClientBuilder getApacheHttpClientBuilder(); + + /** + * Gets wx mp config storage. + * + * @param appId the app id + * @return the wx mp config storage + */ + WxMpConfigStorage getWxMpConfigStorage(String appId); + + /** + * Gets wx ma config. + * + * @param appId the app id + * @return the wx ma config + */ + WxMaConfig getWxMaConfig(String appId); + + /** + * Gets component access token lock. + * + * @return the component access token lock + */ + Lock getComponentAccessTokenLock(); + + /** + * Gets lock by key. + * + * @param key the key + * @return the lock by key + */ + Lock getLockByKey(String key); + + /** + * 应该是线程安全的 + * + * @param componentAccessToken 新的accessToken值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds); + + /** + * 是否自动刷新token + * + * @return the boolean + */ + boolean autoRefreshToken(); + + /** + * Gets authorizer refresh token. + * + * @param appId the app id + * @return the authorizer refresh token + */ + String getAuthorizerRefreshToken(String appId); + + /** + * Sets authorizer refresh token. + * + * @param appId the app id + * @param authorizerRefreshToken the authorizer refresh token + */ + void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken); + + /** + * setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) 方法重载方法 + * @param appId the app id + * @param authorizerRefreshToken the authorizer refresh token + */ + void updateAuthorizerRefreshToken(String appId, String authorizerRefreshToken); + + /** + * Gets authorizer access token. + * + * @param appId the app id + * @return the authorizer access token + */ + String getAuthorizerAccessToken(String appId); + + /** + * Is authorizer access token expired boolean. + * + * @param appId the app id + * @return the boolean + */ + boolean isAuthorizerAccessTokenExpired(String appId); + + /** + * 强制将access token过期掉 + * + * @param appId the app id + */ + void expireAuthorizerAccessToken(String appId); + + /** + * 应该是线程安全的 + * + * @param appId the app id + * @param authorizerAccessToken 要更新的WxAccessToken对象 + */ + void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken); + + /** + * 应该是线程安全的 + * + * @param appId the app id + * @param authorizerAccessToken 新的accessToken值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds); + + /** + * Gets jsapi ticket. + * + * @param appId the app id + * @return the jsapi ticket + */ + String getJsapiTicket(String appId); + + /** + * Is jsapi ticket expired boolean. + * + * @param appId the app id + * @return the boolean + */ + boolean isJsapiTicketExpired(String appId); + + /** + * 强制将jsapi ticket过期掉 + * + * @param appId the app id + */ + void expireJsapiTicket(String appId); + + /** + * 应该是线程安全的 + * + * @param appId the app id + * @param jsapiTicket 新的jsapi ticket值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds); + + /** + * Gets card api ticket. + * + * @param appId the app id + * @return the card api ticket + */ + String getCardApiTicket(String appId); + + + /** + * Is card api ticket expired boolean. + * + * @param appId the app id + * @return the boolean + */ + boolean isCardApiTicketExpired(String appId); + + /** + * 强制将卡券api ticket过期掉 + * + * @param appId the app id + */ + void expireCardApiTicket(String appId); + + /** + * 应该是线程安全的 + * + * @param appId the app id + * @param cardApiTicket 新的cardApi ticket值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds); + + /** + * 设置第三方平台基础信息 + * + * @param componentAppId 第三方平台 appid + * @param componentAppSecret 第三方平台 appsecret + * @param componentToken 消息校验Token + * @param componentAesKey 消息加解密Key + */ + void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken, String componentAesKey); +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenFastMaService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenFastMaService.java new file mode 100644 index 0000000000..8710689cdc --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenFastMaService.java @@ -0,0 +1,204 @@ +package me.chanjar.weixin.open.api; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.bean.ma.WxFastMaCategory; +import me.chanjar.weixin.open.bean.result.*; + +import java.util.List; + +/** + *
    + *     微信开放平台【快速创建小程序】的专用接口
    + *     https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21528465979XX32V&token=&lang=zh_CN
    + *    注意:该类的接口仅限通过快速创建小程序接口的小程序使用
    + * 
    + * + * @author Hipple + * @date 2019/01/23 + */ +public interface WxOpenFastMaService extends WxMaService { + /** + * 1 获取帐号基本信息. + */ + String OPEN_GET_ACCOUNT_BASIC_INFO = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"; + + /** + * 2 小程序名称设置及改名. + */ + String OPEN_SET_NICKNAME = "https://api.weixin.qq.com/wxa/setnickname"; + + /** + * 3 小程序改名审核状态查询. + */ + String OPEN_API_WXA_QUERYNICKNAME = "https://api.weixin.qq.com/wxa/api_wxa_querynickname"; + + /** + * 4 微信认证名称检测. + */ + String OPEN_CHECK_WX_VERIFY_NICKNAME = "https://api.weixin.qq.com/cgi-bin/wxverify/checkwxverifynickname"; + + /** + * 5 修改头像. + */ + String OPEN_MODIFY_HEADIMAGE = "https://api.weixin.qq.com/cgi-bin/account/modifyheadimage"; + + /** + * 6修改功能介绍. + */ + String OPEN_MODIFY_SIGNATURE = "https://api.weixin.qq.com/cgi-bin/account/modifysignature"; + + /** + * 7 换绑小程序管理员接口. + */ + String OPEN_COMPONENT_REBIND_ADMIN = "https://api.weixin.qq.com/cgi-bin/account/componentrebindadmin"; + + /** + * 8.1 获取账号可以设置的所有类目 + */ + String OPEN_GET_ALL_CATEGORIES = "https://api.weixin.qq.com/cgi-bin/wxopen/getallcategories"; + /** + * 8.2 添加类目 + */ + String OPEN_ADD_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/addcategory"; + /** + * 8.3 删除类目 + */ + String OPEN_DELETE_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/deletecategory"; + /** + * 8.4 获取账号已经设置的所有类目 + */ + String OPEN_GET_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/getcategory"; + /** + * 8.5 修改类目 + */ + String OPEN_MODIFY_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/modifycategory"; + + + /** + * 1.获取小程序的信息 + * + * @return . + * @throws WxErrorException . + */ + WxFastMaAccountBasicInfoResult getAccountBasicInfo() throws WxErrorException; + + /** + * 2.小程序名称设置及改名 + *
    +   *      若接口未返回audit_id,说明名称已直接设置成功,无需审核;若返回audit_id则名称正在审核中。
    +   *  
    + * + * @param nickname 昵称 + * @param idCard 身份证照片–临时素材mediaid(个人号必填) + * @param license 组织机构代码证或营业执照–临时素材mediaid(组织号必填) + * @param namingOtherStuff1 其他证明材料---临时素材 mediaid + * @param namingOtherStuff2 其他证明材料---临时素材 mediaid + * @return . + * @throws WxErrorException . + */ + WxFastMaSetNickameResult setNickname(String nickname, String idCard, String license, String namingOtherStuff1, + String namingOtherStuff2) throws WxErrorException; + + /** + * 3 小程序改名审核状态查询 + * + * @param auditId 审核单id + * @return . + * @throws WxErrorException . + */ + WxFastMaQueryNicknameStatusResult querySetNicknameStatus(String auditId) throws WxErrorException; + + /** + * 4. 微信认证名称检测 + * + * @param nickname 名称 + * @return . + * @throws WxErrorException . + */ + WxFastMaCheckNickameResult checkWxVerifyNickname(String nickname) throws WxErrorException; + + /** + * 5.修改头像 + *
    +   *     图片格式只支持:BMP、JPEG、JPG、GIF、PNG,大小不超过2M
    +   *      注:实际头像始终为正方形
    +   * 
    + * + * @param headImgMediaId 头像素材media_id + * @param x1 裁剪框左上角x坐标(取值范围:[0, 1]) + * @param y1 裁剪框左上角y坐标(取值范围:[0, 1]) + * @param x2 裁剪框右下角x坐标(取值范围:[0, 1]) + * @param y2 裁剪框右下角y坐标(取值范围:[0, 1]) + * @return . + * @throws WxErrorException . + */ + WxOpenResult modifyHeadImage(String headImgMediaId, float x1, float y1, float x2, float y2) throws WxErrorException; + + /** + * 6.修改功能介绍 + * + * @param signature 简介:4-120字 + * @return . + * @throws WxErrorException . + */ + WxOpenResult modifySignature(String signature) throws WxErrorException; + + /** + * 7.3 管理员换绑 + * + * @param taskId 换绑管理员任务序列号(公众平台最终点击提交回跳到第三方平台时携带) + * @return . + * @throws WxErrorException . + */ + WxOpenResult componentRebindAdmin(String taskId) throws WxErrorException; + + /** + * 8.1 获取账号可以设置的所有类目 + *
    +   *     因为不同类目含有特定字段
    +   *     目前没有完整的类目信息数据
    +   *     为保证兼容性,放弃将response转换为实体
    +   * 
    + * + * @return . + * @throws WxErrorException . + */ + String getAllCategories() throws WxErrorException; + + /** + * 8.2添加类目 + * + * @param categoryList 类目列表 + * @return . + * @throws WxErrorException . + */ + WxOpenResult addCategory(List categoryList) throws WxErrorException; + + /** + * 8.3删除类目 + * + * @param first 一级类目ID + * @param second 二级类目ID + * @return . + * @throws WxErrorException . + */ + WxOpenResult deleteCategory(int first, int second) throws WxErrorException; + + /** + * 8.4获取账号已经设置的所有类目 + * + * @return . + * @throws WxErrorException . + */ + WxFastMaBeenSetCategoryResult getCategory() throws WxErrorException; + + /** + * 8.5修改类目 + * + * @param category 实体 + * @return . + * @throws WxErrorException . + */ + WxOpenResult modifyCategory(WxFastMaCategory category) throws WxErrorException; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java new file mode 100644 index 0000000000..ccaeeff019 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java @@ -0,0 +1,586 @@ +package me.chanjar.weixin.open.api; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo; +import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage; +import me.chanjar.weixin.open.bean.result.*; + +import java.io.File; +import java.util.List; +import java.util.Map; + +/** + *
    + *     微信开放平台代小程序实现服务能力
    + *     https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489144594_DhNoV&token=&lang=zh_CN
    + * 
    + * + * @author yqx + * @date 2018 /9/12 + */ +public interface WxOpenMaService extends WxMaService { + /** + * 设置小程序服务器域名. + * + *
    +   *     授权给第三方的小程序,其服务器域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的服务器域名将被删除,
    +   *     只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加第三方自身的域名。
    +   *     提示:需要先将域名登记到第三方平台的小程序服务器域名中,才可以调用接口进行配置
    +   * 
    + */ + String API_MODIFY_DOMAIN = "https://api.weixin.qq.com/wxa/modify_domain"; + + /** + * 设置小程序业务域名(仅供第三方代小程序调用) + *
    +   *     授权给第三方的小程序,其业务域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的业务域名将被删除,
    +   *     只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加业务域名。
    +   * 提示:
    +   * 1、需要先将域名登记到第三方平台的小程序业务域名中,才可以调用接口进行配置。
    +   * 2、为授权的小程序配置域名时支持配置子域名,例如第三方登记的业务域名如为qq.com,则可以直接将qq.com及其子域名(如xxx.qq.com)也配置到授权的小程序中。
    +   * 
    + */ + String API_SET_WEBVIEW_DOMAIN = "https://api.weixin.qq.com/wxa/setwebviewdomain"; + + /** + * 获取帐号基本信息 + *
    +   * GET请求
    +   * 注意:需要使用1.3环节获取到的新创建小程序appid及authorization_code换取authorizer_refresh_token进而得到authorizer_access_token。
    +   * 
    + */ + String API_GET_ACCOUNT_BASICINFO = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"; + + /** + * 绑定微信用户为小程序体验者 + */ + String API_BIND_TESTER = "https://api.weixin.qq.com/wxa/bind_tester"; + + + /** + * 解除绑定微信用户为小程序体验者 + */ + String API_UNBIND_TESTER = "https://api.weixin.qq.com/wxa/unbind_tester"; + + + /** + * 获取体验者列表 + */ + String API_GET_TESTERLIST = "https://api.weixin.qq.com/wxa/memberauth"; + + /** + * 以下接口基础信息设置 + *

    + * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21517799059ZSEMr&token=6f965b5daf30a98a6bbd2a386faea5c934e929bf&lang=zh_CN + *

    + * 1. 设置小程序隐私设置(是否可被搜索) + */ + String API_CHANGE_WXA_SEARCH_STATUS = "https://api.weixin.qq.com/wxa/changewxasearchstatus"; + + /** + * 2. 查询小程序当前隐私设置(是否可被搜索) + */ + String API_GET_WXA_SEARCH_STATUS = "https://api.weixin.qq.com/wxa/getwxasearchstatus"; + + /** + * 3.1. 获取展示的公众号信息 + */ + String API_GET_SHOW_WXA_ITEM = "https://api.weixin.qq.com/wxa/getshowwxaitem"; + + /** + * 3.2 设置展示的公众号 + */ + String API_UPDATE_SHOW_WXA_ITEM = "https://api.weixin.qq.com/wxa/updateshowwxaitem"; + + + /** + * 以下接口为三方平台代小程序实现的代码管理功能 + *

    + * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=fe774228c66725425675810097f9e48d0737a4bf&lang=zh_CN + *

    + * 1. 为授权的小程序帐号上传小程序代码 + */ + String API_CODE_COMMIT = "https://api.weixin.qq.com/wxa/commit"; + + /** + * 2. 获取体验小程序的体验二维码 + */ + String API_TEST_QRCODE = "https://api.weixin.qq.com/wxa/get_qrcode"; + + /** + * 3. 获取授权小程序帐号的可选类目 + */ + String API_GET_CATEGORY = "https://api.weixin.qq.com/wxa/get_category"; + + /** + * 4. 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) + */ + String API_GET_PAGE = "https://api.weixin.qq.com/wxa/get_page"; + + /** + * 5. 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) + */ + String API_SUBMIT_AUDIT = "https://api.weixin.qq.com/wxa/submit_audit"; + + /** + * 7. 查询某个指定版本的审核状态(仅供第三方代小程序调用) + */ + String API_GET_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_auditstatus"; + + /** + * 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用) + */ + String API_GET_LATEST_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_latest_auditstatus"; + + /** + * 9. 发布已通过审核的小程序(仅供第三方代小程序调用) + */ + String API_RELEASE = "https://api.weixin.qq.com/wxa/release"; + + /** + * 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用) + */ + String API_CHANGE_VISITSTATUS = "https://api.weixin.qq.com/wxa/change_visitstatus"; + + /** + * 11.小程序版本回退(仅供第三方代小程序调用) + */ + String API_REVERT_CODE_RELEASE = "https://api.weixin.qq.com/wxa/revertcoderelease"; + + /** + * 12.查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) + */ + String API_GET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/getweappsupportversion"; + + /** + * 13.设置最低基础库版本(仅供第三方代小程序调用) + */ + String API_SET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/setweappsupportversion"; + + /** + * 14.设置小程序“扫普通链接二维码打开小程序”能力 + *

    + * https://mp.weixin.qq.com/debug/wxadoc/introduction/qrcode.html + * 14.1 增加或修改二维码规则 + */ + String API_QRCODE_JUMP_ADD = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpadd"; + + /** + * 14.2 获取已设置的二维码规则 + */ + String API_QRCODE_JUMP_GET = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpget"; + + /** + * 14.3 获取校验文件名称及内容 + */ + String API_QRCODE_JUMP_DOWNLOAD = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdownload"; + + /** + * 14.4 删除已设置的二维码规则 + */ + String API_QRCODE_JUMP_DELETE = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumpdelete"; + + /** + * 14.5 发布已设置的二维码规则 + */ + String API_QRCODE_JUMP_PUBLISH = "https://api.weixin.qq.com/cgi-bin/wxopen/qrcodejumppublish"; + + /** + * 15.小程序审核撤回 + *

    + * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 + *

    + */ + String API_UNDO_CODE_AUDIT = "https://api.weixin.qq.com/wxa/undocodeaudit"; + + /** + * 16.1 小程序分阶段发布-分阶段发布接口 + */ + String API_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/grayrelease"; + + /** + * 16.2 小程序分阶段发布-取消分阶段发布 + */ + String API_REVERT_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/revertgrayrelease"; + + /** + * 16.3 小程序分阶段发布-查询当前分阶段发布详情 + */ + String API_GET_GRAY_RELEASE_PLAN = "https://api.weixin.qq.com/wxa/getgrayreleaseplan"; + + + /** + * 查询服务商的当月提审限额和加急次数(Quota) + */ + String API_QUERY_QUOTA = "https://api.weixin.qq.com/wxa/queryquota"; + + /** + * 加急审核申请 + */ + String API_SPEED_AUDIT = "https://api.weixin.qq.com/wxa/speedupaudit"; + + /** + * 获得小程序的域名配置信息 + * + * @return the domain + * @throws WxErrorException the wx error exception + */ + WxOpenMaDomainResult getDomain() throws WxErrorException; + + /** + * 修改域名 + * + * @param action delete删除, set覆盖, get获取 + * @param requestDomains the requestdomain list + * @param wsRequestDomains the wsrequestdomain list + * @param uploadDomains the uploaddomain list + * @param downloadDomains the downloaddomain list + * @return the wx open ma domain result + * @throws WxErrorException the wx error exception + */ + WxOpenMaDomainResult modifyDomain(String action, List requestDomains, List wsRequestDomains, + List uploadDomains, List downloadDomains) throws WxErrorException; + + /** + * 获取小程序的业务域名 + * + * @return 直接返回字符串 web view domain + * @throws WxErrorException the wx error exception + */ + String getWebViewDomain() throws WxErrorException; + + /** + * 获取小程序的业务域名 + * + * @return web view domain info + * @throws WxErrorException the wx error exception + */ + WxOpenMaWebDomainResult getWebViewDomainInfo() throws WxErrorException; + + /** + * 设置小程序的业务域名 + * + * @param action add添加, delete删除, set覆盖 + * @param domainList the domain list + * @return 直接返回字符串 web view domain + * @throws WxErrorException the wx error exception + */ + String setWebViewDomain(String action, List domainList) throws WxErrorException; + + /** + * 设置小程序的业务域名 + * + * @param action add添加, delete删除, set覆盖 + * @param domainList the domain list + * @return web view domain info + * @throws WxErrorException the wx error exception + */ + WxOpenMaWebDomainResult setWebViewDomainInfo(String action, List domainList) throws WxErrorException; + + /** + * 获取小程序的信息 + * + * @return the account basic info + * @throws WxErrorException the wx error exception + */ + String getAccountBasicInfo() throws WxErrorException; + + /** + * 绑定小程序体验者 + * + * @param wechatId 体验者微信号(不是openid) + * @return wx open ma bind tester result + * @throws WxErrorException the wx error exception + */ + WxOpenMaBindTesterResult bindTester(String wechatId) throws WxErrorException; + + /** + * 解除绑定小程序体验者 + * + * @param wechatId 体验者微信号(不是openid) + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult unbindTester(String wechatId) throws WxErrorException; + + /** + * 解除绑定小程序体验者,其他平台绑定的体验者无法获取到wechatid,可用此方法解绑,详见文档 + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/unbind_tester.html + * + * @param userStr 人员对应的唯一字符串, 可通过获取已绑定的体验者列表获取人员对应的字符串 + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult unbindTesterByUserStr(String userStr) throws WxErrorException; + + /** + * 获得体验者列表 + * + * @return the tester list + * @throws WxErrorException the wx error exception + */ + WxOpenMaTesterListResult getTesterList() throws WxErrorException; + + /** + * 设置小程序隐私设置(是否可被搜索) + * + * @param status 1表示不可搜索,0表示可搜索 + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult changeWxaSearchStatus(Integer status) throws WxErrorException; + + /** + * 2. 查询小程序当前隐私设置(是否可被搜索) + * + * @return the wxa search status + * @throws WxErrorException the wx error exception + */ + WxOpenMaSearchStatusResult getWxaSearchStatus() throws WxErrorException; + + /** + * 3.1 获取展示的公众号信息 + * + * @return the show wxa item + * @throws WxErrorException the wx error exception + */ + WxOpenMaShowItemResult getShowWxaItem() throws WxErrorException; + + /** + * 3.2 设置展示的公众号 + * + * @param flag 0 关闭,1 开启 + * @param mpAppId 如果开启,需要传新的公众号appid + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult updateShowWxaItem(Integer flag, String mpAppId) throws WxErrorException; + + /** + * 1、为授权的小程序帐号上传小程序代码 + * + * @param templateId 代码模板ID + * @param userVersion 用户定义版本 + * @param userDesc 用户定义版本描述 + * @param extInfo 第三方自定义的配置 + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException; + + /** + * 获取体验小程序的体验二维码 + * + * @param pagePath the page path + * @param params the params + * @return the test qrcode + * @throws WxErrorException the wx error exception + */ + File getTestQrcode(String pagePath, Map params) throws WxErrorException; + + /** + * 获取授权小程序帐号的可选类目 + *

    + * 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。 + *

    + * + * @return the category list + * @throws WxErrorException the wx error exception + */ + WxOpenMaCategoryListResult getCategoryList() throws WxErrorException; + + /** + * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) + * + * @return page list + * @throws WxErrorException the wx error exception + */ + WxOpenMaPageListResult getPageList() throws WxErrorException; + + /** + * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) + * + * @param submitAuditMessage the submit audit message + * @return the wx open ma submit audit result + * @throws WxErrorException the wx error exception + */ + WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException; + + /** + * 查询某个指定版本的审核状态(仅供第三方代小程序调用) + * + * @param auditId the auditid + * @return the audit status + * @throws WxErrorException the wx error exception + */ + WxOpenMaQueryAuditResult getAuditStatus(Long auditId) throws WxErrorException; + + /** + * 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用) + * + * @return 。 + * @throws WxErrorException 。 + */ + WxOpenMaQueryAuditResult getLatestAuditStatus() throws WxErrorException; + + /** + * 9. 发布已通过审核的小程序(仅供第三方代小程序调用) + *

    + * 请填写空的数据包,POST的json数据包为空即可。 + *

    + * + * @return 。 + * @throws WxErrorException 。 + */ + WxOpenResult releaseAudited() throws WxErrorException; + + /** + * 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用) + * + * @param action the action + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult changeVisitStatus(String action) throws WxErrorException; + + /** + * 11. 小程序版本回退(仅供第三方代小程序调用) + * + * @return 。 + * @throws WxErrorException 。 + */ + WxOpenResult revertCodeRelease() throws WxErrorException; + + /** + * 15. 小程序审核撤回 + *

    + * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 + *

    + * + * @return 。 + * @throws WxErrorException 。 + */ + WxOpenResult undoCodeAudit() throws WxErrorException; + + /** + * 12. 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) + * + * @return 。 + * @throws WxErrorException 。 + */ + String getSupportVersion() throws WxErrorException; + + /** + * 12. 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) + * + * @return . support version info + * @throws WxErrorException . + */ + WxOpenMaWeappSupportVersionResult getSupportVersionInfo() throws WxErrorException; + + /** + * 设置最低基础库版本(仅供第三方代小程序调用) + * + * @param version the version + * @return the support version + * @throws WxErrorException the wx error exception + */ + String setSupportVersion(String version) throws WxErrorException; + + /** + * 13. 设置最低基础库版本(仅供第三方代小程序调用) + * + * @param version the version + * @return support version info + * @throws WxErrorException the wx error exception + */ + WxOpenResult setSupportVersionInfo(String version) throws WxErrorException; + + /** + * 16. 小程序分阶段发布 - 1)分阶段发布接口 + * + * @param grayPercentage 灰度的百分比,1到100的整数 + * @return . wx open result + * @throws WxErrorException . + */ + WxOpenResult grayRelease(Integer grayPercentage) throws WxErrorException; + + /** + * 16. 小程序分阶段发布 - 2)取消分阶段发布 + * + * @return . wx open result + * @throws WxErrorException . + */ + WxOpenResult revertGrayRelease() throws WxErrorException; + + /** + * 16. 小程序分阶段发布 - 3)查询当前分阶段发布详情 + * + * @return . gray release plan + * @throws WxErrorException . + */ + WxOpenMaGrayReleasePlanResult getGrayReleasePlan() throws WxErrorException; + + /** + * 查询服务商的当月提审限额和加急次数(Quota) + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/query_quota.html + * + * @return the wx open ma query quota result + * @throws WxErrorException the wx error exception + */ + WxOpenMaQueryQuotaResult queryQuota() throws WxErrorException; + + /** + * 加急审核申请 + * 有加急次数的第三方可以通过该接口,对已经提审的小程序进行加急操作,加急后的小程序预计2-12小时内审完。 + * + * @param auditId the auditid + * @return the boolean + * @throws WxErrorException the wx error exception + */ + Boolean speedAudit(Long auditId) throws WxErrorException; + + /** + * (1)增加或修改二维码规则 + * + * @param wxQrcodeJumpRule the wx qrcode jump rule + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult addQrcodeJump(WxQrcodeJumpRule wxQrcodeJumpRule) throws WxErrorException; + + /** + * (2)获取已设置的二维码规则 + * + * @return the qrcode jump + * @throws WxErrorException the wx error exception + */ + WxGetQrcodeJumpResult getQrcodeJump() throws WxErrorException; + + /** + * (3)获取校验文件名称及内容 + * + * @return the wx downlooad qrcode jump result + * @throws WxErrorException the wx error exception + */ + WxDownlooadQrcodeJumpResult downloadQrcodeJump() throws WxErrorException; + + /** + * (4)删除已设置的二维码规则 + * + * @param prefix the prefix + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult deleteQrcodeJump(String prefix) throws WxErrorException; + + /** + * (5)发布已设置的二维码规则 + * + * @param prefix the prefix + * @return the wx open result + * @throws WxErrorException the wx error exception + */ + WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenService.java new file mode 100644 index 0000000000..c2d00877aa --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenService.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.open.api; + +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * The interface Wx open service. + * + * @author 007 + */ +public interface WxOpenService { + /** + * Gets wx open component service. + * + * @return the wx open component service + */ + WxOpenComponentService getWxOpenComponentService(); + + /** + * Gets wx open config storage. + * + * @return the wx open config storage + */ + WxOpenConfigStorage getWxOpenConfigStorage(); + + /** + * Sets wx open config storage. + * + * @param wxOpenConfigStorage the wx open config storage + */ + void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage); + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 + * + * @param url the url + * @param queryParam the query param + * @return the string + * @throws WxErrorException the wx error exception + */ + String get(String url, String queryParam) throws WxErrorException; + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 + * + * @param url the url + * @param postData the post data + * @return the string + * @throws WxErrorException the wx error exception + */ + String post(String url, String postData) throws WxErrorException; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java new file mode 100644 index 0000000000..52799da57c --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.open.api.impl; + + +import org.apache.commons.lang3.StringUtils; + +/** + * @author yangyidian + * @date 2020/01/09 + **/ +public abstract class AbstractWxOpenInRedisConfigStorage extends WxOpenInMemoryConfigStorage { + protected final static String COMPONENT_VERIFY_TICKET_KEY = "wechat_component_verify_ticket:"; + protected final static String COMPONENT_ACCESS_TOKEN_KEY = "wechat_component_access_token:"; + + protected final static String AUTHORIZER_REFRESH_TOKEN_KEY = "wechat_authorizer_refresh_token:"; + protected final static String AUTHORIZER_ACCESS_TOKEN_KEY = "wechat_authorizer_access_token:"; + + protected final static String LOCK_KEY = "wechat_lock:"; + + protected final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket:"; + protected final static String CARD_API_TICKET_KEY = "wechat_card_api_ticket:"; + + /** + * redis 存储的 key 的前缀,可为空 + */ + protected String keyPrefix; + protected String componentVerifyTicketKey; + protected String componentAccessTokenKey; + protected String authorizerRefreshTokenKey; + protected String authorizerAccessTokenKey; + protected String jsapiTicketKey; + protected String cardApiTicket; + protected String lockKey; + + @Override + public void setComponentAppId(String componentAppId) { + super.setComponentAppId(componentAppId); + String prefix = StringUtils.isBlank(keyPrefix) ? "" : + (StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":")); + componentVerifyTicketKey = prefix + COMPONENT_VERIFY_TICKET_KEY.concat(componentAppId); + componentAccessTokenKey = prefix + COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId); + authorizerRefreshTokenKey = prefix + AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId); + authorizerAccessTokenKey = prefix + AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId); + lockKey = prefix + LOCK_KEY.concat(componentAppId); + jsapiTicketKey = prefix + JSAPI_TICKET_KEY.concat(componentAppId); + cardApiTicket = prefix + CARD_API_TICKET_KEY.concat(componentAppId); + } + + protected String getKey(String prefix, String appId) { + return prefix.endsWith(":") ? prefix.concat(appId) : prefix.concat(":").concat(appId); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java new file mode 100644 index 0000000000..d460152dfa --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java @@ -0,0 +1,560 @@ +package me.chanjar.weixin.open.api.impl; + +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.http.URIUtil; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.open.api.*; +import me.chanjar.weixin.open.bean.*; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; +import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; +import me.chanjar.weixin.open.bean.result.*; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * @author 007 + */ +@Slf4j +@AllArgsConstructor +public class WxOpenComponentServiceImpl implements WxOpenComponentService { + + private static final Map WX_OPEN_MA_SERVICE_MAP = new ConcurrentHashMap<>(); + private static final Map WX_OPEN_MP_SERVICE_MAP = new ConcurrentHashMap<>(); + private static final Map WX_OPEN_FAST_MA_SERVICE_MAP = new ConcurrentHashMap<>(); + + private final WxOpenService wxOpenService; + + @Override + public WxMpService getWxMpServiceByAppid(String appId) { + WxMpService wxMpService = WX_OPEN_MP_SERVICE_MAP.get(appId); + if (wxMpService == null) { + synchronized (WX_OPEN_MP_SERVICE_MAP) { + wxMpService = WX_OPEN_MP_SERVICE_MAP.get(appId); + if (wxMpService == null) { + wxMpService = new WxOpenMpServiceImpl(this, appId, getWxOpenConfigStorage().getWxMpConfigStorage(appId)); + + WX_OPEN_MP_SERVICE_MAP.put(appId, wxMpService); + } + } + } + return wxMpService; + } + + @Override + public WxOpenMaService getWxMaServiceByAppid(String appId) { + WxOpenMaService wxOpenMaService = WX_OPEN_MA_SERVICE_MAP.get(appId); + if (wxOpenMaService == null) { + synchronized (WX_OPEN_MA_SERVICE_MAP) { + wxOpenMaService = WX_OPEN_MA_SERVICE_MAP.get(appId); + if (wxOpenMaService == null) { + wxOpenMaService = new WxOpenMaServiceImpl(this, appId, getWxOpenConfigStorage().getWxMaConfig(appId)); + WX_OPEN_MA_SERVICE_MAP.put(appId, wxOpenMaService); + } + } + } + return wxOpenMaService; + } + + @Override + public WxOpenFastMaService getWxFastMaServiceByAppid(String appId) { + WxOpenFastMaService fastMaService = WX_OPEN_FAST_MA_SERVICE_MAP.get(appId); + if (fastMaService == null) { + synchronized (WX_OPEN_FAST_MA_SERVICE_MAP) { + fastMaService = WX_OPEN_FAST_MA_SERVICE_MAP.get(appId); + if (fastMaService == null) { + fastMaService = new WxOpenFastMaServiceImpl(this, appId, getWxOpenConfigStorage().getWxMaConfig(appId)); + WX_OPEN_FAST_MA_SERVICE_MAP.put(appId, fastMaService); + } + } + } + return fastMaService; + } + + public WxOpenService getWxOpenService() { + return wxOpenService; + } + + @Override + public WxOpenConfigStorage getWxOpenConfigStorage() { + return wxOpenService.getWxOpenConfigStorage(); + } + + @Override + public boolean checkSignature(String timestamp, String nonce, String signature) { + try { + return SHA1.gen(getWxOpenConfigStorage().getComponentToken(), timestamp, nonce) + .equals(signature); + } catch (Exception e) { + this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); + return false; + } + } + + @Override + public String getComponentAccessToken(boolean forceRefresh) throws WxErrorException { + final WxOpenConfigStorage config = this.getWxOpenConfigStorage(); + if (!config.isComponentAccessTokenExpired() && !forceRefresh) { + return config.getComponentAccessToken(); + } + Lock lock = config.getComponentAccessTokenLock(); + lock.lock(); + try { + if (!config.isComponentAccessTokenExpired() && !forceRefresh) { + return config.getComponentAccessToken(); + } + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + jsonObject.addProperty("component_appsecret", getWxOpenConfigStorage().getComponentAppSecret()); + jsonObject.addProperty("component_verify_ticket", getWxOpenConfigStorage().getComponentVerifyTicket()); + + String responseContent = this.getWxOpenService().post(API_COMPONENT_TOKEN_URL, jsonObject.toString()); + WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); + config.updateComponentAccessToken(componentAccessToken); + return config.getComponentAccessToken(); + } finally { + lock.unlock(); + } + } + + @Override + public String post(String uri, String postData) throws WxErrorException { + return post(uri, postData, "component_access_token"); + } + + @Override + public String post(String uri, String postData, String accessTokenKey) throws WxErrorException { + String componentAccessToken = getComponentAccessToken(false); + String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + accessTokenKey + "=" + componentAccessToken; + try { + return getWxOpenService().post(uriWithComponentAccessToken, postData); + } catch (WxErrorException e) { + WxError error = e.getError(); + if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) { + // 强制设置access token过期,这样在下一次请求里就会刷新access token + Lock lock = this.getWxOpenConfigStorage().getComponentAccessTokenLock(); + lock.lock(); + try { + if (StringUtils.equals(componentAccessToken, this.getWxOpenConfigStorage().getComponentAccessToken())) { + this.getWxOpenConfigStorage().expireComponentAccessToken(); + } + } catch (Exception ex) { + this.getWxOpenConfigStorage().expireComponentAccessToken(); + } finally { + lock.unlock(); + } + + if (this.getWxOpenConfigStorage().autoRefreshToken()) { + log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg()); + return this.post(uri, postData, accessTokenKey); + } + } + if (error.getErrorCode() != 0) { + throw new WxErrorException(error, e); + } + return null; + } + } + + @Override + public String get(String uri) throws WxErrorException { + return get(uri, "component_access_token"); + } + + @Override + public String get(String uri, String accessTokenKey) throws WxErrorException { + String componentAccessToken = getComponentAccessToken(false); + String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + accessTokenKey + "=" + componentAccessToken; + try { + return getWxOpenService().get(uriWithComponentAccessToken, null); + } catch (WxErrorException e) { + WxError error = e.getError(); + if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) { + // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token + Lock lock = this.getWxOpenConfigStorage().getComponentAccessTokenLock(); + lock.lock(); + try { + if (StringUtils.equals(componentAccessToken, this.getWxOpenConfigStorage().getComponentAccessToken())) { + this.getWxOpenConfigStorage().expireComponentAccessToken(); + } + } catch (Exception ex) { + this.getWxOpenConfigStorage().expireComponentAccessToken(); + } finally { + lock.unlock(); + } + if (this.getWxOpenConfigStorage().autoRefreshToken()) { + log.warn("即将重新获取新的access_token,错误代码:{},错误信息:{}", error.getErrorCode(), error.getErrorMsg()); + return this.get(uri, accessTokenKey); + } + } + if (error.getErrorCode() != 0) { + throw new WxErrorException(error, e); + } + return null; + } + } + + @Override + public String getPreAuthUrl(String redirectUri) throws WxErrorException { + return getPreAuthUrl(redirectUri, null, null); + } + + @Override + public String getPreAuthUrl(String redirectUri, String authType, String bizAppid) throws WxErrorException { + return createPreAuthUrl(redirectUri, authType, bizAppid, false); + } + + @Override + public String getMobilePreAuthUrl(String redirectUri) throws WxErrorException { + return getMobilePreAuthUrl(redirectUri, null, null); + } + + @Override + public String getMobilePreAuthUrl(String redirectUri, String authType, String bizAppid) throws WxErrorException { + return createPreAuthUrl(redirectUri, authType, bizAppid, true); + } + + /** + * 创建预授权链接 + */ + private String createPreAuthUrl(String redirectUri, String authType, String bizAppid, boolean isMobile) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + String responseContent = post(API_CREATE_PREAUTHCODE_URL, jsonObject.toString()); + jsonObject = WxGsonBuilder.create().fromJson(responseContent, JsonObject.class); + + String preAuthUrlStr = String.format((isMobile ? COMPONENT_MOBILE_LOGIN_PAGE_URL : COMPONENT_LOGIN_PAGE_URL), + getWxOpenConfigStorage().getComponentAppId(), + jsonObject.get("pre_auth_code").getAsString(), + URIUtil.encodeURIComponent(redirectUri)); + if (StringUtils.isNotEmpty(authType)) { + preAuthUrlStr = preAuthUrlStr.replace("&auth_type=xxx", "&auth_type=" + authType); + } else { + preAuthUrlStr = preAuthUrlStr.replace("&auth_type=xxx", ""); + } + if (StringUtils.isNotEmpty(bizAppid)) { + preAuthUrlStr = preAuthUrlStr.replace("&biz_appid=xxx", "&biz_appid=" + bizAppid); + } else { + preAuthUrlStr = preAuthUrlStr.replace("&biz_appid=xxx", ""); + } + return preAuthUrlStr; + } + + + @Override + public String route(final WxOpenXmlMessage wxMessage) throws WxErrorException { + if (wxMessage == null) { + throw new NullPointerException("message is empty"); + } + if (StringUtils.equalsIgnoreCase(wxMessage.getInfoType(), "component_verify_ticket")) { + getWxOpenConfigStorage().setComponentVerifyTicket(wxMessage.getComponentVerifyTicket()); + return "success"; + } + //新增、更新授权 + if (StringUtils.equalsAnyIgnoreCase(wxMessage.getInfoType(), "authorized", "updateauthorized")) { + WxOpenQueryAuthResult queryAuth = wxOpenService.getWxOpenComponentService().getQueryAuth(wxMessage.getAuthorizationCode()); + if (queryAuth == null || queryAuth.getAuthorizationInfo() == null || queryAuth.getAuthorizationInfo().getAuthorizerAppid() == null) { + throw new NullPointerException("getQueryAuth"); + } + return "success"; + } + //快速创建小程序 + if (StringUtils.equalsIgnoreCase(wxMessage.getInfoType(), "notify_third_fasteregister") && wxMessage.getStatus() == 0) { + WxOpenQueryAuthResult queryAuth = wxOpenService.getWxOpenComponentService().getQueryAuth(wxMessage.getAuthCode()); + if (queryAuth == null || queryAuth.getAuthorizationInfo() == null || queryAuth.getAuthorizationInfo().getAuthorizerAppid() == null) { + throw new NullPointerException("getQueryAuth"); + } + return "success"; + } + return ""; + } + + @Override + public WxOpenQueryAuthResult getQueryAuth(String authorizationCode) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + jsonObject.addProperty("authorization_code", authorizationCode); + String responseContent = post(API_QUERY_AUTH_URL, jsonObject.toString()); + WxOpenQueryAuthResult queryAuth = WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenQueryAuthResult.class); + if (queryAuth == null || queryAuth.getAuthorizationInfo() == null) { + return queryAuth; + } + WxOpenAuthorizationInfo authorizationInfo = queryAuth.getAuthorizationInfo(); + if (authorizationInfo.getAuthorizerAccessToken() != null) { + getWxOpenConfigStorage().updateAuthorizerAccessToken(authorizationInfo.getAuthorizerAppid(), + authorizationInfo.getAuthorizerAccessToken(), authorizationInfo.getExpiresIn()); + } + if (authorizationInfo.getAuthorizerRefreshToken() != null) { + getWxOpenConfigStorage().updateAuthorizerRefreshToken(authorizationInfo.getAuthorizerAppid(), authorizationInfo.getAuthorizerRefreshToken()); + } + return queryAuth; + } + + @Override + public WxOpenAuthorizerInfoResult getAuthorizerInfo(String authorizerAppid) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + jsonObject.addProperty("authorizer_appid", authorizerAppid); + String responseContent = post(API_GET_AUTHORIZER_INFO_URL, jsonObject.toString()); + return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerInfoResult.class); + } + + @Override + public WxOpenAuthorizerListResult getAuthorizerList(int begin, int len) throws WxErrorException { + begin = Math.max(begin, 0); + len = len == 0 ? 10 : len; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + jsonObject.addProperty("offset", begin); + jsonObject.addProperty("count", len); + String responseContent = post(API_GET_AUTHORIZER_LIST, jsonObject.toString()); + WxOpenAuthorizerListResult ret = WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerListResult.class); + if (ret != null && ret.getList() != null) { + for (Map data : ret.getList()) { + String authorizerAppid = data.get("authorizer_appid"); + String refreshToken = data.get("refresh_token"); + if (authorizerAppid != null && refreshToken != null) { + this.getWxOpenConfigStorage().updateAuthorizerRefreshToken(authorizerAppid, refreshToken); + } + } + } + return ret; + } + + @Override + public WxOpenAuthorizerOptionResult getAuthorizerOption(String authorizerAppid, String optionName) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + jsonObject.addProperty("authorizer_appid", authorizerAppid); + jsonObject.addProperty("option_name", optionName); + String responseContent = post(API_GET_AUTHORIZER_OPTION_URL, jsonObject.toString()); + return WxOpenGsonBuilder.create().fromJson(responseContent, WxOpenAuthorizerOptionResult.class); + } + + @Override + public void setAuthorizerOption(String authorizerAppid, String optionName, String optionValue) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + jsonObject.addProperty("authorizer_appid", authorizerAppid); + jsonObject.addProperty("option_name", optionName); + jsonObject.addProperty("option_value", optionValue); + post(API_SET_AUTHORIZER_OPTION_URL, jsonObject.toString()); + } + + @Override + public String getAuthorizerAccessToken(String appId, boolean forceRefresh) throws WxErrorException { + WxOpenConfigStorage config = getWxOpenConfigStorage(); + if (!config.isAuthorizerAccessTokenExpired(appId) && !forceRefresh) { + return config.getAuthorizerAccessToken(appId); + } + Lock lock = config.getWxMpConfigStorage(appId).getAccessTokenLock(); + boolean locked = false; + try { + do { + locked = lock.tryLock(100, TimeUnit.MILLISECONDS); + if (!forceRefresh && !config.isAuthorizerAccessTokenExpired(appId)) { + return config.getAuthorizerAccessToken(appId); + } + } while (!locked); + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("component_appid", getWxOpenConfigStorage().getComponentAppId()); + jsonObject.addProperty("authorizer_appid", appId); + jsonObject.addProperty("authorizer_refresh_token", getWxOpenConfigStorage().getAuthorizerRefreshToken(appId)); + String responseContent = post(API_AUTHORIZER_TOKEN_URL, jsonObject.toString()); + + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + config.updateAuthorizerAccessToken(appId, wxOpenAuthorizerAccessToken); + config.updateAuthorizerRefreshToken(appId,wxOpenAuthorizerAccessToken.getAuthorizerRefreshToken()); + return config.getAuthorizerAccessToken(appId); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (locked) { + lock.unlock(); + } + } + } + + @Override + public WxMpOAuth2AccessToken oauth2getAccessToken(String appId, String code) throws WxErrorException { + String url = String.format(OAUTH2_ACCESS_TOKEN_URL, appId, code, getWxOpenConfigStorage().getComponentAppId()); + String responseContent = get(url); + return WxMpOAuth2AccessToken.fromJson(responseContent); + } + + @Override + public boolean checkSignature(String appid, String timestamp, String nonce, String signature) { + return false; + } + + @Override + public WxMpOAuth2AccessToken oauth2refreshAccessToken(String appId, String refreshToken) throws WxErrorException { + String url = String.format(OAUTH2_REFRESH_TOKEN_URL, appId, refreshToken, getWxOpenConfigStorage().getComponentAppId()); + String responseContent = get(url); + return WxMpOAuth2AccessToken.fromJson(responseContent); + } + + @Override + public String oauth2buildAuthorizationUrl(String appId, String redirectURI, String scope, String state) { + return String.format(CONNECT_OAUTH2_AUTHORIZE_URL, + appId, URIUtil.encodeURIComponent(redirectURI), scope, StringUtils.trimToEmpty(state), getWxOpenConfigStorage().getComponentAppId()); + } + + @Override + public WxMaJscode2SessionResult miniappJscode2Session(String appId, String jsCode) throws WxErrorException { + String url = String.format(MINIAPP_JSCODE_2_SESSION, appId, jsCode, getWxOpenConfigStorage().getComponentAppId()); + String responseContent = get(url); + return WxMaJscode2SessionResult.fromJson(responseContent); + } + + @Override + public List getTemplateDraftList() throws WxErrorException { + String responseContent = get(GET_TEMPLATE_DRAFT_LIST_URL, "access_token"); + JsonObject response = GsonParser.parse(StringUtils.defaultString(responseContent, "{}")); + boolean hasDraftList = response.has("draft_list"); + if (hasDraftList) { + return WxOpenGsonBuilder.create().fromJson(response.getAsJsonArray("draft_list"), + new TypeToken>() { + }.getType()); + } else { + return null; + } + } + + @Override + public List getTemplateList() throws WxErrorException { + String responseContent = get(GET_TEMPLATE_LIST_URL, "access_token"); + JsonObject response = GsonParser.parse(StringUtils.defaultString(responseContent, "{}")); + boolean hasTemplateList = response.has("template_list"); + if (hasTemplateList) { + return WxOpenGsonBuilder.create().fromJson(response.getAsJsonArray("template_list"), + new TypeToken>() { + }.getType()); + } else { + return null; + } + } + + @Override + public void addToTemplate(long draftId) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("draft_id", draftId); + post(ADD_TO_TEMPLATE_URL, param.toString(), "access_token"); + } + + @Override + public void deleteTemplate(long templateId) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("template_id", templateId); + post(DELETE_TEMPLATE_URL, param.toString(), "access_token"); + } + + /** + * 微信开放平台帐号管理统一请求入口 + * + * @param appId 操作appId 小程序/公众号 + * @param appIdType 操作类型 小程序/公众号 + * @param requestUrl 请求地址 + * @param param 请求参数 + * @return 请求结果 + * @throws WxErrorException + */ + private String openAccountServicePost(String appId, String appIdType, String requestUrl, JsonObject param) throws WxErrorException { + String result = ""; + switch (appIdType) { + case WxConsts.AppIdType.MP_TYPE: + WxMpService wxMpService = this.getWxMpServiceByAppid(appId); + result = wxMpService.post(requestUrl, param.toString()); + return result; + case WxConsts.AppIdType.MINI_TYPE: + WxOpenMaService maService = this.getWxMaServiceByAppid(appId); + result = maService.post(requestUrl, param.toString()); + return result; + default: + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("appIdType类型异常").build()); + } + } + + @Override + public WxOpenCreateResult createOpenAccount(String appId, String appIdType) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("appid", appId); + + String json = openAccountServicePost(appId, appIdType, CREATE_OPEN_URL, param); + + return WxOpenCreateResult.fromJson(json); + } + + + @Override + public Boolean bindOpenAccount(String appId, String appIdType, String openAppid) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("appid", appId); + param.addProperty("open_appid", openAppid); + + String json = openAccountServicePost(appId, appIdType, BIND_OPEN_URL, param); + return WxOpenResult.fromJson(json).isSuccess(); + } + + + @Override + public Boolean unbindOpenAccount(String appId, String appIdType, String openAppid) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("appid", appId); + param.addProperty("open_appid", openAppid); + + String json = openAccountServicePost(appId, appIdType, UNBIND_OPEN_URL, param); + return WxOpenResult.fromJson(json).isSuccess(); + } + + + @Override + public WxOpenGetResult getOpenAccount(String appId, String appIdType) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("appid", appId); + + String json = openAccountServicePost(appId, appIdType, GET_OPEN_URL, param); + return WxOpenGetResult.fromJson(json); + } + + + @Override + public WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("name", name); + jsonObject.addProperty("code", code); + jsonObject.addProperty("code_type", codeType); + jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat); + jsonObject.addProperty("legal_persona_name", legalPersonaName); + jsonObject.addProperty("component_phone", componentPhone); + String response = post(FAST_REGISTER_WEAPP_URL, jsonObject.toString(), "component_access_token"); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("name", name); + jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat); + jsonObject.addProperty("legal_persona_name", legalPersonaName); + String response = post(FAST_REGISTER_WEAPP_SEARCH_URL, jsonObject.toString(), "component_access_token"); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImpl.java new file mode 100644 index 0000000000..2cea9530bb --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImpl.java @@ -0,0 +1,151 @@ +package me.chanjar.weixin.open.api.impl; + +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.api.WxOpenComponentService; +import me.chanjar.weixin.open.api.WxOpenFastMaService; +import me.chanjar.weixin.open.bean.ma.WxFastMaCategory; +import me.chanjar.weixin.open.bean.result.*; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * . + * + * @author Hipple + * @since 2019/1/23 15:27 + */ +public class WxOpenFastMaServiceImpl extends WxMaServiceImpl implements WxOpenFastMaService { + private final WxOpenComponentService wxOpenComponentService; + private final WxMaConfig wxMaConfig; + private final String appId; + + public WxOpenFastMaServiceImpl(WxOpenComponentService wxOpenComponentService, String appId, WxMaConfig wxMaConfig) { + this.wxOpenComponentService = wxOpenComponentService; + this.appId = appId; + this.wxMaConfig = wxMaConfig; + initHttp(); + } + + @Override + public WxMaConfig getWxMaConfig() { + return wxMaConfig; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + return wxOpenComponentService.getAuthorizerAccessToken(appId, forceRefresh); + } + + @Override + public WxFastMaAccountBasicInfoResult getAccountBasicInfo() throws WxErrorException { + String response = get(OPEN_GET_ACCOUNT_BASIC_INFO, ""); + return WxOpenGsonBuilder.create().fromJson(response, WxFastMaAccountBasicInfoResult.class); + } + + @Override + public WxFastMaSetNickameResult setNickname(String nickname, String idCard, String license, String namingOtherStuff1, String namingOtherStuff2) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("nick_name", nickname); + params.addProperty("id_card", idCard); + params.addProperty("license", license); + params.addProperty("naming_other_stuff_1", namingOtherStuff1); + params.addProperty("naming_other_stuff_2", namingOtherStuff2); + String response = post(OPEN_SET_NICKNAME, GSON.toJson(params)); + return WxOpenGsonBuilder.create().fromJson(response, WxFastMaSetNickameResult.class); + } + + @Override + public WxFastMaQueryNicknameStatusResult querySetNicknameStatus(String auditId) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("audit_id", auditId); + String response = post(OPEN_API_WXA_QUERYNICKNAME, GSON.toJson(params)); + return WxOpenGsonBuilder.create().fromJson(response, WxFastMaQueryNicknameStatusResult.class); + } + + @Override + public WxFastMaCheckNickameResult checkWxVerifyNickname(String nickname) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("nick_name", nickname); + String response = post(OPEN_CHECK_WX_VERIFY_NICKNAME, GSON.toJson(params)); + return WxOpenGsonBuilder.create().fromJson(response, WxFastMaCheckNickameResult.class); + } + + @Override + public WxOpenResult modifyHeadImage(String headImgMediaId, float x1, float y1, float x2, float y2) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("head_img_media_id", headImgMediaId); + params.addProperty("x1", x1); + params.addProperty("y1", y1); + params.addProperty("x2", x2); + params.addProperty("y2", y2); + String response = post(OPEN_MODIFY_HEADIMAGE, GSON.toJson(params)); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult modifySignature(String signature) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("signature", signature); + String response = post(OPEN_MODIFY_SIGNATURE, GSON.toJson(params)); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult componentRebindAdmin(String taskid) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("taskid", taskid); + String response = post(OPEN_COMPONENT_REBIND_ADMIN, GSON.toJson(params)); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public String getAllCategories() throws WxErrorException { + return get(OPEN_GET_ALL_CATEGORIES, ""); + } + + @Override + public WxOpenResult addCategory(List categoryList) throws WxErrorException { + Map map = new HashMap<>(); + map.put("categories", categoryList); + String response = post(OPEN_ADD_CATEGORY, WxOpenGsonBuilder.create().toJson(map)); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult deleteCategory(int first, int second) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("first", first); + params.addProperty("second", second); + String response = post(OPEN_DELETE_CATEGORY, GSON.toJson(params)); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxFastMaBeenSetCategoryResult getCategory() throws WxErrorException { + String response = get(OPEN_GET_CATEGORY, ""); + return WxOpenGsonBuilder.create().fromJson(response, WxFastMaBeenSetCategoryResult.class); + } + + @Override + public WxOpenResult modifyCategory(WxFastMaCategory category) throws WxErrorException { + String response = post(OPEN_MODIFY_CATEGORY, GSON.toJson(category)); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + private JsonArray toJsonArray(List strList) { + JsonArray jsonArray = new JsonArray(); + if (strList != null && !strList.isEmpty()) { + for (String str : strList) { + jsonArray.add(str); + } + } + return jsonArray; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java new file mode 100644 index 0000000000..13f4b48a42 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java @@ -0,0 +1,540 @@ +package me.chanjar.weixin.open.api.impl; + + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import lombok.Data; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.enums.TicketType; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.mp.bean.WxMpHostConfig; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 + * + * @author 007 + */ +@Data +public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage { + private String componentAppId; + private String componentAppSecret; + private String componentToken; + private String componentAesKey; + private String componentVerifyTicket; + private String componentAccessToken; + private long componentExpiresTime; + + private String httpProxyHost; + private int httpProxyPort; + private String httpProxyUsername; + private String httpProxyPassword; + private ApacheHttpClientBuilder apacheHttpClientBuilder; + + private Map authorizerRefreshTokens = new ConcurrentHashMap<>(); + private Map authorizerAccessTokens = new ConcurrentHashMap<>(); + private Map jsapiTickets = new ConcurrentHashMap<>(); + private Map cardApiTickets = new ConcurrentHashMap<>(); + private Map locks = new ConcurrentHashMap<>(); + + @Override + public boolean isComponentAccessTokenExpired() { + return System.currentTimeMillis() > componentExpiresTime; + } + + @Override + public void expireComponentAccessToken() { + this.componentExpiresTime = 0L; + } + + @Override + public void updateComponentAccessToken(WxOpenComponentAccessToken componentAccessToken) { + updateComponentAccessToken(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn()); + } + + private Lock accessTokenLockInstance; + + @Override + public Lock getComponentAccessTokenLock() { + if (this.accessTokenLockInstance == null) { + synchronized (this) { + if (this.accessTokenLockInstance == null) { + this.accessTokenLockInstance = getLockByKey("componentAccessTokenLock"); + } + } + } + return this.accessTokenLockInstance; + } + + @Override + public Lock getLockByKey(String key) { + Lock lock = locks.get(key); + if (lock == null) { + synchronized (this) { + lock = locks.get(key); + if (lock == null) { + lock = new ReentrantLock(); + locks.put(key, lock); + } + } + } + return lock; + } + + @Override + public WxMpConfigStorage getWxMpConfigStorage(String appId) { + return new WxOpenInnerConfigStorage(this, appId); + } + + @Override + public WxMaConfig getWxMaConfig(String appId) { + return new WxOpenInnerConfigStorage(this, appId); + } + + @Override + public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) { + this.componentAccessToken = componentAccessToken; + this.componentExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + + @Override + public void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken, + String componentAesKey) { + setComponentAppId(componentAppId); + setComponentAppSecret(componentAppSecret); + setComponentToken(componentToken); + setComponentAesKey(componentAesKey); + } + + @Override + public boolean autoRefreshToken() { + return true; + } + + private String getTokenString(Map map, String key) { + Token token = map.get(key); + if (token == null || (token.expiresTime != null && System.currentTimeMillis() > token.expiresTime)) { + return null; + } + return token.token; + } + + private void expireToken(Map map, String key) { + Token token = map.get(key); + if (token != null) { + token.expiresTime = 0L; + } + } + + private void updateToken(Map map, String key, String tokenString, Integer expiresInSeconds) { + Token token = map.get(key); + if (token == null) { + token = new Token(); + map.put(key, token); + } + token.token = tokenString; + if (expiresInSeconds != null && expiresInSeconds != -1) { + token.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + } + + @Override + public String getAuthorizerRefreshToken(String appId) { + return getTokenString(authorizerRefreshTokens, appId); + } + + @Override + public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) { + updateToken(authorizerRefreshTokens, appId, authorizerRefreshToken, null); + } + + @Override + public void updateAuthorizerRefreshToken(String appId, String authorizerRefreshToken) { + this.setAuthorizerRefreshToken(appId, authorizerRefreshToken); + } + + @Override + public String getAuthorizerAccessToken(String appId) { + return getTokenString(authorizerAccessTokens, appId); + } + + + @Override + public boolean isAuthorizerAccessTokenExpired(String appId) { + return getTokenString(authorizerAccessTokens, appId) == null; + } + + @Override + public void expireAuthorizerAccessToken(String appId) { + expireToken(authorizerAccessTokens, appId); + } + + @Override + public void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) { + updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(), + authorizerAccessToken.getExpiresIn()); + } + + @Override + public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) { + updateToken(authorizerAccessTokens, appId, authorizerAccessToken, expiresInSeconds); + } + + @Override + public String getJsapiTicket(String appId) { + return getTokenString(jsapiTickets, appId); + } + + @Override + public boolean isJsapiTicketExpired(String appId) { + return getTokenString(jsapiTickets, appId) == null; + } + + @Override + public void expireJsapiTicket(String appId) { + expireToken(jsapiTickets, appId); + } + + @Override + public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) { + updateToken(jsapiTickets, appId, jsapiTicket, expiresInSeconds); + } + + @Override + public String getCardApiTicket(String appId) { + return getTokenString(cardApiTickets, appId); + } + + @Override + public boolean isCardApiTicketExpired(String appId) { + return getTokenString(cardApiTickets, appId) == null; + } + + @Override + public void expireCardApiTicket(String appId) { + expireToken(cardApiTickets, appId); + } + + @Override + public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) { + updateToken(cardApiTickets, appId, cardApiTicket, expiresInSeconds); + } + + private static class Token { + private String token; + private Long expiresTime; + } + + private static class WxOpenInnerConfigStorage implements WxMpConfigStorage, WxMaConfig { + private final WxOpenConfigStorage wxOpenConfigStorage; + private final String appId; + private WxMpHostConfig hostConfig; + + /** + * 小程序原始ID + */ + private volatile String originalId; + /** + * 云环境ID + */ + private volatile String cloudEnv; + private final Lock accessTokenLock; + private final Lock jsapiTicketLock; + private final Lock cardApiTicketLock; + + private WxOpenInnerConfigStorage(WxOpenConfigStorage wxOpenConfigStorage, String appId) { + this.wxOpenConfigStorage = wxOpenConfigStorage; + this.appId = appId; + this.accessTokenLock = wxOpenConfigStorage.getLockByKey(appId + ":accessTokenLock"); + this.jsapiTicketLock = wxOpenConfigStorage.getLockByKey(appId + ":jsapiTicketLock"); + this.cardApiTicketLock = wxOpenConfigStorage.getLockByKey(appId + ":cardApiTicketLock"); + } + + @Override + public String getAccessToken() { + return wxOpenConfigStorage.getAuthorizerAccessToken(appId); + } + + @Override + public Lock getAccessTokenLock() { + return this.accessTokenLock; + } + + @Override + public boolean isAccessTokenExpired() { + return wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appId); + } + + @Override + public synchronized void updateAccessToken(WxAccessToken accessToken) { + updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + wxOpenConfigStorage.updateAuthorizerAccessToken(appId, accessToken, expiresInSeconds); + } + + @Override + public String getTicket(TicketType type) { + switch (type) { + case JSAPI: { + return wxOpenConfigStorage.getJsapiTicket(appId); + } + case WX_CARD: { + return wxOpenConfigStorage.getCardApiTicket(appId); + } + default: { + // do nothing + } + } + return null; + } + + @Override + public Lock getTicketLock(TicketType type) { + switch (type) { + case JSAPI: { + return this.jsapiTicketLock; + } + case WX_CARD: { + return this.cardApiTicketLock; + } + default: { + // do nothing + } + } + return null; + } + + @Override + public boolean isTicketExpired(TicketType type) { + switch (type) { + case JSAPI: { + return wxOpenConfigStorage.isJsapiTicketExpired(appId); + } + case WX_CARD: { + return wxOpenConfigStorage.isCardApiTicketExpired(appId); + } + default: { + // do nothing + } + } + + return false; + } + + @Override + public void expireTicket(TicketType type) { + switch (type) { + case JSAPI: { + wxOpenConfigStorage.expireJsapiTicket(appId); + break; + } + case WX_CARD: { + wxOpenConfigStorage.expireCardApiTicket(appId); + break; + } + default: { + // do nothing + } + } + } + + @Override + public void updateTicket(TicketType type, String ticket, int expiresInSeconds) { + switch (type) { + case JSAPI: { + wxOpenConfigStorage.updateJsapiTicket(appId, ticket, expiresInSeconds); + break; + } + case WX_CARD: { + wxOpenConfigStorage.updateCardApiTicket(appId, ticket, expiresInSeconds); + break; + } + default: { + // do nothing + } + } + + } + + @Override + public String getAppid() { + return this.appId; + } + + @Override + public String getOriginalId() { + return originalId; + } + + public void setOriginalId(String originalId) { + this.originalId = originalId; + } + + @Override + public String getCloudEnv() { + return this.cloudEnv; + } + + public void setCloudEnv(String cloudEnv) { + this.cloudEnv = cloudEnv; + } + + @Override + public void expireAccessToken() { + wxOpenConfigStorage.expireAuthorizerAccessToken(appId); + } + + @Override + public String getJsapiTicket() { + return wxOpenConfigStorage.getJsapiTicket(appId); + } + + @Override + public Lock getJsapiTicketLock() { + return this.jsapiTicketLock; + } + + @Override + public boolean isJsapiTicketExpired() { + return wxOpenConfigStorage.isJsapiTicketExpired(appId); + } + + @Override + public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + wxOpenConfigStorage.updateJsapiTicket(appId, jsapiTicket, expiresInSeconds); + } + + @Override + public void expireJsapiTicket() { + wxOpenConfigStorage.expireJsapiTicket(appId); + } + + @Override + public String getCardApiTicket() { + return wxOpenConfigStorage.getCardApiTicket(appId); + } + + @Override + public Lock getCardApiTicketLock() { + return this.cardApiTicketLock; + } + + @Override + public boolean isCardApiTicketExpired() { + return wxOpenConfigStorage.isCardApiTicketExpired(appId); + } + + @Override + public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + wxOpenConfigStorage.updateCardApiTicket(appId, cardApiTicket, expiresInSeconds); + } + + @Override + public void expireCardApiTicket() { + wxOpenConfigStorage.expireCardApiTicket(appId); + } + + @Override + public String getAppId() { + return this.appId; + } + + @Override + public String getSecret() { + return null; + } + + @Override + public String getToken() { + return wxOpenConfigStorage.getComponentToken(); + } + + @Override + public String getTemplateId() { + return null; + } + + @Override + public long getExpiresTime() { + return 0; + } + + + @Override + public String getAesKey() { + return wxOpenConfigStorage.getComponentAesKey(); + } + + @Override + public String getMsgDataFormat() { + return null; + } + + @Override + public String getOauth2redirectUri() { + return null; + } + + @Override + public String getHttpProxyHost() { + return this.wxOpenConfigStorage.getHttpProxyHost(); + } + + @Override + public int getHttpProxyPort() { + return this.wxOpenConfigStorage.getHttpProxyPort(); + } + + @Override + public String getHttpProxyUsername() { + return this.wxOpenConfigStorage.getHttpProxyUsername(); + } + + @Override + public String getHttpProxyPassword() { + return this.wxOpenConfigStorage.getHttpProxyPassword(); + } + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + + @Override + public File getTmpDirFile() { + return null; + } + + @Override + public ApacheHttpClientBuilder getApacheHttpClientBuilder() { + return wxOpenConfigStorage.getApacheHttpClientBuilder(); + } + + @Override + public boolean autoRefreshToken() { + return wxOpenConfigStorage.autoRefreshToken(); + } + + @Override + public WxMpHostConfig getHostConfig() { + return this.hostConfig; + } + + @Override + public void setHostConfig(WxMpHostConfig hostConfig) { + this.hostConfig = hostConfig; + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java new file mode 100644 index 0000000000..9ce7851065 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java @@ -0,0 +1,140 @@ +package me.chanjar.weixin.open.api.impl; + +import lombok.NonNull; +import me.chanjar.weixin.common.redis.JedisWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.util.Pool; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * @author 007 + */ +public class WxOpenInRedisConfigStorage extends AbstractWxOpenInRedisConfigStorage { + + private final WxRedisOps redisOps; + + public WxOpenInRedisConfigStorage(Pool jedisPool) { + this(jedisPool, null); + } + + public WxOpenInRedisConfigStorage(@NonNull Pool jedisPool, String keyPrefix) { + this(new JedisWxRedisOps(jedisPool), keyPrefix); + } + + public WxOpenInRedisConfigStorage(@NonNull WxRedisOps redisOps, String keyPrefix) { + this.redisOps = redisOps; + this.keyPrefix = keyPrefix; + } + + @Override + public String getComponentVerifyTicket() { + return redisOps.getValue(this.componentVerifyTicketKey); + } + + @Override + public void setComponentVerifyTicket(String componentVerifyTicket) { + redisOps.setValue(this.componentVerifyTicketKey, componentVerifyTicket, Integer.MAX_VALUE, TimeUnit.SECONDS); + } + + @Override + public String getComponentAccessToken() { + return redisOps.getValue(this.componentAccessTokenKey); + } + + @Override + public boolean isComponentAccessTokenExpired() { + Long expire = redisOps.getExpire(this.componentAccessTokenKey); + return expire == null || expire < 2; + } + + @Override + public void expireComponentAccessToken() { + redisOps.expire(this.componentAccessTokenKey, 0, TimeUnit.SECONDS); + } + + @Override + public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) { + redisOps.setValue(this.componentAccessTokenKey, componentAccessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getAuthorizerRefreshToken(String appId) { + return redisOps.getValue(this.getKey(this.authorizerRefreshTokenKey, appId)); + } + + @Override + public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) { + redisOps.setValue(this.getKey(this.authorizerRefreshTokenKey, appId), authorizerRefreshToken, 0, TimeUnit.SECONDS); + } + + @Override + public String getAuthorizerAccessToken(String appId) { + return redisOps.getValue(this.getKey(this.authorizerAccessTokenKey, appId)); + } + + @Override + public boolean isAuthorizerAccessTokenExpired(String appId) { + Long expire = redisOps.getExpire(this.getKey(this.authorizerAccessTokenKey, appId)); + return expire == null || expire < 2; + } + + @Override + public void expireAuthorizerAccessToken(String appId) { + redisOps.expire(this.getKey(this.authorizerAccessTokenKey, appId), 0, TimeUnit.SECONDS); + } + + @Override + public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) { + redisOps.setValue(this.getKey(this.authorizerAccessTokenKey, appId), authorizerAccessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getJsapiTicket(String appId) { + return redisOps.getValue(this.getKey(this.jsapiTicketKey, appId)); + } + + @Override + public boolean isJsapiTicketExpired(String appId) { + Long expire = redisOps.getExpire(this.getKey(this.jsapiTicketKey, appId)); + return expire == null || expire < 2; + } + + @Override + public void expireJsapiTicket(String appId) { + redisOps.expire(this.getKey(this.jsapiTicketKey, appId), 0, TimeUnit.SECONDS); + } + + @Override + public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) { + redisOps.setValue(this.getKey(this.jsapiTicketKey, appId), jsapiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getCardApiTicket(String appId) { + return redisOps.getValue(this.getKey(this.cardApiTicket, appId)); + } + + @Override + public boolean isCardApiTicketExpired(String appId) { + Long expire = redisOps.getExpire(this.getKey(this.cardApiTicket, appId)); + return expire == null || expire < 2; + } + + @Override + public void expireCardApiTicket(String appId) { + redisOps.expire(this.getKey(this.cardApiTicket, appId), 0, TimeUnit.SECONDS); + } + + @Override + public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) { + redisOps.setValue(this.getKey(this.cardApiTicket, appId), cardApiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public Lock getLockByKey(String key) { + return redisOps.getLock(key); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorage.java new file mode 100644 index 0000000000..070d9ebf88 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorage.java @@ -0,0 +1,140 @@ +package me.chanjar.weixin.open.api.impl; + +import lombok.NonNull; +import me.chanjar.weixin.common.redis.RedissonWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * @author yangyidian + * @date 2020/01/06 + **/ +public class WxOpenInRedissonConfigStorage extends AbstractWxOpenInRedisConfigStorage { + + private final WxRedisOps redisOps; + + public WxOpenInRedissonConfigStorage(@NonNull RedissonClient redissonClient, String keyPrefix) { + this(new RedissonWxRedisOps(redissonClient), keyPrefix); + } + + public WxOpenInRedissonConfigStorage(@NonNull RedissonClient redissonClient) { + this(redissonClient, null); + } + + private WxOpenInRedissonConfigStorage(@NonNull WxRedisOps redisOps, String keyPrefix) { + this.redisOps = redisOps; + this.keyPrefix = keyPrefix; + } + + @Override + public String getComponentVerifyTicket() { + return redisOps.getValue(this.componentVerifyTicketKey); + } + + @Override + public void setComponentVerifyTicket(String componentVerifyTicket) { + redisOps.setValue(this.componentVerifyTicketKey, componentVerifyTicket, Integer.MAX_VALUE, TimeUnit.SECONDS); + } + + @Override + public String getComponentAccessToken() { + return redisOps.getValue(this.componentAccessTokenKey); + } + + @Override + public boolean isComponentAccessTokenExpired() { + Long expire = redisOps.getExpire(this.componentAccessTokenKey); + return expire == null || expire < 2; + } + + @Override + public void expireComponentAccessToken() { + redisOps.expire(this.componentAccessTokenKey, 0, TimeUnit.SECONDS); + } + + @Override + public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) { + redisOps.setValue(this.componentAccessTokenKey, componentAccessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getAuthorizerRefreshToken(String appId) { + return redisOps.getValue(this.getKey(this.authorizerRefreshTokenKey, appId)); + } + + @Override + public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) { + redisOps.setValue(this.getKey(this.authorizerRefreshTokenKey, appId), authorizerRefreshToken, 0, TimeUnit.SECONDS); + } + + @Override + public String getAuthorizerAccessToken(String appId) { + return redisOps.getValue(this.getKey(this.authorizerAccessTokenKey, appId)); + } + + @Override + public boolean isAuthorizerAccessTokenExpired(String appId) { + Long expire = redisOps.getExpire(this.getKey(this.authorizerAccessTokenKey, appId)); + return expire == null || expire < 2; + } + + @Override + public void expireAuthorizerAccessToken(String appId) { + redisOps.expire(this.getKey(this.authorizerAccessTokenKey, appId), 0, TimeUnit.SECONDS); + } + + @Override + public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) { + redisOps.setValue(this.getKey(this.authorizerAccessTokenKey, appId), authorizerAccessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getJsapiTicket(String appId) { + return redisOps.getValue(this.getKey(this.jsapiTicketKey, appId)); + } + + @Override + public boolean isJsapiTicketExpired(String appId) { + Long expire = redisOps.getExpire(this.getKey(this.jsapiTicketKey, appId)); + return expire == null || expire < 2; + } + + @Override + public void expireJsapiTicket(String appId) { + redisOps.expire(this.getKey(this.jsapiTicketKey, appId), 0, TimeUnit.SECONDS); + } + + @Override + public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) { + redisOps.setValue(this.getKey(this.jsapiTicketKey, appId), jsapiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getCardApiTicket(String appId) { + return redisOps.getValue(this.getKey(this.cardApiTicket, appId)); + } + + @Override + public boolean isCardApiTicketExpired(String appId) { + Long expire = redisOps.getExpire(this.getKey(this.cardApiTicket, appId)); + return expire == null || expire < 2; + } + + @Override + public void expireCardApiTicket(String appId) { + redisOps.expire(this.getKey(this.cardApiTicket, appId), 0, TimeUnit.SECONDS); + } + + @Override + public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) { + redisOps.setValue(this.getKey(this.cardApiTicket, appId), cardApiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public Lock getLockByKey(String key) { + return redisOps.getLock(key); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java new file mode 100644 index 0000000000..a8c252bae0 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java @@ -0,0 +1,360 @@ +package me.chanjar.weixin.open.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.api.WxOpenComponentService; +import me.chanjar.weixin.open.api.WxOpenMaService; +import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo; +import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; +import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage; +import me.chanjar.weixin.open.bean.result.*; +import me.chanjar.weixin.open.executor.MaQrCodeRequestExecutor; + +import java.io.File; +import java.util.List; +import java.util.Map; + +/** + *
    + *     增加开放平台代小程序管理服务能力
    + *     说明:这里让这个服务公开便于调用者模拟本地测试服务
    + * 
    + * + * @author 007 + * @author yqx + * @date 2018-09-12 + */ +public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaService { + private final WxOpenComponentService wxOpenComponentService; + private final WxMaConfig wxMaConfig; + private final String appId; + + public WxOpenMaServiceImpl(WxOpenComponentService wxOpenComponentService, String appId, WxMaConfig wxMaConfig) { + this.wxOpenComponentService = wxOpenComponentService; + this.appId = appId; + this.wxMaConfig = wxMaConfig; + initHttp(); + } + + @Override + public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { + return wxOpenComponentService.miniappJscode2Session(appId, jsCode); + } + + @Override + public WxMaConfig getWxMaConfig() { + return wxMaConfig; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + return wxOpenComponentService.getAuthorizerAccessToken(appId, forceRefresh); + } + + @Override + public WxOpenMaDomainResult getDomain() throws WxErrorException { + return modifyDomain("get", null, null, null, null); + } + + @Override + public WxOpenMaDomainResult modifyDomain(String action, List requestDomains, List wsRequestDomains, List uploadDomains, List downloadDomains) throws WxErrorException { +// if (!"get".equals(action) && (requestdomainList == null || wsrequestdomainList == null || uploaddomainList == null || downloaddomainList == null)) { +// throw new WxErrorException(WxError.builder().errorCode(44004).errorMsg("域名参数不能为空").build()); +// } + JsonObject requestJson = new JsonObject(); + requestJson.addProperty("action", action); + if (!"get".equals(action)) { + requestJson.add("requestdomain", toJsonArray(requestDomains)); + requestJson.add("wsrequestdomain", toJsonArray(wsRequestDomains)); + requestJson.add("uploaddomain", toJsonArray(uploadDomains)); + requestJson.add("downloaddomain", toJsonArray(downloadDomains)); + } + String response = post(API_MODIFY_DOMAIN, GSON.toJson(requestJson)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaDomainResult.class); + } + + @Override + public String getWebViewDomain() throws WxErrorException { + return setWebViewDomain("get", null); + } + + @Override + public WxOpenMaWebDomainResult getWebViewDomainInfo() throws WxErrorException { + return setWebViewDomainInfo("get", null); + } + + @Override + public String setWebViewDomain(String action, List domainList) throws WxErrorException { + JsonObject requestJson = new JsonObject(); + requestJson.addProperty("action", action); + if (!"get".equals(action)) { + requestJson.add("webviewdomain", toJsonArray(domainList)); + } + return post(API_SET_WEBVIEW_DOMAIN, GSON.toJson(requestJson)); + } + + + @Override + public WxOpenMaWebDomainResult setWebViewDomainInfo(String action, List domainList) throws WxErrorException { + String response = this.setWebViewDomain(action, domainList); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaWebDomainResult.class); + } + + + @Override + public String getAccountBasicInfo() throws WxErrorException { + return get(API_GET_ACCOUNT_BASICINFO, ""); + } + + + @Override + public WxOpenMaBindTesterResult bindTester(String wechatId) throws WxErrorException { + JsonObject paramJson = new JsonObject(); + paramJson.addProperty("wechatid", wechatId); + String response = post(API_BIND_TESTER, GSON.toJson(paramJson)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaBindTesterResult.class); + } + + @Override + public WxOpenResult unbindTester(String wechatId) throws WxErrorException { + JsonObject paramJson = new JsonObject(); + paramJson.addProperty("wechatid", wechatId); + String response = post(API_UNBIND_TESTER, GSON.toJson(paramJson)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult unbindTesterByUserStr(String userStr) throws WxErrorException { + JsonObject paramJson = new JsonObject(); + paramJson.addProperty("userstr", userStr); + String response = post(API_UNBIND_TESTER, GSON.toJson(paramJson)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenMaTesterListResult getTesterList() throws WxErrorException { + JsonObject paramJson = new JsonObject(); + paramJson.addProperty("action", "get_experiencer"); + String response = post(API_GET_TESTERLIST, GSON.toJson(paramJson)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaTesterListResult.class); + } + + @Override + public WxOpenResult changeWxaSearchStatus(Integer status) throws WxErrorException { + JsonObject paramJson = new JsonObject(); + paramJson.addProperty("status", status); + String response = post(API_CHANGE_WXA_SEARCH_STATUS, GSON.toJson(paramJson)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenMaSearchStatusResult getWxaSearchStatus() throws WxErrorException { + String response = get(API_GET_WXA_SEARCH_STATUS, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSearchStatusResult.class); + } + + @Override + public WxOpenMaShowItemResult getShowWxaItem() throws WxErrorException { + String response = get(API_GET_SHOW_WXA_ITEM, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaShowItemResult.class); + } + + @Override + public WxOpenResult updateShowWxaItem(Integer flag, String mpAppId) throws WxErrorException { + JsonObject paramJson = new JsonObject(); + paramJson.addProperty("wxa_subscribe_biz_flag", flag); + paramJson.addProperty("appid", mpAppId); + String response = post(API_UPDATE_SHOW_WXA_ITEM, GSON.toJson(paramJson)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("template_id", templateId); + params.addProperty("user_version", userVersion); + params.addProperty("user_desc", userDesc); + //注意:ext_json必须是字符串类型 + params.addProperty("ext_json", GSON.toJson(extInfo)); + String response = post(API_CODE_COMMIT, GSON.toJson(params)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public File getTestQrcode(String pagePath, Map params) throws WxErrorException { + WxMaQrcodeParam qrcodeParam = WxMaQrcodeParam.create(pagePath); + qrcodeParam.addPageParam(params); + WxMaService wxMaService = this; + return wxMaService.execute(MaQrCodeRequestExecutor.create(getRequestHttp()), API_TEST_QRCODE, qrcodeParam); + } + + @Override + public WxOpenMaCategoryListResult getCategoryList() throws WxErrorException { + String response = get(API_GET_CATEGORY, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaCategoryListResult.class); + } + + @Override + public WxOpenMaPageListResult getPageList() throws WxErrorException { + String response = get(API_GET_PAGE, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaPageListResult.class); + } + + @Override + public WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException { + String response = post(API_SUBMIT_AUDIT, GSON.toJson(submitAuditMessage)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSubmitAuditResult.class); + } + + @Override + public WxOpenMaQueryAuditResult getAuditStatus(Long auditId) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("auditid", auditId); + String response = post(API_GET_AUDIT_STATUS, GSON.toJson(params)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryAuditResult.class); + } + + @Override + public WxOpenMaQueryAuditResult getLatestAuditStatus() throws WxErrorException { + String response = get(API_GET_LATEST_AUDIT_STATUS, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryAuditResult.class); + } + + @Override + public WxOpenResult releaseAudited() throws WxErrorException { + JsonObject params = new JsonObject(); + String response = post(API_RELEASE, GSON.toJson(params)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult changeVisitStatus(String action) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("action", action); + String response = post(API_CHANGE_VISITSTATUS, GSON.toJson(params)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult revertCodeRelease() throws WxErrorException { + String response = get(API_REVERT_CODE_RELEASE, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult undoCodeAudit() throws WxErrorException { + String response = get(API_UNDO_CODE_AUDIT, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public String getSupportVersion() throws WxErrorException { + JsonObject params = new JsonObject(); + return post(API_GET_WEAPP_SUPPORT_VERSION, GSON.toJson(params)); + } + + @Override + public WxOpenMaWeappSupportVersionResult getSupportVersionInfo() throws WxErrorException { + String response = this.getSupportVersion(); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaWeappSupportVersionResult.class); + } + + @Override + public String setSupportVersion(String version) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("version", version); + return post(API_SET_WEAPP_SUPPORT_VERSION, GSON.toJson(params)); + } + + @Override + public WxOpenResult setSupportVersionInfo(String version) throws WxErrorException { + String response = this.setSupportVersion(version); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult grayRelease(Integer grayPercentage) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("gray_percentage", grayPercentage); + String response = post(API_GRAY_RELEASE, GSON.toJson(params)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult revertGrayRelease() throws WxErrorException { + String response = get(API_REVERT_GRAY_RELEASE, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenMaGrayReleasePlanResult getGrayReleasePlan() throws WxErrorException { + String response = get(API_GET_GRAY_RELEASE_PLAN, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaGrayReleasePlanResult.class); + } + + @Override + public WxOpenMaQueryQuotaResult queryQuota() throws WxErrorException { + String response = get(API_QUERY_QUOTA, null); + return WxMaGsonBuilder.create().fromJson(response, WxOpenMaQueryQuotaResult.class); + } + + @Override + public Boolean speedAudit(Long auditId) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("auditid", auditId); + String response = post(API_SPEED_AUDIT, GSON.toJson(params)); + WxOpenResult result = WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + return result.isSuccess(); + } + + + @Override + public WxOpenResult addQrcodeJump(WxQrcodeJumpRule wxQrcodeJumpRule) throws WxErrorException { + String response = post(API_QRCODE_JUMP_ADD, GSON.toJson(wxQrcodeJumpRule)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxGetQrcodeJumpResult getQrcodeJump() throws WxErrorException { + String response = post(API_QRCODE_JUMP_GET, "{}"); + return WxMaGsonBuilder.create().fromJson(response, WxGetQrcodeJumpResult.class); + } + + @Override + public WxDownlooadQrcodeJumpResult downloadQrcodeJump() throws WxErrorException { + String response = post(API_QRCODE_JUMP_DOWNLOAD, "{}"); + return WxMaGsonBuilder.create().fromJson(response, WxDownlooadQrcodeJumpResult.class); + } + + @Override + public WxOpenResult deleteQrcodeJump(String prefix) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("prefix", prefix); + String response = post(API_QRCODE_JUMP_DELETE, GSON.toJson(params)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + @Override + public WxOpenResult publishQrcodeJump(String prefix) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("prefix", prefix); + String response = post(API_QRCODE_JUMP_PUBLISH, GSON.toJson(params)); + return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class); + } + + private JsonArray toJsonArray(List strList) { + JsonArray jsonArray = new JsonArray(); + if (strList != null && !strList.isEmpty()) { + for (String str : strList) { + jsonArray.add(str); + } + } + return jsonArray; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMessageRouter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMessageRouter.java new file mode 100644 index 0000000000..2e483fc0aa --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMessageRouter.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.mp.api.WxMpMessageRouter; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import me.chanjar.weixin.open.api.WxOpenService; + +import java.util.HashMap; +import java.util.Map; + +public class WxOpenMessageRouter extends WxMpMessageRouter { + private WxOpenService wxOpenService; + + public WxOpenMessageRouter(WxOpenService wxOpenService) { + super(null); + this.wxOpenService = wxOpenService; + } + + public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, String appId) { + return route(wxMessage, new HashMap(), appId); + } + + public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map context, String appId) { + return route(wxMessage, context, wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId)); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMpServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMpServiceImpl.java new file mode 100644 index 0000000000..5efa429ade --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMpServiceImpl.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.open.api.WxOpenComponentService; + +/** + * @author 007 + */ +public class WxOpenMpServiceImpl extends WxMpServiceImpl { + private WxOpenComponentService wxOpenComponentService; + private WxMpConfigStorage wxMpConfigStorage; + private String appId; + + public WxOpenMpServiceImpl(WxOpenComponentService wxOpenComponentService, String appId, WxMpConfigStorage wxMpConfigStorage) { + this.wxOpenComponentService = wxOpenComponentService; + this.appId = appId; + this.wxMpConfigStorage = wxMpConfigStorage; + initHttp(); + } + + @Override + public WxMpConfigStorage getWxMpConfigStorage() { + return wxMpConfigStorage; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + return wxOpenComponentService.getAuthorizerAccessToken(appId, forceRefresh); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java new file mode 100644 index 0000000000..eb4ffbfb2b --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java @@ -0,0 +1,62 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.open.api.WxOpenComponentService; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.api.WxOpenService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * @author 007 + */ +public abstract class WxOpenServiceAbstractImpl implements WxOpenService, RequestHttp { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + private WxOpenComponentService wxOpenComponentService = new WxOpenComponentServiceImpl(this); + private WxOpenConfigStorage wxOpenConfigStorage; + + @Override + public WxOpenComponentService getWxOpenComponentService() { + return wxOpenComponentService; + } + + @Override + public WxOpenConfigStorage getWxOpenConfigStorage() { + return wxOpenConfigStorage; + } + + @Override + public void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage) { + this.wxOpenConfigStorage = wxOpenConfigStorage; + this.initHttp(); + } + + /** + * 初始化 RequestHttp. + */ + public abstract void initHttp(); + + protected T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + try { + T result = executor.execute(uri, data, WxType.Open); + this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uri, data, result); + return result; + } catch (WxErrorException e) { + WxError error = e.getError(); + if (error.getErrorCode() != 0) { + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uri, data, error); + throw new WxErrorException(error, e); + } + return null; + } catch (IOException e) { + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uri, data, e.getMessage()); + throw new RuntimeException(e); + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceApacheHttpClientImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceApacheHttpClientImpl.java new file mode 100644 index 0000000000..24ee39cc30 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceApacheHttpClientImpl.java @@ -0,0 +1,67 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import org.apache.http.HttpHost; +import org.apache.http.impl.client.CloseableHttpClient; + +/** + * apache-http方式实现 + * + * @author 007 + */ +public class WxOpenServiceApacheHttpClientImpl extends WxOpenServiceAbstractImpl { + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + + @Override + public void initHttp() { + WxOpenConfigStorage configStorage = this.getWxOpenConfigStorage(); + ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); + if (null == apacheHttpClientBuilder) { + apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); + } + + apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) + .httpProxyPort(configStorage.getHttpProxyPort()) + .httpProxyUsername(configStorage.getHttpProxyUsername()) + .httpProxyPassword(configStorage.getHttpProxyPassword()); + + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + + } + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.APACHE_HTTP; + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(SimpleGetRequestExecutor.create(this), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(SimplePostRequestExecutor.create(this), url, postData); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java new file mode 100644 index 0000000000..c807ccdf99 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java @@ -0,0 +1,8 @@ +package me.chanjar.weixin.open.api.impl; + +/** + * @author 007 + */ +public class WxOpenServiceImpl extends WxOpenServiceApacheHttpClientImpl { + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenAuthorizerAccessToken.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenAuthorizerAccessToken.java new file mode 100644 index 0000000000..9a558ab776 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenAuthorizerAccessToken.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.open.bean; + +import lombok.Data; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxOpenAuthorizerAccessToken implements Serializable { + private static final long serialVersionUID = -4069745419280727420L; + + private String authorizerAccessToken; + + private String authorizerRefreshToken; + + private int expiresIn = -1; + + public static WxOpenAuthorizerAccessToken fromJson(String json) { + return WxOpenGsonBuilder.create().fromJson(json, WxOpenAuthorizerAccessToken.class); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenComponentAccessToken.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenComponentAccessToken.java new file mode 100644 index 0000000000..8a97c24dfb --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenComponentAccessToken.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.open.bean; + +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.io.Serializable; + +/** + * @author 007 + */ +public class WxOpenComponentAccessToken implements Serializable { + private static final long serialVersionUID = 2134550135400443725L; + + private String componentAccessToken; + + private int expiresIn = -1; + + public static WxOpenComponentAccessToken fromJson(String json) { + return WxOpenGsonBuilder.create().fromJson(json, WxOpenComponentAccessToken.class); + } + + public String getComponentAccessToken() { + return componentAccessToken; + } + + public void setComponentAccessToken(String componentAccessToken) { + this.componentAccessToken = componentAccessToken; + } + + public int getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java new file mode 100644 index 0000000000..274b5c49dd --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.open.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + *
    + * code换取session_key接口的响应
    + * 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject
    + * 微信返回报文:{"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"}
    + * 
    + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxOpenCreateResult implements Serializable { + + @SerializedName("open_appid") + private String openAppid; + + @SerializedName("errcode") + private String errcode; + + @SerializedName("errmsg") + private String errmsg; + + public static WxOpenCreateResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOpenCreateResult.class); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenGetResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenGetResult.java new file mode 100644 index 0000000000..3fb380ed39 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenGetResult.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.open.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.open.bean.result.WxOpenResult; + +import java.io.Serializable; + +/** + * 文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/account/get.html + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxOpenGetResult extends WxOpenResult implements Serializable { + + private static final long serialVersionUID = -1196242565823312696L; + + @SerializedName("open_appid") + private String openAppid; + + public static WxOpenGetResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOpenGetResult.class); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenMaCodeTemplate.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenMaCodeTemplate.java new file mode 100644 index 0000000000..be1bb9b138 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenMaCodeTemplate.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.open.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Charming + * @since 2018-04-26 17:10 + */ +@Data +public class WxOpenMaCodeTemplate implements Serializable { + private static final long serialVersionUID = -3278116984473619010L; + /** + * 草稿id + */ + @SerializedName(value = "draftId", alternate = "draft_id") + private Long draftId; + /** + * 模版id + */ + @SerializedName(value = "templateId", alternate = "template_id") + private Long templateId; + /** + * 模版版本号,开发者自定义字段 + */ + @SerializedName(value = "userVersion", alternate = "user_version") + private String userVersion; + /** + * 模版描述 开发者自定义字段 + */ + @SerializedName(value = "userDesc", alternate = "user_desc") + private String userDesc; + /** + * 开发者上传草稿时间 / 被添加为模版的时间 + */ + @SerializedName(value = "createTime", alternate = "create_time") + private Long createTime; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizationInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizationInfo.java new file mode 100644 index 0000000000..23e8f1497e --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizationInfo.java @@ -0,0 +1,20 @@ +package me.chanjar.weixin.open.bean.auth; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ +@Data +public class WxOpenAuthorizationInfo implements Serializable { + private static final long serialVersionUID = -8713680081354754208L; + + private String authorizerAppid; + private String authorizerAccessToken; + private int expiresIn; + private String authorizerRefreshToken; + private List funcInfo; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizerInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizerInfo.java new file mode 100644 index 0000000000..a80dab0307 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenAuthorizerInfo.java @@ -0,0 +1,66 @@ +package me.chanjar.weixin.open.bean.auth; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * @author 007 + */ +@Data +public class WxOpenAuthorizerInfo implements Serializable { + private static final long serialVersionUID = -5327886953416394738L; + + private String nickName; + private String headImg; + private Integer serviceTypeInfo; + private Integer verifyTypeInfo; + private String userName; + private String principalName; + private Map businessInfo; + private String alias; + private String qrcodeUrl; + /** + * 账号介绍 + */ + private String signature; + + /** + * 可根据这个字段判断是否为小程序类型授权 + */ + private MiniProgramInfo miniProgramInfo; + + @Data + public class MiniProgramInfo { + @SerializedName("visit_status") + private Integer visitStatus; + /** + * 小程序已设置的各个服务器域名. + */ + private Network network; + private List categories; + + @Data + public class Category { + private String first; + private String second; + } + + @Data + public class Network { + @SerializedName("RequestDomain") + private List requestDomain; + @SerializedName("WsRequestDomain") + private List wsRequestDomain; + @SerializedName("UploadDomain") + private List uploadDomain; + @SerializedName("DownloadDomain") + private List downloadDomain; + @SerializedName("BizDomain") + private List bizDomain; + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenMiniProgramInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenMiniProgramInfo.java new file mode 100644 index 0000000000..44f56e6f2b --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/WxOpenMiniProgramInfo.java @@ -0,0 +1,14 @@ +package me.chanjar.weixin.open.bean.auth; + +import lombok.Data; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; +import java.util.Map; + +@Data +public class WxOpenMiniProgramInfo { + private Map> network; + private List> categories; + private Integer visitStatus; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxFastMaCategory.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxFastMaCategory.java new file mode 100644 index 0000000000..3c0d2bab83 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxFastMaCategory.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 修改更新类目所需实体. + * + * @author Hipple + * @since 2019/1/25 10:49 + */ +@Data +public class WxFastMaCategory implements Serializable { + private static final long serialVersionUID = 1595589596066509155L; + + /** + * 一级类目ID. + */ + private int first; + + /** + * 二级类目ID. + */ + private int second; + + /** + * 资质信息. + */ + private List certicates; + + @Data + public static class Certificate { + private String key; + private String value; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java new file mode 100644 index 0000000000..e7a43894a2 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java @@ -0,0 +1,119 @@ +package me.chanjar.weixin.open.bean.ma; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 微信小程序三方平台代上传代码提交额外信息对象 + *

    + * 如果代码中已经有配置,则配置的合并规则为:除了pages和tabBar.list直接覆盖原配置,其他都为插入或同级覆盖。 + * extjson 详细说明 + * https://developers.weixin.qq.com/miniprogram/dev/devtools/ext.html#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%A8%A1%E6%9D%BF%E5%BC%80%E5%8F%91 + *

    + * + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxMaOpenCommitExtInfo implements Serializable { + + WxMaOpenCommitExtInfo() { + + } + + /** + * 授权小程序Appid,可填入商户小程序AppID,以区分不同商户 + */ + private String extAppid; + + /** + * 配置 ext.json 是否生效 + */ + private Boolean extEnable = Boolean.TRUE; + + /** + * 是否直接提交到待审核列表 + */ + private Boolean directCommit = Boolean.FALSE; + + @SerializedName("ext") + private Map extMap; + + @SerializedName("extPages") + private Map extPages; + + /** + * 页面路径列表(和app.json结构一致) + */ + @SerializedName("pages") + private List pageList; + + /** + * 分包结构配置 + */ + @SerializedName("subpackages") + private List subpackageList; + + @SerializedName("window") + private WxMaOpenWindow window; + + @SerializedName("networkTimeout") + private WxMaOpenNetworkTimeout networkTimeout; + + @SerializedName("tabBar") + private WxMaOpenTabBar tabBar; + + /** + * 添加扩展项 + * + * @param key + * @param value + */ + public void addExt(String key, String value) { + if (extMap == null) + extMap = new HashMap<>(); + if (StringUtils.isNoneBlank(key, value)) + extMap.put(key, value); + } + + /** + * 添加扩展页面 + * + * @param pagePath + * @param page + */ + public void addExtPage(String pagePath, WxMaOpenPage page) { + if (extPages == null) + extPages = new HashMap<>(); + if (StringUtils.isNotBlank(pagePath) && page != null) + extPages.put(pagePath, page); + } + + /** + * 添加页面 + * + * @param pagePath + */ + public void addPage(String pagePath) { + if (pageList == null) + pageList = new ArrayList<>(); + if (StringUtils.isNotBlank(pagePath)) + pageList.add(pagePath); + } + + public static WxMaOpenCommitExtInfo INSTANCE() { + return new WxMaOpenCommitExtInfo(); + } + + public String toJson() { + return WxOpenGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenNetworkTimeout.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenNetworkTimeout.java new file mode 100644 index 0000000000..9717f42af8 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenNetworkTimeout.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxMaOpenNetworkTimeout implements Serializable { + + private Integer request; + + private Integer connectSocket; + + private Integer uploadFile; + + private Integer downloadFile; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenPage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenPage.java new file mode 100644 index 0000000000..ca63fc3d8f --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenPage.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxMaOpenPage implements Serializable { + private String navigationBarBackgroundColor; + private String navigationBarTextStyle; + private String navigationBarTitleText; + private String backgroundColor; + private String backgroundTextStyle; + private Boolean enablePullDownRefresh; + private Integer onReachBottomDistance; + private Boolean disableScroll; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenSubpackage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenSubpackage.java new file mode 100644 index 0000000000..e74049a530 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenSubpackage.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Builder; +import lombok.Data; + +/** + * @author: momorans + * @create: 2019-03-12 + **/ +@Data +@Builder +public class WxMaOpenSubpackage { + /** + * 分包根目录 + */ + private String root; + + /** + * 分包别名,分包预下载时可以使用 + */ + private String name; + + + /** + * 分包页面路径,相对与分包根目录 + */ + private String pages; + + /** + * 分包是否是独立分包 + */ + private Boolean independent; + + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTab.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTab.java new file mode 100644 index 0000000000..48e1044db8 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTab.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Data; +import lombok.NonNull; + +import java.io.Serializable; + +/** + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxMaOpenTab implements Serializable { + @NonNull + private String pagePath; + + @NonNull + private String text; + private String iconPath; + private String selectedIconPath; + + public WxMaOpenTab(@NonNull String pagePath, @NonNull String text) { + this.pagePath = pagePath; + this.text = text; + } + + + public WxMaOpenTab(@NonNull String pagePath, @NonNull String text, String iconPath, String selectedIconPath) { + this.pagePath = pagePath; + this.text = text; + this.iconPath = iconPath; + this.selectedIconPath = selectedIconPath; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTabBar.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTabBar.java new file mode 100644 index 0000000000..6245c0331d --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTabBar.java @@ -0,0 +1,49 @@ +package me.chanjar.weixin.open.bean.ma; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * tabBar对象 + * + * @author yqx + * @date 2018/9/13 + */ +@Data +@NoArgsConstructor +public class WxMaOpenTabBar implements Serializable { + @NonNull + private String color; + + @NonNull + private String selectedColor; + + @NonNull + private String backgroundColor; + + private String borderStyle; + + @NonNull + @SerializedName("list") + private List tabList; + + private String position; + + /** + * 添加tab + * + * @param text + * @param pagePath + */ + public void addTab(String text, String pagePath) { + if (tabList == null) + tabList = new ArrayList<>(); + tabList.add(new WxMaOpenTab(pagePath, text)); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenWindow.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenWindow.java new file mode 100644 index 0000000000..4848f8c7b1 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenWindow.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Data; + +import java.io.Serializable; + +/** + * window对象 + * + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxMaOpenWindow implements Serializable { + private String navigationBarBackgroundColor; + + private String navigationBarTextStyle; + + private String navigationBarTitleText; + + private String navigationStyle; + + private String backgroundColor; + + private String backgroundTextStyle; + + private String backgroundColorTop; + + private String backgroundColorBottom; + + private Boolean enablePullDownRefresh; + + private Integer onReachBottomDistance; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaQrcodeParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaQrcodeParam.java new file mode 100644 index 0000000000..2dafa037d6 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaQrcodeParam.java @@ -0,0 +1,86 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * 微信小程序体验二维码参数 + * + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxMaQrcodeParam { + + WxMaQrcodeParam() { + + } + + /** + * 页面路径 + */ + private String pagePath; + + /** + * 页面参数 + */ + private Map pageParams; + + /** + * 添加页面参数 + * + * @param key + * @param value + */ + public WxMaQrcodeParam addPageParam(String key, String value) { + if (StringUtils.isNoneBlank(key, value)) { + if (pageParams == null) + pageParams = new HashMap<>(); + pageParams.put(key, value); + } + return this; + } + + /** + * 添加页面参数 + * + * @param params + * @return + */ + public WxMaQrcodeParam addPageParam(Map params) { + if (params != null) { + if (pageParams == null) + pageParams = new HashMap<>(); + pageParams.putAll(params); + } + return this; + } + + /** + * 组装完整的页面请求路径(带参数) + * + * @return + */ + public String getRequestPath() { + if (StringUtils.isNotBlank(getPagePath()) && getPageParams() != null && !getPageParams().isEmpty()) { + Set keys = getPageParams().keySet(); + StringBuilder sb = new StringBuilder(); + for (String key : keys) { + sb.append("&").append(key).append("=").append(getPageParams().get(key)); + } + return pagePath + "?" + sb.substring(1); + } else { + return pagePath; + } + } + + public static WxMaQrcodeParam create(String pagePath) { + WxMaQrcodeParam instance = new WxMaQrcodeParam(); + instance.setPagePath(pagePath); + return instance; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaCategory.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaCategory.java new file mode 100644 index 0000000000..f5f68df143 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaCategory.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.open.bean.ma; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.io.Serializable; + +/** + * 微信小程序分类目录. + * + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxOpenMaCategory implements Serializable { + private static final long serialVersionUID = -700005096619889630L; + + @SerializedName("first_class") + private String firstClass; + + @SerializedName("second_class") + private String secondClass; + + @SerializedName("third_class") + private String thirdClass; + + @SerializedName("first_id") + private Integer firstId; + + @SerializedName("second_id") + private Integer secondId; + + @SerializedName("third_id") + private Integer thirdId; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaMember.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaMember.java new file mode 100644 index 0000000000..dc939373ab --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaMember.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 微信开放平台小程序成员对象 + * + * @author yqx + * @date 2018/9/12 + */ +@Data +public class WxOpenMaMember implements Serializable { + /** + * 人员对应的唯一字符串 + */ + private String userstr; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaPreviewInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaPreviewInfo.java new file mode 100644 index 0000000000..47f8b37a3e --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaPreviewInfo.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.open.bean.ma; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + * @author zxfreedom + * @description + * @date 2019/12/30 + */ +@Data +public class WxOpenMaPreviewInfo { + + /** + * 录屏mediaid列表,可以通过提审素材上传接口获得 + */ + @SerializedName("video_id_list") + private String[] videoIdList; + + /** + * 截屏mediaid列表,可以通过提审素材上传接口获得 + */ + @SerializedName("pic_id_list") + private String[] picIdList; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaSubmitAudit.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaSubmitAudit.java new file mode 100644 index 0000000000..9c9e712241 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaSubmitAudit.java @@ -0,0 +1,60 @@ +package me.chanjar.weixin.open.bean.ma; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 三方平台提交小程序代码审核 + * + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxOpenMaSubmitAudit implements Serializable { + + + /** + * 小程序的页面,可通过“获取小程序的第三方提交代码的页面配置”接口获得 + */ + @SerializedName("address") + private String pagePath; + + /** + * 小程序的标签,多个标签用空格分隔,标签不能多于10个,标签长度不超过20 + */ + @SerializedName("tag") + private String tag; + + /** + * 类目名称,可通过“获取授权小程序帐号的可选类目”接口获得 + */ + @SerializedName("first_class") + private String firstClass; + + @SerializedName("second_class") + private String secondClass; + + @SerializedName("third_class") + private String thirdClass; + + /** + * 类目的ID,可通过“获取授权小程序帐号的可选类目”接口获得 + */ + @SerializedName("first_id") + private Integer firstId; + + @SerializedName("second_id") + private Integer secondId; + + @SerializedName("third_id") + private Integer thirdId; + + /** + * 小程序页面的标题,标题长度不超过32 + */ + @SerializedName("title") + private String title; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java new file mode 100644 index 0000000000..56129a9845 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java @@ -0,0 +1,49 @@ +package me.chanjar.weixin.open.bean.message; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.open.bean.ma.WxOpenMaPreviewInfo; +import me.chanjar.weixin.open.bean.ma.WxOpenMaSubmitAudit; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信小程序代码包提交审核(仅供第三方开发者代小程序调用) + * + * @author yqx + * @date 2018/9/13 + */ +@Data +public class WxOpenMaSubmitAuditMessage implements Serializable { + + /** + * 提交审核项的一个列表(至少填写1项,至多填写5项) + */ + @SerializedName("item_list") + private List itemList; + + /** + * 预览信息(小程序页面截图和操作录屏) + */ + @SerializedName("preview_info") + private List previewInfo; + + /** + * 小程序版本说明和功能解释 + */ + @SerializedName("version_desc") + private String versionDesc; + + /** + * 反馈内容,不超过200字 + */ + @SerializedName("feedback_info") + private String feedbackInfo; + + /** + * 图片media_id列表,中间用“丨”分割,xx丨yy丨zz,不超过5张图片, 其中 media_id 可以通过新增临时素材接口上传而得到 + */ + @SerializedName("feedback_stuff") + private String feedbackStuff; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java new file mode 100644 index 0000000000..bef7d16d26 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java @@ -0,0 +1,155 @@ +package me.chanjar.weixin.open.bean.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.util.WxOpenCryptUtil; +import me.chanjar.weixin.open.util.xml.XStreamTransformer; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +/** + * @author 007 + */ +@Data +@Slf4j +@XStreamAlias("xml") +public class WxOpenXmlMessage implements Serializable { + private static final long serialVersionUID = -5641769554709507771L; + + @XStreamAlias("AppId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String appId; + + @XStreamAlias("CreateTime") + private Long createTime; + + @XStreamAlias("InfoType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String infoType; + + @XStreamAlias("ComponentVerifyTicket") + @XStreamConverter(value = XStreamCDataConverter.class) + private String componentVerifyTicket; + + @XStreamAlias("AuthorizerAppid") + @XStreamConverter(value = XStreamCDataConverter.class) + private String authorizerAppid; + + @XStreamAlias("AuthorizationCode") + @XStreamConverter(value = XStreamCDataConverter.class) + private String authorizationCode; + + @XStreamAlias("AuthorizationCodeExpiredTime") + private Long authorizationCodeExpiredTime; + + @XStreamAlias("PreAuthCode") + @XStreamConverter(value = XStreamCDataConverter.class) + private String preAuthCode; + + // 以下为快速创建小程序接口推送的的信息 + + @XStreamAlias("appid") + private String registAppId; + + @XStreamAlias("status") + private int status; + + @XStreamAlias("auth_code") + private String authCode; + + @XStreamAlias("msg") + @XStreamConverter(value = XStreamCDataConverter.class) + private String msg; + + @XStreamAlias("info") + private Info info = new Info(); + + @XStreamAlias("info") + @Data + public static class Info implements Serializable { + private static final long serialVersionUID = 7706235740094081194L; + + @XStreamAlias("name") + @XStreamConverter(value = XStreamCDataConverter.class) + private String name; + + @XStreamAlias("code") + @XStreamConverter(value = XStreamCDataConverter.class) + private String code; + + @XStreamAlias("code_type") + private int codeType; + + @XStreamAlias("legal_persona_wechat") + @XStreamConverter(value = XStreamCDataConverter.class) + private String legalPersonaWechat; + + @XStreamAlias("legal_persona_name") + @XStreamConverter(value = XStreamCDataConverter.class) + private String legalPersonaName; + + @XStreamAlias("component_phone") + @XStreamConverter(value = XStreamCDataConverter.class) + private String componentPhone; + } + + public static String wxMpOutXmlMessageToEncryptedXml(WxMpXmlOutMessage message, WxOpenConfigStorage wxOpenConfigStorage) { + String plainXml = message.toXml(); + WxOpenCryptUtil pc = new WxOpenCryptUtil(wxOpenConfigStorage); + return pc.encrypt(plainXml); + } + + public static WxOpenXmlMessage fromXml(String xml) { + //修改微信变态的消息内容格式,方便解析 + xml = xml.replace("", ""); + return XStreamTransformer.fromXml(WxOpenXmlMessage.class, xml); + } + + public static WxOpenXmlMessage fromXml(InputStream is) { + return XStreamTransformer.fromXml(WxOpenXmlMessage.class, is); + } + + /** + * 从加密字符串转换 + * + * @param encryptedXml 密文 + * @param wxOpenConfigStorage 配置存储器对象 + * @param timestamp 时间戳 + * @param nonce 随机串 + * @param msgSignature 签名串 + */ + public static WxOpenXmlMessage fromEncryptedXml(String encryptedXml, WxOpenConfigStorage wxOpenConfigStorage, + String timestamp, String nonce, String msgSignature) { + WxOpenCryptUtil cryptUtil = new WxOpenCryptUtil(wxOpenConfigStorage); + String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); + log.debug("解密后的原始xml消息内容:{}", plainText); + return fromXml(plainText); + } + + public static WxMpXmlMessage fromEncryptedMpXml(String encryptedXml, WxOpenConfigStorage wxOpenConfigStorage, + String timestamp, String nonce, String msgSignature) { + WxOpenCryptUtil cryptUtil = new WxOpenCryptUtil(wxOpenConfigStorage); + String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); + return WxMpXmlMessage.fromXml(plainText); + } + + public static WxOpenXmlMessage fromEncryptedXml(InputStream is, WxOpenConfigStorage wxOpenConfigStorage, + String timestamp, String nonce, String msgSignature) { + try { + return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), + wxOpenConfigStorage, timestamp, nonce, msgSignature); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxDownlooadQrcodeJumpResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxDownlooadQrcodeJumpResult.java new file mode 100644 index 0000000000..3bdc532b4a --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxDownlooadQrcodeJumpResult.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +/** + * 二维码规则的校验文件名称及内容 + * + * @author hanwei59 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxDownlooadQrcodeJumpResult extends WxOpenResult { + + //文件名称 + @SerializedName("file_name") + private String fileName; + + //文件内容 + @SerializedName("file_content") + private String fileContent; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResult.java new file mode 100644 index 0000000000..11382889ff --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResult.java @@ -0,0 +1,136 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 快速创建的小程序的账号基本信息. + * + * @author Hipple + * @since 2019/1/23 14:39 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxFastMaAccountBasicInfoResult extends WxOpenResult { + private static final long serialVersionUID = -8713680081353954208L; + + /** + * 小程序ID + */ + @SerializedName("appid") + private String appId; + + /** + * 帐号类型(1:订阅号,2:服务号,3:小程序) + */ + @SerializedName("account_type") + private Integer accountType; + + /** + * 主体类型(1:企业) + */ + @SerializedName("principal_type") + private Integer principalType; + + /** + * 主体名称 + */ + @SerializedName("principal_name") + private String principalName; + + /** + * 实名验证状态(1:实名验证成功,2:实名验证中,3:实名验证失败)调用接口1.1创建帐号时,realname_status会初始化为2对于注册方式为微信认证的帐号,资质认证成功时,realname_status会更新为1 注意!!!当realname_status不为1时,帐号只允许调用本文档内的以下API:(即无权限调用其他API) 微信认证相关接口(参考2.x) 帐号设置相关接口(参考3.x) + */ + @SerializedName("realname_status") + private Integer realnameStatus; + + + /** + * 微信认证信息 + */ + @SerializedName("wx_verify_info") + private WxVerifyInfo wxVerifyInfo; + /** + * 功能介绍信息 + */ + @SerializedName("signature_info") + private SignatureInfo signatureInfo; + /** + * 头像信息 + */ + @SerializedName("head_image_info") + private HeadImageInfo headImageInfo; + + @Data + public static class WxVerifyInfo { + /** + * 是否资质认证(true:是,false:否)若是,拥有微信认证相关的权限 + */ + @SerializedName("qualification_verify") + private Boolean qualificationVerify; + /** + * 是否名称认证(true:是,false:否)对于公众号(订阅号、服务号),是名称认证,微信客户端才会有微信认证打勾的标识。 + */ + @SerializedName("naming_verify") + private Boolean namingVerify; + /** + * 是否需要年审(true:是,false:否)(qualification_verify = true时才有该字段) + */ + @SerializedName("annual_review") + private Boolean annualReview; + + /** + * 年审开始时间,时间戳(qualification_verify = true时才有该字段) + */ + @SerializedName("annual_review_begin_time") + private String annualReviewBeginTime; + + /** + * 年审截止时间,时间戳(qualification_verify = true时才有该字段) + */ + @SerializedName("annual_review_end_time") + private String annualReviewEndTime; + } + + + @Data + public static class SignatureInfo { + /** + * 功能介绍 + */ + @SerializedName("signature") + private String signature; + /** + * 头像已使用修改次数(本月) + */ + @SerializedName("modify_used_count") + private Integer modifyUsedCount; + /** + * 头像修改次数总额度(本月) + */ + @SerializedName("modify_quota") + private Integer modifyQuota; + } + + + @Data + public static class HeadImageInfo { + /** + * 头像url + */ + @SerializedName("head_image_url") + private String headImageUrl; + /** + * 头像已使用修改次数(本月) + */ + @SerializedName("modify_used_count") + private Integer modifyUsedCount; + + /** + * 头像修改次数总额度(本月) + */ + @SerializedName("modify_quota") + private Integer modifyQuota; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResult.java new file mode 100644 index 0000000000..825285a1f6 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResult.java @@ -0,0 +1,74 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 获取小程序已经设置的类目结果类. + * + * @author Hipple + * @since 2019/1/26 18:27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxFastMaBeenSetCategoryResult extends WxOpenResult { + private static final long serialVersionUID = -7662344448437700644L; + + /** + * 一个更改周期内可以设置类目的次数 + */ + @SerializedName("limit") + private int limit; + /** + * 本更改周期内还可以设置类目的次数 + */ + @SerializedName("quota") + private int quota; + /** + * 最多可以设置的类目数量 + */ + @SerializedName("category_limit") + private int categoryLimit; + /** + * 类目 + */ + @SerializedName("categories") + private List categories; + + @Data + public static class CategoriesBean { + /** + * 一级类目ID + */ + @SerializedName("first") + private int first; + /** + * 一级类目名称 + */ + @SerializedName("first_name") + private String firstName; + /** + * 二级类目ID + */ + @SerializedName("second") + private int second; + /** + * 二级类目名称 + */ + @SerializedName("second_name") + private String secondName; + /** + * 审核状态(1审核中 2审核不通过 3审核通过) + */ + @SerializedName("audit_status") + private int auditStatus; + /** + * 审核不通过原因 + */ + @SerializedName("audit_reason") + private String auditReason; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResult.java new file mode 100644 index 0000000000..b3d0dd9d94 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResult.java @@ -0,0 +1,62 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 获取账号所有可以设置的类目. + * + * @author Hipple + * @since 2019/1/26 18:43 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxFastMaCanSetCategoryResult extends WxOpenResult { + private static final long serialVersionUID = -2469386233538980102L; + @SerializedName("errcode") + private int errCode; + @SerializedName("categories_list") + private CategoriesListBean categoriesList; + + @Data + public static class CategoriesListBean { + private List categories; + + @Data + public static class CategoriesBean { + private int id; + private QualifyBean qualify; + private String name; + private int level; + private int father; + @SerializedName("sensitive_type") + private int sensitiveType; + @SerializedName("available_for_plugin") + private boolean availableForPlugin; + @SerializedName("is_hidden") + private boolean isHidden; + private String type; + @SerializedName("need_report") + private int needReport; + @SerializedName("can_use_cityserivce") + private int canUseCityService; + private List children; + @SerializedName("type_list") + private List typeList; + @SerializedName("available_api_list") + private List availableApiList; + private List apis; + + @Data + public static class QualifyBean { + private String remark; + @SerializedName("exter_list") + private List externalList; + } + + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCheckNickameResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCheckNickameResult.java new file mode 100644 index 0000000000..eed76934a4 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCheckNickameResult.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 微信认证名称检测结果类. + * + * @author Hipple + * @since 2019/1/26 17:39 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxFastMaCheckNickameResult extends WxOpenResult { + private static final long serialVersionUID = 8022192589710319473L; + + /** + * 审核编号. + */ + @SerializedName("hit_condition") + boolean hitCondition; + + /** + * 材料说明 + */ + @SerializedName("wording") + String wording; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaQueryNicknameStatusResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaQueryNicknameStatusResult.java new file mode 100644 index 0000000000..fa8fd1d5c9 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaQueryNicknameStatusResult.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 查询改名状态实体类. + * + * @author Hipple + * @since 2019/1/26 17:52 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class WxFastMaQueryNicknameStatusResult extends WxOpenResult { + + private static final long serialVersionUID = 8492116046290791757L; + + /** + * 审核昵称 + */ + @SerializedName("nickname") + private String nickname; + /** + * 审核状态,1:审核中,2:审核失败,3:审核成功 + */ + @SerializedName("audit_stat") + private int auditStat; + /** + * 失败原因 + */ + @SerializedName("fail_reason") + private String failReason; + /** + * 审核提交时间 + */ + @SerializedName("create_time") + private String createTime; + /** + * 审核提交时间 + */ + @SerializedName("audit_time") + private String auditTime; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaSetNickameResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaSetNickameResult.java new file mode 100644 index 0000000000..3beee9b252 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaSetNickameResult.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 设置小程序名称结果类. + * + * @author Hipple + * @since 2019/1/26 17:39 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxFastMaSetNickameResult extends WxOpenResult { + private static final long serialVersionUID = 8022192589710319473L; + + /** + * 审核编号. + */ + @SerializedName("audit_id") + Long auditId; + + /** + * 材料说明 + */ + @SerializedName("wording") + String wording; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxGetQrcodeJumpResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxGetQrcodeJumpResult.java new file mode 100644 index 0000000000..f47e8be618 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxGetQrcodeJumpResult.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.util.List; + +/** + * 已设置的二维码规则信息 + * + * @author hanwei59 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxGetQrcodeJumpResult extends WxOpenResult { + + //二维码规则详情,数组形式 + @SerializedName("rule_list") + List ruleList; + + //是否已经打开二维码跳转链接设置 + @SerializedName("qrcodejump_open") + private String qrcodejumpOpen; + + //本月还可发布的次数 + @SerializedName("qrcodejump_pub_quota") + private Integer qrcodejumpPubQuota; + + //二维码规则数量 + @SerializedName("list_size") + private Integer listSize; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerInfoResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerInfoResult.java new file mode 100644 index 0000000000..60dd065b56 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerInfoResult.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.open.bean.result; + +import lombok.Data; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxOpenAuthorizerInfoResult implements Serializable { + private static final long serialVersionUID = 3166298050833019785L; + + private WxOpenAuthorizationInfo authorizationInfo; + private WxOpenAuthorizerInfo authorizerInfo; + + public boolean isMiniProgram() { + return authorizerInfo != null && authorizerInfo.getMiniProgramInfo() != null; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerListResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerListResult.java new file mode 100644 index 0000000000..19f3ebdecf --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerListResult.java @@ -0,0 +1,15 @@ +package me.chanjar.weixin.open.bean.result; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * @author robgao + */ +@Data +public class WxOpenAuthorizerListResult { + private int totalCount; + private List> list; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerOptionResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerOptionResult.java new file mode 100644 index 0000000000..c0ccf008ad --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenAuthorizerOptionResult.java @@ -0,0 +1,17 @@ +package me.chanjar.weixin.open.bean.result; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxOpenAuthorizerOptionResult implements Serializable { + private static final long serialVersionUID = 4477837353654658179L; + + String authorizerAppid; + String optionName; + String optionValue; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaBindTesterResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaBindTesterResult.java new file mode 100644 index 0000000000..ac06003456 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaBindTesterResult.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaBindTesterResult extends WxOpenResult { + + + private static final long serialVersionUID = -730133894662203011L; + + @SerializedName("userstr") + private String userstr; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaCategoryListResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaCategoryListResult.java new file mode 100644 index 0000000000..262f79b487 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaCategoryListResult.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.bean.ma.WxOpenMaCategory; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.util.List; + +/** + * 微信开放平台小程序分类目录列表返回 + * + * @author yqx + * @date 2018/9/12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaCategoryListResult extends WxOpenResult { + private static final long serialVersionUID = 4549360618179745721L; + + @SerializedName("category_list") + List categoryList; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaDomainResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaDomainResult.java new file mode 100644 index 0000000000..feccc786b6 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaDomainResult.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 微信开放平台小程序域名设置返回对象. + * + * @author yqx + * @date 2018/9/12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaDomainResult extends WxOpenResult { + private static final long serialVersionUID = 3406315629639573330L; + + @SerializedName("requestdomain") + List requestdomainList; + + @SerializedName("wsrequestdomain") + List wsrequestdomainList; + + @SerializedName("uploaddomain") + List uploaddomainList; + + @SerializedName("downloaddomain") + List downloaddomainList; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaGrayReleasePlanResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaGrayReleasePlanResult.java new file mode 100644 index 0000000000..71216ab79c --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaGrayReleasePlanResult.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +/** + * 微信开放平台小程序当前分阶段发布详情 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaGrayReleasePlanResult extends WxOpenResult { + + private static final long serialVersionUID = 8417849861393170728L; + + @SerializedName("gray_release_plan") + private GrayReleasePlanBean grayReleasePlan; + + + @Data + public static class GrayReleasePlanBean { + + /** + * 0:初始状态 1:执行中 2:暂停中 3:执行完毕 4:被删除 + */ + @SerializedName("status") + private Integer status; + + /** + * 创建时间 + */ + @SerializedName("create_timestamp") + private Long createTimestamp; + + + /** + * 灰度百分比 + */ + @SerializedName("gray_percentage") + private Integer grayPercentage; + } + + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaPageListResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaPageListResult.java new file mode 100644 index 0000000000..9f7ee95f72 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaPageListResult.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.util.List; + +/** + * 微信开放平台小程序第三方提交代码的页面配置列表. + * + * @author yqx + * @date 2018/9/12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaPageListResult extends WxOpenResult { + private static final long serialVersionUID = 6982848180319905444L; + + @SerializedName("page_list") + List pageList; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryAuditResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryAuditResult.java new file mode 100644 index 0000000000..3f01aa745c --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryAuditResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * . + * + * @author yqx + * @date 2018/10/3 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaQueryAuditResult extends WxOpenResult { + private static final long serialVersionUID = 8022192589710319473L; + + /** + * 审核编号. + */ + @SerializedName("auditid") + Long auditId; + + /** + * 审核状态:2-审核中,0-审核通过,1-审核失败. + */ + Integer status; + + /** + * 审核失败原因. + */ + String reason; + /** + * 当status=1,审核被拒绝时,会返回审核失败的小程序截图示例。 xxx丨yyy丨zzz是media_id可通过获取永久素材接口 拉取截图内容). + */ + @SerializedName(value = "screenshot") + private String screenShot; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryQuotaResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryQuotaResult.java new file mode 100644 index 0000000000..3b02906242 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryQuotaResult.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +/** + * 微信开放平台小程序当前分阶段发布详情 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaQueryQuotaResult extends WxOpenResult { + + private static final long serialVersionUID = 5915265985261653007L; + + @SerializedName("rest") + private Integer rest; + + @SerializedName("limit") + private Integer limit; + + @SerializedName("speedup_rest") + private Integer speedupRest; + + @SerializedName("speedup_limit") + private Integer speedupLimit; + + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSearchStatusResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSearchStatusResult.java new file mode 100644 index 0000000000..9c5c08f1ac --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSearchStatusResult.java @@ -0,0 +1,20 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaSearchStatusResult extends WxOpenResult { + private static final long serialVersionUID = -1843419921284224813L; + + @SerializedName("status") + private Integer status; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaShowItemResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaShowItemResult.java new file mode 100644 index 0000000000..a0a30f9277 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaShowItemResult.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaShowItemResult extends WxOpenResult { + + private static final long serialVersionUID = -5707576958339934210L; + + @SerializedName("is_open") + private Integer isOpen; + + @SerializedName("can_open") + private Integer canOpen; + + @SerializedName("appid") + private String appid; + + @SerializedName("nickname") + private String nickname; + + @SerializedName("headimg") + private String headimg; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSubmitAuditResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSubmitAuditResult.java new file mode 100644 index 0000000000..69774e8e4f --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSubmitAuditResult.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 微信开放平台小程序发布代码审核结果. + * + * @author yqx + * @date 2018/9/12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaSubmitAuditResult extends WxOpenResult { + private static final long serialVersionUID = 7431725910039734365L; + + /** + * 审核编号. + */ + @SerializedName("auditid") + Long auditId; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaTesterListResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaTesterListResult.java new file mode 100644 index 0000000000..014381ea4f --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaTesterListResult.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.open.bean.result; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.bean.ma.WxOpenMaMember; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +/** + * 微信开放平台小程序体验者列表返回. + * + * @author yqx + * @date 2018/9/12 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxOpenMaTesterListResult extends WxOpenResult { + private static final long serialVersionUID = -613936397557067111L; + + @SerializedName("members") + List membersList; + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaWeappSupportVersionResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaWeappSupportVersionResult.java new file mode 100644 index 0000000000..a39ef2b4a0 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaWeappSupportVersionResult.java @@ -0,0 +1,53 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; + +import java.util.List; + +/** + * 查询当前设置的最低基础库版本及各版本用户占比 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaWeappSupportVersionResult extends WxOpenResult { + + + private static final long serialVersionUID = -2955725249930665377L; + + @SerializedName("now_version") + String nowVersion; + + @SerializedName("uv_info") + UvInfoBean uvInfo; + + + @Data + public static class UvInfoBean { + + @SerializedName("items") + List items; + + } + + @Data + public static class VersionPercentageBean { + + @SerializedName("percentage") + private Integer percentage; + + @SerializedName("version") + private String version; + + } + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } + + + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaWebDomainResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaWebDomainResult.java new file mode 100644 index 0000000000..4ff878e92d --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaWebDomainResult.java @@ -0,0 +1,18 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WxOpenMaWebDomainResult extends WxOpenResult { + + private static final long serialVersionUID = -2182687859448940313L; + + @SerializedName("webviewdomain") + List webviewdomainList; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenQueryAuthResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenQueryAuthResult.java new file mode 100644 index 0000000000..5da7b6e6cf --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenQueryAuthResult.java @@ -0,0 +1,16 @@ +package me.chanjar.weixin.open.bean.result; + +import lombok.Data; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxOpenQueryAuthResult implements Serializable { + private static final long serialVersionUID = 2394736235020206855L; + + private WxOpenAuthorizationInfo authorizationInfo; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenResult.java new file mode 100644 index 0000000000..90433d945c --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenResult.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.open.bean.result; + +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * 基础的微信开放平台请求结果. + * + * @author yqx + * @date 2018/10/1 + */ +@Data +public class WxOpenResult implements Serializable { + private static final long serialVersionUID = 2101652152604850904L; + protected String errcode; + protected String errmsg; + + /** + * 请求是否成功. + */ + public boolean isSuccess() { + return StringUtils.equalsIgnoreCase(errcode, "0"); + } + + public static WxOpenResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOpenResult.class); + } + + @Override + public String toString() { + return WxOpenGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxQrcodeJumpRule.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxQrcodeJumpRule.java new file mode 100644 index 0000000000..93c06a2c22 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxQrcodeJumpRule.java @@ -0,0 +1,63 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 二维码规则 + * + * @author hanwei59 + */ +@Data +public class WxQrcodeJumpRule implements Serializable { + private static final long serialVersionUID = -7139573923977433678L; + + /** + * 二维码规则 + */ + @SerializedName("prefix") + private String prefix; + + /** + * 是否独占符合二维码前缀匹配规则的所有子规则:1为不占用,2为占用 + * 详细说明:https://mp.weixin.qq.com/debug/wxadoc/introduction/qrcode.html#前缀占用规则 + */ + @SerializedName("permit_sub_rule") + private String permitSubRule; + + /** + * 小程序功能页面 + */ + @SerializedName("path") + private String path; + + /** + * 测试范围: + * 1为开发版(配置只对开发者生效) + * 2为体验版(配置对管理员、体验者生效) + * 3为线上版本(配置对管理员、开发者和体验者生效) + */ + @SerializedName("open_version") + private String openVersion; + + /** + * 测试链接(选填)可填写不多于5个用于测试的二维码完整链接,此链接必须符合已填写的二维码规则。 + */ + @SerializedName("debug_url") + private List debugUrl; + + /** + * 编辑标志位,0表示新增二维码规则,1表示修改已有二维码规则 + */ + @SerializedName("is_edit") + private String isEdit; + + /** + * 发布标志位,1 表示未发布,2 表示已发布 + */ + @SerializedName("state") + private String state; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeApacheHttpRequestExecutor.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeApacheHttpRequestExecutor.java new file mode 100644 index 0000000000..7f9b7694e5 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeApacheHttpRequestExecutor.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.open.executor; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.UUID; + +/** + * @author yqx + * @date 2018-09-13 + */ +public class MaQrCodeApacheHttpRequestExecutor extends MaQrCodeRequestExecutor { + public MaQrCodeApacheHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public File execute(String uri, WxMaQrcodeParam qrcodeParam, WxType wxType) throws WxErrorException, IOException { + if (qrcodeParam != null && StringUtils.isNotBlank(qrcodeParam.getPagePath())) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") + ? "path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8") + : "&path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8"); + } + + HttpGet httpGet = new HttpGet(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet); + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0) { + // 出错 + if (ContentType.TEXT_PLAIN.getMimeType() + .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + } + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } finally { + httpGet.releaseConnection(); + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeJoddHttpRequestExecutor.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeJoddHttpRequestExecutor.java new file mode 100644 index 0000000000..7f24674d9f --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeJoddHttpRequestExecutor.java @@ -0,0 +1,62 @@ +package me.chanjar.weixin.open.executor; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.net.MimeTypes; +import jodd.util.StringPool; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; +import org.apache.commons.lang3.StringUtils; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.UUID; + +/** + * @author yqx + * @date 2018-09-13 + */ +public class MaQrCodeJoddHttpRequestExecutor extends MaQrCodeRequestExecutor { + public MaQrCodeJoddHttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public File execute(String uri, WxMaQrcodeParam qrcodeParam, WxType wxType) throws WxErrorException, IOException { + if (qrcodeParam != null && StringUtils.isNotBlank(qrcodeParam.getPagePath())) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") + ? "path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8") + : "&path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8"); + } + + + HttpRequest request = HttpRequest.get(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + + HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String contentTypeHeader = response.header("Content-Type"); + if (MimeTypes.MIME_TEXT_PLAIN.equals(contentTypeHeader)) { + String responseContent = response.bodyText(); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + try (InputStream inputStream = new ByteArrayInputStream(response.bodyBytes())) { + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeOkhttpRequestExecutor.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeOkhttpRequestExecutor.java new file mode 100644 index 0000000000..4b8a754502 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeOkhttpRequestExecutor.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.open.executor; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.UUID; + +/** + * @author yqx + * @date 2018-09-13 + */ +public class MaQrCodeOkhttpRequestExecutor extends MaQrCodeRequestExecutor { + public MaQrCodeOkhttpRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public File execute(String uri, WxMaQrcodeParam qrcodeParam, WxType wxType) throws WxErrorException, IOException { + if (qrcodeParam != null && StringUtils.isNotBlank(qrcodeParam.getPagePath())) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") + ? "path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8") + : "&path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8"); + } + + OkHttpClient client = requestHttp.getRequestHttpClient(); + Request request = new Request.Builder().url(uri).get().build(); + Response response = client.newCall(request).execute(); + String contentTypeHeader = response.header("Content-Type"); + if ("text/plain".equals(contentTypeHeader)) { + String responseContent = response.body().string(); + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP)); + } + + try (InputStream inputStream = response.body().byteStream()) { + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } + + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeRequestExecutor.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeRequestExecutor.java new file mode 100644 index 0000000000..dfaec08565 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/MaQrCodeRequestExecutor.java @@ -0,0 +1,45 @@ +package me.chanjar.weixin.open.executor; + +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; + +/** + * 获得小程序体验QrCode图片 请求执行器. + * + * @author yqx + * @date 2018-09-13 + */ +public abstract class MaQrCodeRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MaQrCodeRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, WxMaQrcodeParam data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) throws WxErrorException { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MaQrCodeApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new MaQrCodeJoddHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new MaQrCodeOkhttpRequestExecutor(requestHttp); + default: + throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("不支持的http框架").build()); + } + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/WxOpenCryptUtil.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/WxOpenCryptUtil.java new file mode 100644 index 0000000000..b507e0daa4 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/WxOpenCryptUtil.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.open.util; + +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; + +/** + * @author 007 + */ +public class WxOpenCryptUtil extends me.chanjar.weixin.common.util.crypto.WxCryptUtil { + /** + * 构造函数 + * + * @param wxOpenConfigStorage + */ + public WxOpenCryptUtil(WxOpenConfigStorage wxOpenConfigStorage) { + /* + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appId 公众平台appid + */ + String encodingAesKey = wxOpenConfigStorage.getComponentAesKey(); + String token = wxOpenConfigStorage.getComponentToken(); + String appId = wxOpenConfigStorage.getComponentAppId(); + + this.token = token; + this.appidOrCorpid = appId; + this.aesKey = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey)); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java new file mode 100644 index 0000000000..c774d8f047 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java @@ -0,0 +1,49 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.result.WxFastMaAccountBasicInfoResult; + +import java.lang.reflect.Type; + +/** + * . + * + * @author Hipple + * @since 2019/1/23 15:02 + */ +public class WxFastMaAccountBasicInfoGsonAdapter implements JsonDeserializer { + @Override + public WxFastMaAccountBasicInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) + throws JsonParseException { + WxFastMaAccountBasicInfoResult accountBasicInfo = new WxFastMaAccountBasicInfoResult(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + accountBasicInfo.setAppId(GsonHelper.getString(jsonObject, "appid")); + accountBasicInfo.setAccountType(GsonHelper.getInteger(jsonObject, "account_type")); + accountBasicInfo.setPrincipalType(GsonHelper.getInteger(jsonObject, "principal_type")); + accountBasicInfo.setPrincipalName(GsonHelper.getString(jsonObject, "principal_name")); + accountBasicInfo.setRealnameStatus(GsonHelper.getInteger(jsonObject, "realname_status")); + + WxFastMaAccountBasicInfoResult.WxVerifyInfo verifyInfo = WxOpenGsonBuilder.create() + .fromJson(jsonObject.get("wx_verify_info"), + new TypeToken() { + }.getType()); + accountBasicInfo.setWxVerifyInfo(verifyInfo); + + WxFastMaAccountBasicInfoResult.SignatureInfo signatureInfo = WxOpenGsonBuilder.create() + .fromJson(jsonObject.get("signature_info"), + new TypeToken() { + }.getType()); + accountBasicInfo.setSignatureInfo(signatureInfo); + + WxFastMaAccountBasicInfoResult.HeadImageInfo headImageInfo = WxOpenGsonBuilder.create() + .fromJson(jsonObject.get("head_image_info"), + new TypeToken() { + }.getType()); + accountBasicInfo.setHeadImageInfo(headImageInfo); + + return accountBasicInfo; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizationInfoGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizationInfoGsonAdapter.java new file mode 100644 index 0000000000..69df0647c8 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizationInfoGsonAdapter.java @@ -0,0 +1,45 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +/** + * @author 007 + */ +public class WxOpenAuthorizationInfoGsonAdapter implements JsonDeserializer { + @Override + public WxOpenAuthorizationInfo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxOpenAuthorizationInfo authorizationInfo = new WxOpenAuthorizationInfo(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + authorizationInfo.setAuthorizerAppid(GsonHelper.getString(jsonObject, "authorizer_appid")); + authorizationInfo.setAuthorizerAccessToken(GsonHelper.getString(jsonObject, "authorizer_access_token")); + authorizationInfo.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in")); + authorizationInfo.setAuthorizerRefreshToken(GsonHelper.getString(jsonObject, "authorizer_refresh_token")); + List funcInfo = new ArrayList<>(); + JsonArray jsonArray = GsonHelper.getAsJsonArray(jsonObject.get("func_info")); + if (jsonArray != null && !jsonArray.isJsonNull()) { + for (int i = 0; i < jsonArray.size(); i++) { + jsonObject = jsonArray.get(i).getAsJsonObject(); + if (jsonObject == null || jsonObject.isJsonNull()) { + continue; + } + jsonObject = jsonObject.getAsJsonObject("funcscope_category"); + if (jsonObject == null || jsonObject.isJsonNull()) { + continue; + } + Integer id = GsonHelper.getInteger(jsonObject, "id"); + if (id == null) { + continue; + } + funcInfo.add(id); + } + } + authorizationInfo.setFuncInfo(funcInfo); + return authorizationInfo; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerAccessTokenGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerAccessTokenGsonAdapter.java new file mode 100644 index 0000000000..6932d25736 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerAccessTokenGsonAdapter.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; + +import java.lang.reflect.Type; + +/** + * @author 007 + */ +public class WxOpenAuthorizerAccessTokenGsonAdapter implements JsonDeserializer { + @Override + public WxOpenAuthorizerAccessToken deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxOpenAuthorizerAccessToken authorizerAccessToken = new WxOpenAuthorizerAccessToken(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + authorizerAccessToken.setAuthorizerAccessToken(GsonHelper.getString(jsonObject, "authorizer_access_token")); + authorizerAccessToken.setAuthorizerRefreshToken(GsonHelper.getString(jsonObject, "authorizer_refresh_token")); + authorizerAccessToken.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in")); + return authorizerAccessToken; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoGsonAdapter.java new file mode 100644 index 0000000000..2fb4e7957e --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoGsonAdapter.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo; + +import java.lang.reflect.Type; +import java.util.Map; + +/** + * @author 007 + */ +public class WxOpenAuthorizerInfoGsonAdapter implements JsonDeserializer { + @Override + public WxOpenAuthorizerInfo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxOpenAuthorizerInfo authorizationInfo = new WxOpenAuthorizerInfo(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + authorizationInfo.setNickName(GsonHelper.getString(jsonObject, "nick_name")); + authorizationInfo.setHeadImg(GsonHelper.getString(jsonObject, "head_img")); + authorizationInfo.setUserName(GsonHelper.getString(jsonObject, "user_name")); + authorizationInfo.setPrincipalName(GsonHelper.getString(jsonObject, "principal_name")); + authorizationInfo.setAlias(GsonHelper.getString(jsonObject, "alias")); + authorizationInfo.setQrcodeUrl(GsonHelper.getString(jsonObject, "qrcode_url")); + authorizationInfo.setSignature(GsonHelper.getString(jsonObject, "signature")); + + if (jsonObject.has("service_type_info")) { + authorizationInfo.setServiceTypeInfo(GsonHelper.getInteger(jsonObject.getAsJsonObject("service_type_info"), "id")); + } + if (jsonObject.has("verify_type_info")) { + authorizationInfo.setVerifyTypeInfo(GsonHelper.getInteger(jsonObject.getAsJsonObject("verify_type_info"), "id")); + } + Map businessInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("business_info"), + new TypeToken>() { + }.getType()); + authorizationInfo.setBusinessInfo(businessInfo); + if (jsonObject.has("MiniProgramInfo")) { + WxOpenAuthorizerInfo.MiniProgramInfo miniProgramInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("MiniProgramInfo"), + new TypeToken() { + }.getType()); + authorizationInfo.setMiniProgramInfo(miniProgramInfo); + } + return authorizationInfo; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoResultGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoResultGsonAdapter.java new file mode 100644 index 0000000000..112ec9f478 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerInfoResultGsonAdapter.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.open.util.json; + +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo; +import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult; + +/** + * @author 007 + */ +public class WxOpenAuthorizerInfoResultGsonAdapter implements JsonDeserializer { + @Override + public WxOpenAuthorizerInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxOpenAuthorizerInfoResult authorizerInfoResult = new WxOpenAuthorizerInfoResult(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + WxOpenAuthorizationInfo authorizationInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("authorization_info"), + new TypeToken() { + }.getType()); + + authorizerInfoResult.setAuthorizationInfo(authorizationInfo); + WxOpenAuthorizerInfo authorizerInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("authorizer_info"), + new TypeToken() { + }.getType()); + + authorizerInfoResult.setAuthorizerInfo(authorizerInfo); + return authorizerInfoResult; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerListResultGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerListResultGsonAdapter.java new file mode 100644 index 0000000000..68e6d92e4d --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerListResultGsonAdapter.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerListResult; + +import java.lang.reflect.Type; +import java.util.*; + +/** + * @author robgao + * @Email 315789501@qq.com + */ +public class WxOpenAuthorizerListResultGsonAdapter implements JsonDeserializer { + + private static final String AUTHORIZER_APPID = "authorizer_appid"; + private static final String REFRESH_TOKEN = "refresh_token"; + private static final String AUTH_TIME = "auth_time"; + + @Override + public WxOpenAuthorizerListResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + WxOpenAuthorizerListResult wxOpenAuthorizerListResult = new WxOpenAuthorizerListResult(); + wxOpenAuthorizerListResult.setTotalCount(GsonHelper.getInteger(jsonObject, "total_count").intValue()); + + List> list = new ArrayList<>(); + Iterator jsonElementIterator = jsonObject.getAsJsonArray("list").iterator(); + + while (jsonElementIterator.hasNext()) { + JsonObject authorizer = jsonElementIterator.next().getAsJsonObject(); + Map authorizerMap = new HashMap<>(10); + + authorizerMap.put(AUTHORIZER_APPID, GsonHelper.getString(authorizer, AUTHORIZER_APPID)); + authorizerMap.put(REFRESH_TOKEN, GsonHelper.getString(authorizer, REFRESH_TOKEN)); + authorizerMap.put(AUTH_TIME, GsonHelper.getString(authorizer, AUTH_TIME)); + list.add(authorizerMap); + } + wxOpenAuthorizerListResult.setList(list); + return wxOpenAuthorizerListResult; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerOptionResultGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerOptionResultGsonAdapter.java new file mode 100644 index 0000000000..8a2d379c31 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenAuthorizerOptionResultGsonAdapter.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult; + +import java.lang.reflect.Type; + +/** + * @author 007 + */ +public class WxOpenAuthorizerOptionResultGsonAdapter implements JsonDeserializer { + @Override + public WxOpenAuthorizerOptionResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxOpenAuthorizerOptionResult authorizerOptionResult = new WxOpenAuthorizerOptionResult(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + authorizerOptionResult.setAuthorizerAppid(GsonHelper.getString(jsonObject, "authorizer_appid")); + authorizerOptionResult.setOptionName(GsonHelper.getString(jsonObject, "option_name")); + authorizerOptionResult.setOptionValue(GsonHelper.getString(jsonObject, "option_value")); + return authorizerOptionResult; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenComponentAccessTokenGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenComponentAccessTokenGsonAdapter.java new file mode 100644 index 0000000000..10ed85083c --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenComponentAccessTokenGsonAdapter.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; + +import java.lang.reflect.Type; + +/** + * @author 007 + */ +public class WxOpenComponentAccessTokenGsonAdapter implements JsonDeserializer { + @Override + public WxOpenComponentAccessToken deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxOpenComponentAccessToken componentAccessToken = new WxOpenComponentAccessToken(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + componentAccessToken.setComponentAccessToken(GsonHelper.getString(jsonObject, "component_access_token")); + componentAccessToken.setExpiresIn(GsonHelper.getPrimitiveInteger(jsonObject, "expires_in")); + return componentAccessToken; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java new file mode 100644 index 0000000000..325383ebf0 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo; +import me.chanjar.weixin.open.bean.result.*; + +/** + * @author 007 + */ +public class WxOpenGsonBuilder { + + private static final GsonBuilder INSTANCE = new GsonBuilder(); + + static { + INSTANCE.disableHtmlEscaping(); + INSTANCE.registerTypeAdapter(WxOpenComponentAccessToken.class, new WxOpenComponentAccessTokenGsonAdapter()); + INSTANCE.registerTypeAdapter(WxOpenAuthorizerAccessToken.class, new WxOpenAuthorizerAccessTokenGsonAdapter()); + INSTANCE.registerTypeAdapter(WxOpenAuthorizationInfo.class, new WxOpenAuthorizationInfoGsonAdapter()); + INSTANCE.registerTypeAdapter(WxOpenAuthorizerInfo.class, new WxOpenAuthorizerInfoGsonAdapter()); + INSTANCE.registerTypeAdapter(WxOpenQueryAuthResult.class, new WxOpenQueryAuthResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxOpenAuthorizerInfoResult.class, new WxOpenAuthorizerInfoResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxOpenAuthorizerOptionResult.class, new WxOpenAuthorizerOptionResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxFastMaAccountBasicInfoResult.class, new WxFastMaAccountBasicInfoGsonAdapter()); + INSTANCE.registerTypeAdapter(WxOpenAuthorizerListResult.class, new WxOpenAuthorizerListResultGsonAdapter()); + + } + + public static Gson create() { + return INSTANCE.create(); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenQueryAuthResultGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenQueryAuthResultGsonAdapter.java new file mode 100644 index 0000000000..78791603e2 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenQueryAuthResultGsonAdapter.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.open.util.json; + +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; +import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult; + +/** + * @author 007 + */ +public class WxOpenQueryAuthResultGsonAdapter implements JsonDeserializer { + @Override + public WxOpenQueryAuthResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxOpenQueryAuthResult queryAuthResult = new WxOpenQueryAuthResult(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + WxOpenAuthorizationInfo authorizationInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("authorization_info"), + new TypeToken() { + }.getType()); + + queryAuthResult.setAuthorizationInfo(authorizationInfo); + return queryAuthResult; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/xml/XStreamTransformer.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/xml/XStreamTransformer.java new file mode 100644 index 0000000000..0065748ab9 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/xml/XStreamTransformer.java @@ -0,0 +1,93 @@ +package me.chanjar.weixin.open.util.xml; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.thoughtworks.xstream.XStream; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; + +/** + * @author 007 + */ +public class XStreamTransformer { + private static final Map, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>(); + + static { + registerClass(WxOpenXmlMessage.class); + } + + /** + * xml -> pojo. + */ + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, String xml) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); + return object; + } + + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, InputStream is) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); + return object; + } + + /** + * pojo -> xml. + */ + public static String toXml(Class clazz, T object) { + return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); + } + + /** + * 注册扩展消息的解析器. + * + * @param clz 类型 + * @param xStream xml解析器 + */ + public static void register(Class clz, XStream xStream) { + CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); + } + + /** + * 会自动注册该类及其子类. + * + * @param clz 要注册的类 + */ + private static void registerClass(Class clz) { + XStream xstream = XStreamInitializer.getInstance(); + + xstream.processAnnotations(clz); + xstream.processAnnotations(getInnerClasses(clz)); + if (clz.equals(WxOpenXmlMessage.class)) { + // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId + xstream.aliasField("MsgID", WxOpenXmlMessage.class, "msgId"); + } + + register(clz, xstream); + } + + private static Class[] getInnerClasses(Class clz) { + Class[] innerClasses = clz.getClasses(); + if (innerClasses == null) { + return null; + } + + List> result = new ArrayList<>(); + result.addAll(Arrays.asList(innerClasses)); + for (Class inner : innerClasses) { + Class[] innerClz = getInnerClasses(inner); + if (innerClz == null) { + continue; + } + + result.addAll(Arrays.asList(innerClz)); + } + + return result.toArray(new Class[0]); + } +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImplTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImplTest.java new file mode 100644 index 0000000000..47a5069f24 --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImplTest.java @@ -0,0 +1,160 @@ +package me.chanjar.weixin.open.api.impl; + +import org.testng.annotations.Test; + +/** + * 单元测试类. + * + * @author Binary Wang + * @date 2020-06-06 + */ +public class WxOpenComponentServiceImplTest { + + @Test + public void testGetWxMpServiceByAppid() { + } + + @Test + public void testGetWxMaServiceByAppid() { + } + + @Test + public void testGetWxFastMaServiceByAppid() { + } + + @Test + public void testGetWxOpenService() { + } + + @Test + public void testGetWxOpenConfigStorage() { + } + + @Test + public void testCheckSignature() { + } + + @Test + public void testGetComponentAccessToken() { + } + + @Test + public void testPost() { + } + + @Test + public void testTestPost() { + } + + @Test + public void testGet() { + } + + @Test + public void testTestGet() { + } + + @Test + public void testGetPreAuthUrl() { + } + + @Test + public void testTestGetPreAuthUrl() { + } + + @Test + public void testGetMobilePreAuthUrl() { + } + + @Test + public void testTestGetMobilePreAuthUrl() { + } + + @Test + public void testRoute() { + } + + @Test + public void testGetQueryAuth() { + } + + @Test + public void testGetAuthorizerInfo() { + } + + @Test + public void testGetAuthorizerList() { + } + + @Test + public void testGetAuthorizerOption() { + } + + @Test + public void testSetAuthorizerOption() { + } + + @Test + public void testGetAuthorizerAccessToken() { + } + + @Test + public void testOauth2getAccessToken() { + } + + @Test + public void testTestCheckSignature() { + } + + @Test + public void testOauth2refreshAccessToken() { + } + + @Test + public void testOauth2buildAuthorizationUrl() { + } + + @Test + public void testMiniappJscode2Session() { + } + + @Test + public void testGetTemplateDraftList() { + } + + @Test + public void testGetTemplateList() { + } + + @Test + public void testAddToTemplate() { + } + + @Test + public void testDeleteTemplate() { + } + + @Test + public void testCreateOpenAccount() { + } + + @Test + public void testBindOpenAccount() { + } + + @Test + public void testUnbindOpenAccount() { + } + + @Test + public void testGetOpenAccount() { + } + + @Test + public void testFastRegisterWeapp() { + } + + @Test + public void testFastRegisterWeappSearch() { + } +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImplTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImplTest.java new file mode 100644 index 0000000000..e5a255be1d --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImplTest.java @@ -0,0 +1,218 @@ +package me.chanjar.weixin.open.api.impl; + +import org.testng.annotations.Test; + +/** + * @author Binary Wang + * @date 2020-06-06 + */ +public class WxOpenFastMaServiceImplTest { + + @Test + public void testInitHttp() { + } + + @Test + public void testGetRequestHttpClient() { + } + + @Test + public void testGetRequestHttpProxy() { + } + + @Test + public void testGetRequestType() { + } + + @Test + public void testDoGetAccessTokenRequest() { + } + + @Test + public void testGetRequestHttp() { + } + + @Test + public void testGetPaidUnionId() { + } + + @Test + public void testJsCode2SessionInfo() { + } + + @Test + public void testSetDynamicData() { + } + + @Test + public void testCheckSignature() { + } + + @Test + public void testGetAccessToken() { + } + + @Test + public void testTestGetAccessToken() { + } + + @Test + public void testGet() { + } + + @Test + public void testPost() { + } + + @Test + public void testTestPost() { + } + + @Test + public void testExecute() { + } + + @Test + public void testExtractAccessToken() { + } + + @Test + public void testGetWxMaConfig() { + } + + @Test + public void testSetWxMaConfig() { + } + + @Test + public void testSetRetrySleepMillis() { + } + + @Test + public void testSetMaxRetryTimes() { + } + + @Test + public void testGetMsgService() { + } + + @Test + public void testGetMediaService() { + } + + @Test + public void testGetUserService() { + } + + @Test + public void testGetQrcodeService() { + } + + @Test + public void testGetTemplateService() { + } + + @Test + public void testGetSubscribeService() { + } + + @Test + public void testGetAnalysisService() { + } + + @Test + public void testGetCodeService() { + } + + @Test + public void testGetJsapiService() { + } + + @Test + public void testGetSettingService() { + } + + @Test + public void testGetShareService() { + } + + @Test + public void testGetRunService() { + } + + @Test + public void testGetSecCheckService() { + } + + @Test + public void testGetPluginService() { + } + + @Test + public void testGetExpressService() { + } + + @Test + public void testGetCloudService() { + } + + @Test + public void testGetLiveService() { + } + + @Test + public void testTestGetWxMaConfig() { + } + + @Test + public void testTestGetAccessToken1() { + } + + @Test + public void testGetAccountBasicInfo() { + } + + @Test + public void testSetNickname() { + } + + @Test + public void testQuerySetNicknameStatus() { + } + + @Test + public void testCheckWxVerifyNickname() { + } + + @Test + public void testModifyHeadImage() { + } + + @Test + public void testModifySignature() { + } + + @Test + public void testComponentRebindAdmin() { + } + + @Test + public void testGetAllCategories() { + } + + @Test + public void testAddCategory() { + } + + @Test + public void testDeleteCategory() { + } + + @Test + public void testGetCategory() { + } + + @Test + public void testModifyCategory() { + } +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorageTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorageTest.java new file mode 100644 index 0000000000..26a30a2040 --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorageTest.java @@ -0,0 +1,131 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import redis.clients.jedis.JedisPool; + +public class WxOpenInRedisConfigStorageTest { + + private WxOpenConfigStorage wxOpenConfigStorage; + + private JedisPool pool; + + @BeforeClass + public void setWxOpenConfigStorage(){ + pool = new JedisPool("127.0.0.1", 6379); + this.wxOpenConfigStorage = new WxOpenInRedisConfigStorage(pool); + this.wxOpenConfigStorage.setWxOpenInfo("ComponentAppId", "ComponentAppSecret", "ComponentToken","ComponentAesKey"); + this.wxOpenConfigStorage.setComponentVerifyTicket("ComponentVerifyTicket"); + } + + @AfterClass + public void clearResource(){ + pool.close(); + } + + @Test + public void testGetComponentVerifyTicket() { + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "ComponentVerifyTicket"); + } + + @Test + public void testSetComponentVerifyTicket() { + this.wxOpenConfigStorage.setComponentVerifyTicket("new ComponentVerifyTicket"); + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "new ComponentVerifyTicket"); + } + + @Test + public void testIsComponentAccessTokenExpired() { + String responseContent = "{\"component_access_token\": \"new componentAccessToken\", \"expires_in\": 10000}"; + WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateComponentAccessToken(componentAccessToken); + boolean expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireComponentAccessToken(); + expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, true); + + } + + @Test + public void testGetAuthorizerRefreshToken() { + String appid = "appid1"; + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 1"); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 1"); + + + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 2"); + authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 2"); + } + + @Test + public void testGetAuthorizerAccessToken() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + } + + @Test + public void testIsAuthorizerAccessTokenExpired() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + + boolean expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireAuthorizerAccessToken(appid); + expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, true); + } + + + @Test + public void testGetJsapiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateJsapiTicket(appid, "jsapiTicket", 100000); + String jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, "jsapiTicket"); + + boolean expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireJsapiTicket(appid); + jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, null); + + expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, true); + } + + @Test + public void testGetCardApiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateCardApiTicket(appid, "new CardApiTicket", 10000); + String cardApiTicket = this.wxOpenConfigStorage.getCardApiTicket(appid); + Assert.assertEquals(cardApiTicket, "new CardApiTicket"); + + boolean expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireCardApiTicket(appid); + expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, true); + } +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorageTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorageTest.java new file mode 100644 index 0000000000..7168d93f1b --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorageTest.java @@ -0,0 +1,129 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.TransportMode; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class WxOpenInRedissonConfigStorageTest { + + private WxOpenConfigStorage wxOpenConfigStorage; + + @BeforeClass + public void setWxOpenConfigStorage(){ + Config config = new Config(); + config.useSingleServer().setAddress("redis://127.0.0.1:6379") + .setDatabase(0); + config.setTransportMode(TransportMode.NIO); + RedissonClient redisson = Redisson.create(config); + this.wxOpenConfigStorage = new WxOpenInRedissonConfigStorage(redisson); + this.wxOpenConfigStorage.setWxOpenInfo("ComponentAppId", "ComponentAppSecret", "ComponentToken","ComponentAesKey"); + this.wxOpenConfigStorage.setComponentVerifyTicket("ComponentVerifyTicket"); + } + + @Test + public void testGetComponentVerifyTicket() { + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "ComponentVerifyTicket"); + } + + @Test + public void testSetComponentVerifyTicket() { + this.wxOpenConfigStorage.setComponentVerifyTicket("new ComponentVerifyTicket"); + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "new ComponentVerifyTicket"); + } + + @Test + public void testIsComponentAccessTokenExpired() { + String responseContent = "{\"component_access_token\": \"new componentAccessToken\", \"expires_in\": 10000}"; + WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateComponentAccessToken(componentAccessToken); + boolean expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireComponentAccessToken(); + expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, true); + + } + + @Test + public void testGetAuthorizerRefreshToken() { + String appid = "appid1"; + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 1"); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 1"); + + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 2"); + authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 2"); + } + + @Test + public void testGetAuthorizerAccessToken() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + } + + @Test + public void testIsAuthorizerAccessTokenExpired() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + + boolean expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireAuthorizerAccessToken(appid); + expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, true); + } + + + @Test + public void testGetJsapiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateJsapiTicket(appid, "jsapiTicket", 100000); + String jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, "jsapiTicket"); + + boolean expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireJsapiTicket(appid); + jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, null); + + expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, true); + } + + @Test + public void testGetCardApiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateCardApiTicket(appid, "new CardApiTicket", 10000); + String cardApiTicket = this.wxOpenConfigStorage.getCardApiTicket(appid); + Assert.assertEquals(cardApiTicket, "new CardApiTicket"); + + boolean expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireCardApiTicket(appid); + expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, true); + } +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImplTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImplTest.java new file mode 100644 index 0000000000..5e5f3e6682 --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImplTest.java @@ -0,0 +1,334 @@ +package me.chanjar.weixin.open.api.impl; + +import org.testng.annotations.Test; + +/** + * @author Binary Wang + * @date 2020-06-06 + */ +public class WxOpenMaServiceImplTest { + + @Test + public void testInitHttp() { + } + + @Test + public void testGetRequestHttpClient() { + } + + @Test + public void testGetRequestHttpProxy() { + } + + @Test + public void testGetRequestType() { + } + + @Test + public void testDoGetAccessTokenRequest() { + } + + @Test + public void testGetRequestHttp() { + } + + @Test + public void testGetPaidUnionId() { + } + + @Test + public void testJsCode2SessionInfo() { + } + + @Test + public void testSetDynamicData() { + } + + @Test + public void testCheckSignature() { + } + + @Test + public void testGetAccessToken() { + } + + @Test + public void testTestGetAccessToken() { + } + + @Test + public void testGet() { + } + + @Test + public void testPost() { + } + + @Test + public void testTestPost() { + } + + @Test + public void testExecute() { + } + + @Test + public void testExtractAccessToken() { + } + + @Test + public void testGetWxMaConfig() { + } + + @Test + public void testSetWxMaConfig() { + } + + @Test + public void testSetRetrySleepMillis() { + } + + @Test + public void testSetMaxRetryTimes() { + } + + @Test + public void testGetMsgService() { + } + + @Test + public void testGetMediaService() { + } + + @Test + public void testGetUserService() { + } + + @Test + public void testGetQrcodeService() { + } + + @Test + public void testGetTemplateService() { + } + + @Test + public void testGetSubscribeService() { + } + + @Test + public void testGetAnalysisService() { + } + + @Test + public void testGetCodeService() { + } + + @Test + public void testGetJsapiService() { + } + + @Test + public void testGetSettingService() { + } + + @Test + public void testGetShareService() { + } + + @Test + public void testGetRunService() { + } + + @Test + public void testGetSecCheckService() { + } + + @Test + public void testGetPluginService() { + } + + @Test + public void testGetExpressService() { + } + + @Test + public void testGetCloudService() { + } + + @Test + public void testGetLiveService() { + } + + @Test + public void testTestJsCode2SessionInfo() { + } + + @Test + public void testTestGetWxMaConfig() { + } + + @Test + public void testTestGetAccessToken1() { + } + + @Test + public void testGetDomain() { + } + + @Test + public void testModifyDomain() { + } + + @Test + public void testGetWebViewDomain() { + } + + @Test + public void testGetWebViewDomainInfo() { + } + + @Test + public void testSetWebViewDomain() { + } + + @Test + public void testSetWebViewDomainInfo() { + } + + @Test + public void testGetAccountBasicInfo() { + } + + @Test + public void testBindTester() { + } + + @Test + public void testUnbindTester() { + } + + @Test + public void testUnbindTesterByUserStr() { + } + + @Test + public void testGetTesterList() { + } + + @Test + public void testChangeWxaSearchStatus() { + } + + @Test + public void testGetWxaSearchStatus() { + } + + @Test + public void testGetShowWxaItem() { + } + + @Test + public void testUpdateShowWxaItem() { + } + + @Test + public void testCodeCommit() { + } + + @Test + public void testGetTestQrcode() { + } + + @Test + public void testGetCategoryList() { + } + + @Test + public void testGetPageList() { + } + + @Test + public void testSubmitAudit() { + } + + @Test + public void testGetAuditStatus() { + } + + @Test + public void testGetLatestAuditStatus() { + } + + @Test + public void testReleaseAudited() { + } + + @Test + public void testChangeVisitStatus() { + } + + @Test + public void testRevertCodeRelease() { + } + + @Test + public void testUndoCodeAudit() { + } + + @Test + public void testGetSupportVersion() { + } + + @Test + public void testGetSupportVersionInfo() { + } + + @Test + public void testSetSupportVersion() { + } + + @Test + public void testSetSupportVersionInfo() { + } + + @Test + public void testGrayRelease() { + } + + @Test + public void testRevertGrayRelease() { + } + + @Test + public void testGetGrayReleasePlan() { + } + + @Test + public void testQueryQuota() { + } + + @Test + public void testSpeedAudit() { + } + + @Test + public void testAddQrcodeJump() { + } + + @Test + public void testGetQrcodeJump() { + } + + @Test + public void testDownloadQrcodeJump() { + } + + @Test + public void testDeleteQrcodeJump() { + } + + @Test + public void testPublishQrcodeJump() { + } +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResultTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResultTest.java new file mode 100644 index 0000000000..851620f9ed --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResultTest.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.open.bean.result; + +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + + +public class WxFastMaAccountBasicInfoResultTest { + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + "\t\"appid\": \"wxdc685123d955453\",\n" + + " \"account_type\": 2,\n" + + "\t\"principal_type\": 1,\n" + + "\t\"principal_name\": \"深圳市腾讯计算机系统有限公司\",\n" + + " \"realname_status\": 1,\n" + + " \"wx_verify_info\": {\n" + + " \"qualification_verify\": true,\n" + + " \"naming_verify\": true,\n" + + " \"annual_review\": true,\n" + + " \"annual_review_begin_time\": 1550490981,\n" + + " \"annual_review_end_time\": 1558266981\n" + + " },\n" + + " \"signature_info\": {\n" + + " \"signature\": \"功能介绍\",\n" + + " \"modify_used_count\": 1,\n" + + " \"modify_quota\": 5\n" + + " },\n" + + "\t\"head_image_info\": {\n" + + " \"head_image_url\": \"http://mmbiz.qpic.cn/mmbiz/a5icZrUmbV8p5jb6RZ8aYfjfS2AVle8URwBt8QIu6XbGewB9wiaWYWkPwq4R7pfdsFibuLkic16UcxDSNYtB8HnC1Q/0\",\n" + + " \"modify_used_count\": 3,\n" + + " \"modify_quota\": 5\n" + + " }\n" + + "}"; + + WxFastMaAccountBasicInfoResult res = WxOpenGsonBuilder.create().fromJson(json, WxFastMaAccountBasicInfoResult.class); + + assertNotNull(res); + assertNotNull(res.getAppId()); + assertNotNull(res.getSignatureInfo().getModifyQuota()); + assertNotNull(res.getHeadImageInfo().getHeadImageUrl()); + assertNotNull(res.getWxVerifyInfo().getNamingVerify()); + assertTrue(res.getWxVerifyInfo().getNamingVerify()); + System.out.println(res); + } + +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResultTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResultTest.java new file mode 100644 index 0000000000..9189000c3f --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResultTest.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.open.bean.result; + +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + + +public class WxFastMaBeenSetCategoryResultTest { + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\":\"ok\",\n" + + " \"categories\": [\n" + + " {\n" + + " \"first\": 8,\n" + + " \"first_name\": \"教育\",\n" + + " \"second\": 39,\n" + + " \"second_name\": \"出国移民\",\n" + + " \"audit_status\": 1,\n" + + " \"audit_reason\": \"不通过啊啊\"\n" + + " }\n" + + " ],\n" + + "\t\"limit\": 5,\n" + + " \"quota\": 4,\n" + + " \"category_limit\": 20\n" + + "}"; + + WxFastMaBeenSetCategoryResult res = WxOpenGsonBuilder.create().fromJson(json, WxFastMaBeenSetCategoryResult.class); + + assertNotNull(res); + assertTrue(res.getCategories().size() > 0); + assertNotNull(res.getCategories().get(0)); + assertNotNull(res.getCategories().get(0).getFirstName()); + System.out.println(res); + } + +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResultTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResultTest.java new file mode 100644 index 0000000000..11ae649699 --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResultTest.java @@ -0,0 +1,79 @@ +package me.chanjar.weixin.open.bean.result; + +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; + + +public class WxFastMaCanSetCategoryResultTest { + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"categories_list\": {\n" + + " \"categories\": [\n" + + " {\n" + + " \"id\": 1, \n" + + " \"name\": \"快递业与邮政\", \n" + + " \"level\": 1, \n" + + " \"father\": 0, \n" + + " \"children\": [\n" + + " 2, \n" + + " 5, \n" + + " 556, \n" + + " 558, \n" + + " 1033\n" + + " ], \n" + + " \"sensitive_type\": 0, \n" + + " \"type_list\": [ ], \n" + + " \"qualify\": {\n" + + " \"exter_list\": [ ], \n" + + " \"remark\": \"\"\n" + + " }, \n" + + " \"available_api_list\": [ ], \n" + + " \"apis\": [ ], \n" + + " \"available_for_plugin\": true\n" + + " }, \n" + + " {\n" + + " \"id\": 8, \n" + + " \"name\": \"教育\", \n" + + " \"level\": 1, \n" + + " \"father\": 0, \n" + + " \"children\": [\n" + + " 9, \n" + + " 590, \n" + + " 592, \n" + + " 27, \n" + + " 32, \n" + + " 37, \n" + + " 39, \n" + + " 578, \n" + + " 580, \n" + + " 582, \n" + + " 1043\n" + + " ], \n" + + " \"sensitive_type\": 0, \n" + + " \"type_list\": [ ], \n" + + " \"qualify\": {\n" + + " \"exter_list\": [ ], \n" + + " \"remark\": \"\"\n" + + " }, \n" + + " \"is_hidden\": false, \n" + + " \"available_api_list\": [ ], \n" + + " \"type\": \"NORMAL\", \n" + + " \"apis\": [ ], \n" + + " \"available_for_plugin\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + WxFastMaCanSetCategoryResult res = WxOpenGsonBuilder.create().fromJson(json, WxFastMaCanSetCategoryResult.class); + + assertNotNull(res); + assertNotNull(res.getCategoriesList()); + System.out.println(res); + } + +} diff --git a/weixin-java-open/src/test/resources/logback-test.xml b/weixin-java-open/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..e4a33acd88 --- /dev/null +++ b/weixin-java-open/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n + + + + + + + + diff --git a/weixin-java-open/src/test/resources/test-config.sample.xml b/weixin-java-open/src/test/resources/test-config.sample.xml new file mode 100644 index 0000000000..896969e438 --- /dev/null +++ b/weixin-java-open/src/test/resources/test-config.sample.xml @@ -0,0 +1,7 @@ + + 第三方平台appID + 第三方平台appsecret + 第三方平台Token + 第三方平台EncodingAESKey + 测试APPID + diff --git a/weixin-java-open/src/test/resources/testng.xml b/weixin-java-open/src/test/resources/testng.xml new file mode 100644 index 0000000000..8ade76f3e3 --- /dev/null +++ b/weixin-java-open/src/test/resources/testng.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index d0d50455f8..1a51f3aad2 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -3,14 +3,14 @@ xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - weixin-java-parent com.github.binarywang - 2.6.0 + wx-java + 3.9.0 4.0.0 weixin-java-pay - WeiXin Java Tools - PAY + WxJava - PAY Java SDK 微信支付 Java SDK @@ -19,29 +19,103 @@ weixin-java-common ${project.version} + + com.github.binarywang + qrcode-utils + org.jodd jodd-http - 3.7 - + compile + - com.github.binarywang - qrcode-utils - 1.0 + org.apache.commons + commons-lang3 + + commons-beanutils + commons-beanutils + 1.9.4 + + + org.bouncycastle + bcpkix-jdk15on + 1.65 + + + + ch.qos.logback + logback-classic + test + org.testng testng test + + org.assertj + assertj-guava + test + com.google.inject guice test + + org.projectlombok + lombok + + + + com.fasterxml.jackson.core + jackson-databind + 2.10.0.pr1 + + + com.google.code.gson + gson + + + joda-time + joda-time + compile + + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayApiData.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayApiData.java new file mode 100644 index 0000000000..1cf4c3c3de --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayApiData.java @@ -0,0 +1,62 @@ +package com.github.binarywang.wxpay.bean; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *
    + * 微信支付接口请求数据封装对象
    + * Created by Binary Wang on 2017-8-25.
    + * 
    + * + * @author Binary Wang + */ +@Data +@NoArgsConstructor +public class WxPayApiData { + /** + * 接口请求地址 + */ + private String url; + + /** + * 请求数据 + */ + private String requestData; + + /** + * 响应数据 + */ + private String responseData; + + /** + * 接口请求异常信息 + */ + private String exceptionMsg; + + /** + * Instantiates a new Wx pay api data. + * + * @param url 接口请求地址 + * @param requestData 请求数据 + * @param responseData 响应数据 + * @param exceptionMsg 接口请求异常信息 + */ + public WxPayApiData(String url, String requestData, String responseData, String exceptionMsg) { + this.url = url; + this.requestData = requestData; + this.responseData = responseData; + this.exceptionMsg = exceptionMsg; + } + + @Override + public String toString() { + if (this.exceptionMsg != null) { + return String.format("\n【请求地址】:%s\n【请求数据】:%s\n【异常信息】:%s", + this.url, this.requestData, this.exceptionMsg); + } + + return String.format("\n【请求地址】:%s\n【请求数据】:%s\n【响应数据】:%s", + this.url, this.requestData, this.responseData); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java deleted file mode 100644 index c72ddcb222..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.github.binarywang.wxpay.bean; - -import com.thoughtworks.xstream.annotations.XStreamAlias; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * 支付异步通知代金券详细 - */ -public class WxPayOrderNotifyCoupon implements Serializable { - /** - * @fields serialVersionUID - */ - private static final long serialVersionUID = -4165343733538156097L; - - @XStreamAlias("coupon_id") - private String couponId; - @XStreamAlias("coupon_type") - private String couponType; - @XStreamAlias("coupon_fee") - private Integer couponFee; - - public String getCouponId() { - return couponId; - } - - public void setCouponId(String couponId) { - this.couponId = couponId; - } - - public String getCouponType() { - return couponType; - } - - public void setCouponType(String couponType) { - this.couponType = couponType; - } - - public Integer getCouponFee() { - return couponFee; - } - - public void setCouponFee(Integer couponFee) { - this.couponFee = couponFee; - } - - public Map toMap(int index) { - Map map = new HashMap<>(); - map.put("coupon_id_" + index, this.getCouponId()); - map.put("coupon_type_" + index, this.getCouponType()); - map.put("coupon_fee_" + index, this.getCouponFee() + ""); - return map; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); - } -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java deleted file mode 100644 index 9d960c08e2..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.github.binarywang.wxpay.bean; - -import com.thoughtworks.xstream.XStream; -import com.thoughtworks.xstream.annotations.XStreamAlias; -import com.thoughtworks.xstream.annotations.XStreamConverter; -import com.thoughtworks.xstream.annotations.XStreamOmitField; - -import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.common.util.xml.XStreamInitializer; - -@XStreamAlias("xml") -public class WxPayOrderNotifyResponse { - @XStreamOmitField - private transient static final String FAIL = "FAIL"; - @XStreamOmitField - private transient static final String SUCCESS = "SUCCESS"; - - @XStreamAlias("return_code") - @XStreamConverter(value = XStreamCDataConverter.class) - private String returnCode; - @XStreamConverter(value = XStreamCDataConverter.class) - @XStreamAlias("return_msg") - private String returnMsg; - - public WxPayOrderNotifyResponse() { - super(); - } - - public WxPayOrderNotifyResponse(String returnCode, String returnMsg) { - super(); - this.returnCode = returnCode; - this.returnMsg = returnMsg; - } - - public static String fail(String msg) { - WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(FAIL, msg); - XStream xstream = XStreamInitializer.getInstance(); - xstream.autodetectAnnotations(true); - return xstream.toXML(response); - } - - public static String success(String msg) { - WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(SUCCESS, msg); - XStream xstream = XStreamInitializer.getInstance(); - xstream.autodetectAnnotations(true); - return xstream.toXML(response); - } - - public String getReturnCode() { - return returnCode; - } - - public void setReturnCode(String returnCode) { - this.returnCode = returnCode; - } - - public String getReturnMsg() { - return returnMsg; - } - - public void setReturnMsg(String returnMsg) { - this.returnMsg = returnMsg; - } -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java new file mode 100644 index 0000000000..24019fb914 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java @@ -0,0 +1,87 @@ +package com.github.binarywang.wxpay.bean.applyment; + +import com.github.binarywang.wxpay.bean.applyment.enums.ApplymentStateEnum; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 查询申请单状态返回对象信息 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class ApplymentStateQueryResult implements Serializable { + private static final long serialVersionUID = 6539090917423486409L; + /** + * 业务申请编号 + */ + @SerializedName("business_code") + private String businessCode; + /** + * 微信支付申请单号 + */ + @SerializedName("applyment_id") + private String applymentId; + /** + * 特约商户号 + */ + @SerializedName("sub_mchid") + private String subMchid; + /** + * 超级管理员签约链接 + */ + @SerializedName("sign_url") + private String signUrl; + + /** + * 申请单状态 + */ + @SerializedName("applyment_state") + private ApplymentStateEnum applymentState; + /** + * 申请状态描述 + */ + @SerializedName("applyment_state_msg") + private String applymentStateMsg; + /** + * 驳回原因详情 + */ + @SerializedName("audit_detail") + private List auditDetail; + + /** + * 驳回原因详情 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class AuditDetail { + /** + * 字段名 + */ + @SerializedName("field") + private String field; + /** + * 字段名称 + */ + @SerializedName("field_name") + private String fieldName; + /** + * 驳回原因 + */ + @SerializedName("reject_reason") + private String rejectReason; + + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ModifySettlementRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ModifySettlementRequest.java new file mode 100644 index 0000000000..8623b5cd67 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ModifySettlementRequest.java @@ -0,0 +1,56 @@ +package com.github.binarywang.wxpay.bean.applyment; + +import com.github.binarywang.wxpay.bean.applyment.enums.AccountTypeEnum; +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 修改结算账户请求对象 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class ModifySettlementRequest implements Serializable { + private static final long serialVersionUID = 4568552340365230872L; + /** + * 账户类型 + */ + @SerializedName("account_type") + private AccountTypeEnum accountType; + /** + * 开户银行 + */ + @SerializedName("account_bank") + private String accountBank; + /** + * 开户银行省市编码 + */ + @SerializedName("bank_address_code") + private String bankAddressCode; + /** + * 开户银行全称(含支行) + */ + @SerializedName("bank_name") + private String bankName; + /** + * 开户银行联行号 + */ + @SerializedName("bank_branch_id") + private String bankBranchId; + + /** + * 银行账号 + */ + @SpecEncrypt + @SerializedName("account_number") + private String accountNumber; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/SettlementInfoResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/SettlementInfoResult.java new file mode 100644 index 0000000000..ffa3bf73e7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/SettlementInfoResult.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.bean.applyment; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 查询结算账户返回对象信息 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class SettlementInfoResult implements Serializable { + private static final long serialVersionUID = 4568552340365230872L; + /** + * 账户类型 + */ + @SerializedName("account_type") + private String accountType; + /** + * 开户银行 + */ + @SerializedName("account_bank") + private String accountBank; + /** + * 开户银行全称(含支行] + */ + @SerializedName("bank_name") + private String bankName; + /** + * 开户银行联行号 + */ + @SerializedName("bank_branch_id") + private String bankBranchId; + /** + * 银行账号 + */ + @SerializedName("account_number") + private String accountNumber; + /** + * 汇款验证结果 + * + * @see com.github.binarywang.wxpay.bean.applyment.enums.SettlementVerifyResultEnum + */ + @SerializedName("verify_result") + private String verifyResult; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java new file mode 100644 index 0000000000..8fa1aa0caa --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java @@ -0,0 +1,890 @@ +package com.github.binarywang.wxpay.bean.applyment; + +import com.github.binarywang.wxpay.bean.applyment.enums.*; +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 特约商户进件 提交申请对象 + * + * @author zhouyongshen + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxPayApplyment4SubCreateRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 业务申请编号 + */ + @SerializedName("business_code") + private String businessCode; + /** + * 超级管理员信息 + */ + @SerializedName("contact_info") + @SpecEncrypt + private ContactInfo contactInfo; + + /** + * 主体资料 + */ + @SerializedName("subject_info") + @SpecEncrypt + private SubjectInfo subjectInfo; + + /** + * 经营资料 + */ + @SerializedName("business_info") + private BusinessInfo businessInfo; + + /** + * 结算规则 + */ + @SerializedName("settlement_info") + private SettlementInfo settlementInfo; + + /** + * 结算银行账户 + */ + @SerializedName("bank_account_info") + @SpecEncrypt + private BankAccountInfo bankAccountInfo; + + /** + * 结算银行账户 + */ + @SerializedName("addition_info") + private AdditionInfo additionInfo; + + /** + * 超级管理员需在开户后进行签约,并接收日常重要管理信息和进行资金操作,请确定其为商户法定代表人或负责人。 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class ContactInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 超级管理员姓名 + */ + @SerializedName("contact_name") + @SpecEncrypt + private String contactName; + + /** + * 超级管理员身份证件号码 + * 1、“超级管理员身份证号码”与“超级管理员微信openid”,二选一必填。 + * 2、超级管理员签约时,校验微信号绑定的银行卡实名信息,是否与该证件号码一致。 + * 3、可传身份证、来往内地通行证、来往大陆通行证、护照等证件号码。 + */ + @SerializedName("contact_id_number") + @SpecEncrypt + private String contactIdNumber; + + /** + * 超级管理员微信openid + * 1、“超级管理员身份证件号码”与“超级管理员微信openid”,二选一必填。 + * 2、超级管理员签约时,校验微信号是否与该微信openid一致。 + */ + @SerializedName("openid") + private String openid; + + /** + * 1、11位数字。 + * 2、用于接收微信支付的重要管理信息及日常操作验证码。 + */ + @SerializedName("mobile_phone") + @SpecEncrypt + private String mobilePhone; + + /** + * 1、用于接收微信支付的开户邮件及日常业务通知。 + * 2、需要带@,遵循邮箱格式校验,该字段需进行加密处理, + */ + @SerializedName("contact_email") + @SpecEncrypt + private String contactEmail; + + } + + /** + * 主体资料 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class SubjectInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 主体类型 + */ + @SerializedName("subject_type") + private SubjectTypeEnum subjectType; + + /** + * 营业执照 + */ + @SerializedName("business_license_info") + private BusinessLicenseInfo businessLicenseInfo; + /** + * 登记证书 + */ + @SerializedName("certificate_info") + private CertificateInfo certificateInfo; + + /** + * 组织机构代码证 + */ + @SerializedName("organization_info") + private OrganizationInfo organizationInfo; + + /** + * 单位证明函照片 + */ + @SerializedName("certificate_letter_copy") + private String certificateLetterCopy; + + /** + * 经营者/法人身份证件 + */ + @SerializedName("identity_info") + @SpecEncrypt + private IdentityInfo identityInfo; + + /** + * 最终受益人信息(UBO] + */ + @SerializedName("ubo_info") + @SpecEncrypt + private UboInfo uboInfo; + + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class BusinessLicenseInfo implements Serializable { + private static final long serialVersionUID = -1016615300418945838L; + /** + * 营业执照照片 + */ + @SerializedName("license_copy") + private String licenseCopy; + /** + * 注册号/统一社会信用代码 + */ + @SerializedName("license_number") + private String licenseNumber; + /** + * 商户名称 + */ + @SerializedName("merchant_name") + private String merchantName; + /** + * 个体户经营者/法人姓名 + */ + @SerializedName("legal_person") + private String legalPerson; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class CertificateInfo implements Serializable { + private static final long serialVersionUID = 5080675335337916895L; + + /** + * 登记证书照片 + */ + @SerializedName("cert_copy") + private String certCopy; + + /** + * 登记证书类型 + */ + @SerializedName("cert_type") + private CertTypeEnum certType; + + + /** + * 证书号 + */ + @SerializedName("cert_number") + private String certNumber; + + + /** + * 商户名称 + */ + @SerializedName("merchant_name") + private String merchantName; + + + /** + * 注册地址 + */ + @SerializedName("company_address") + private String companyAddress; + + + /** + * 法人姓名 + */ + @SerializedName("legal_person") + private String legalPerson; + + + /** + * 有效期限开始日期 + */ + @SerializedName("period_begin") + private String periodBegin; + + + /** + * 有效期限结束日期 + */ + @SerializedName("period_end") + private String periodEnd; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class OrganizationInfo implements Serializable { + private static final long serialVersionUID = 6497045652770046337L; + /** + * 组织机构代码证照片 + */ + @SerializedName("organization_copy") + private String organizationCopy; + /** + * 组织机构代码 + */ + @SerializedName("organization_code") + private String organizationCode; + /** + * 组织机构代码证有效期开始日期 + */ + @SerializedName("org_period_begin") + private String orgPeriodBegin; + /** + * 组织机构代码证有效期结束日期 + */ + @SerializedName("org_period_end") + private String orgPeriodEnd; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class IdentityInfo implements Serializable { + private static final long serialVersionUID = 1683704338370383827L; + + /** + * 证件类型 + */ + @SerializedName("id_doc_type") + private IdTypeEnum idDocType; + + /** + * 身份证信息 + */ + @SerializedName("id_card_info") + @SpecEncrypt + private IdCardInfo idCardInfo; + + /** + * 其他类型证件信息 + */ + @SerializedName("id_doc_info") + @SpecEncrypt + private IdDocInfo idDocInfo; + + /** + * 经营者/法人是否为受益人 + */ + @SerializedName("owner") + private boolean owner; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class IdCardInfo implements Serializable { + private static final long serialVersionUID = -2897792705297641786L; + /** + * 身份证人像面照片 + */ + @SerializedName("id_card_copy") + private String idCardCopy; + /** + * 身份证国徽面照片 + */ + @SerializedName("id_card_national") + private String idCardNational; + + /** + * 身份证姓名 + */ + @SerializedName("id_card_name") + @SpecEncrypt + private String idCardName; + /** + * 身份证号码 + */ + @SerializedName("id_card_number") + @SpecEncrypt + private String idCardNumber; + /** + * 身份证有效期开始时间 + */ + @SerializedName("card_period_begin") + private String cardPeriodBegin; + /** + * 身份证有效期结束时间 + */ + @SerializedName("card_period_end") + private String cardPeriodEnd; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class IdDocInfo implements Serializable { + private static final long serialVersionUID = 7335589815924447719L; + /** + * 证件照片 + */ + @SerializedName("id_doc_copy") + private String idDocCopy; + + /** + * 证件姓名 + */ + @SerializedName("id_doc_name") + @SpecEncrypt + private String idDocName; + + /** + * 证件号码 + */ + @SerializedName("id_doc_number") + @SpecEncrypt + private String idDocNumber; + /** + * 证件有效期开始时间 + */ + @SerializedName("doc_period_begin") + private String docPeriodBegin; + /** + * 证件有效期结束时间 + */ + @SerializedName("doc_period_end") + private String docPeriodEnd; + } + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class UboInfo implements Serializable { + private static final long serialVersionUID = 7918585690831975042L; + /** + * 证件类型 + */ + @SerializedName("id_type") + private IdTypeEnum idType; + /** + * 身份证人像面照片 + */ + @SerializedName("id_card_copy") + private String idCardCopy; + /** + * 身份证国徽面照片 + */ + @SerializedName("id_card_national") + private String idCardNational; + /** + * 证件照片 + */ + @SerializedName("id_doc_copy") + private String idDocCopy; + /** + * 受益人姓名 + */ + @SerializedName("name") + @SpecEncrypt + private String name; + /** + * 证件号码 + */ + @SerializedName("id_number") + @SpecEncrypt + private String idNumber; + /** + * 证件有效期开始时间 + */ + @SerializedName("id_period_begin") + private String idPeriodBegin; + /** + * 证件有效期结束时间 + */ + @SerializedName("id_period_end") + private String idPeriodEnd; + } + + } + + /** + * 经营资料 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class BusinessInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 商户简称 + */ + @SerializedName("merchant_shortname") + private String merchantShortname; + + /** + * 客服电话 + */ + @SerializedName("service_phone") + private String servicePhone; + + /** + * 经营场景 + */ + @SerializedName("sales_info") + private SalesInfo salesInfo; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class SalesInfo implements Serializable { + private static final long serialVersionUID = 6428044729204137659L; + /** + * 经营场景类型 + */ + @SerializedName("sales_scenes_type") + private List salesScenesType; + + /** + * 线下门店场景 + */ + @SerializedName("biz_store_info") + private BizStoreInfo bizStoreInfo; + + /** + * 公众号场景 + */ + @SerializedName("mp_info") + private MpInfo mpInfo; + + /** + * 小程序场景 + */ + @SerializedName("mini_program_info") + private MiniProgramInfo miniProgramInfo; + + /** + * APP场景 + */ + @SerializedName("app_info") + private AppInfo appInfo; + + /** + * 互联网网站场景 + */ + @SerializedName("web_info") + private WebInfo webInfo; + + /** + * 企业微信场景 + */ + @SerializedName("wework_info") + private WeworkInfo weworkInfo; + + /** + * 线下门店场景 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class BizStoreInfo implements Serializable { + private static final long serialVersionUID = 4397253725912709093L; + /** + * 门店名称 + */ + @SerializedName("biz_store_name") + private String bizStoreName; + + /** + * 门店省市编码 + */ + @SerializedName("biz_address_code") + private String bizAddressCode; + + /** + * 门店地址 + */ + @SerializedName("biz_store_address") + private String bizStoreAddress; + + /** + * 门店门头照片 + */ + @SerializedName("store_entrance_pic") + private List storeEntrancePic; + + /** + * 店内环境照片 + */ + @SerializedName("indoor_pic") + private List indoorPic; + + /** + * 线下场所对应的商家APPID + */ + @SerializedName("biz_sub_appid") + private String bizSubAppid; + + } + + /** + * 公众号场景 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class MpInfo implements Serializable { + private static final long serialVersionUID = 167582552189873597L; + /** + * 服务商公众号APPID + */ + @SerializedName("mp_appid") + private String mpAppid; + + /** + * 商家公众号APPID + */ + @SerializedName("mp_sub_appid") + private String mpSubAppid; + + /** + * 公众号页面截图 + */ + @SerializedName("mp_pics") + private List mpPics; + + } + + /** + * 小程序场景 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class MiniProgramInfo implements Serializable { + private static final long serialVersionUID = -371749335686796436L; + /** + * 服务商小程序APPID + */ + @SerializedName("mini_program_appid") + private String miniProgramAppid; + + /** + * 商家小程序APPID + */ + @SerializedName("mini_program_sub_appid") + private String miniProgramSubAppid; + + /** + * 小程序截图 + */ + @SerializedName("mini_program_pics") + private List miniProgramPics; + + + } + + /** + * APP场景 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class AppInfo implements Serializable { + private static final long serialVersionUID = 3959643687528770473L; + /** + * 服务商应用APPID + */ + @SerializedName("app_appid") + private String appAppid; + + /** + * 商家应用APPID + */ + @SerializedName("app_sub_appid") + private String appSubAppid; + + /** + * APP截图 + */ + @SerializedName("app_pics") + private List appPics; + + } + + /** + * 互联网网站场景 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class WebInfo implements Serializable { + private static final long serialVersionUID = -4183874827185822310L; + /** + * 互联网网站域名 + */ + @SerializedName("domain") + private String domain; + + /** + * 网站授权函 + */ + @SerializedName("web_authorisation") + private String webAuthorisation; + + /** + * 互联网网站对应的商家APPID + */ + @SerializedName("web_appid") + private String webAppid; + + } + + /** + * 企业微信场景 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class WeworkInfo implements Serializable { + private static final long serialVersionUID = 9075531305717309383L; + /** + * 商家企业微信CorpID + */ + @SerializedName("sub_corp_id") + private String subCorpId; + + /** + * 企业微信页面截图 + */ + @SerializedName("wework_pics") + private List weworkPics; + + } + } + } + + /** + * 结算规则 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class SettlementInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 入驻结算规则ID + */ + @SerializedName("settlement_id") + private String settlementId; + + /** + * 所属行业 + */ + @SerializedName("qualification_type") + private String qualificationType; + + /** + * 特殊资质图片 + */ + @SerializedName("qualifications") + private List qualifications; + + /** + * 优惠费率活动ID + */ + @SerializedName("activities_id") + private String activitiesId; + + /** + * 优惠费率活动值 + */ + @SerializedName("activities_rate") + private String activitiesRate; + + /** + * 优惠费率活动补充材料 + */ + @SerializedName("activities_additions") + private List activitiesAdditions; + + } + + /** + * 结算银行账户 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class BankAccountInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 账户类型 + */ + @SerializedName("bank_account_type") + private BankAccountTypeEnum bankAccountType; + + /** + * 开户名称 + */ + @SerializedName("account_name") + @SpecEncrypt + private String accountName; + + /** + * 开户银行 + */ + @SerializedName("account_bank") + private String accountBank; + + /** + * 开户银行省市编码 + */ + @SerializedName("bank_address_code") + private String bankAddressCode; + + /** + * 开户银行联行号 + */ + @SerializedName("bank_branch_id") + private String bankBranchId; + + /** + * 开户银行全称(含支行] + */ + @SerializedName("bank_name") + private String bankName; + + /** + * 银行账号 + */ + @SerializedName("account_number") + @SpecEncrypt + private String accountNumber; + + } + + + /** + * 补充材料 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class AdditionInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 法人开户承诺函 + */ + @SerializedName("legal_person_commitment") + private String legalPersonCommitment; + + /** + * 法人开户意愿视频 + */ + @SerializedName("legal_person_video") + private String legalPersonVideo; + + /** + * 补充材料 + */ + @SerializedName("business_addition_pics") + private List businessAdditionPics; + + /** + * 补充说明 + */ + @SerializedName("business_addition_msg") + private String businessAdditionMsg; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplymentCreateResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplymentCreateResult.java new file mode 100644 index 0000000000..157b8fc093 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplymentCreateResult.java @@ -0,0 +1,31 @@ +package com.github.binarywang.wxpay.bean.applyment; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + + +/** + * 特约商户进件 提交申请结果响应 + * + * @author zhouyongshen + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxPayApplymentCreateResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 微信支付申请单号 + */ + @SerializedName("applyment_id") + private String applymentId; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/AccountTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/AccountTypeEnum.java new file mode 100644 index 0000000000..15fcba9f96 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/AccountTypeEnum.java @@ -0,0 +1,18 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + + +/** + * 银行结算账户枚举类 + */ +public enum AccountTypeEnum { + /** + * 对公银行账户 + */ + ACCOUNT_TYPE_BUSINESS, + + /** + * 经营者个人银行卡 + */ + ACCOUNT_TYPE_PRIVATE, + ; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/ApplymentStateEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/ApplymentStateEnum.java new file mode 100644 index 0000000000..2997ed522c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/ApplymentStateEnum.java @@ -0,0 +1,42 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + +/** + * 申请单状态枚举类 + * + * @author zhouyongshen + */ +public enum ApplymentStateEnum { + /** + * (编辑中):提交申请发生错误导致,请尝试重新提交。 + */ + APPLYMENT_STATE_EDITTING, + /** + * (审核中):申请单正在审核中,超级管理员用微信打开“签约链接”,完成绑定微信号后,申请单进度将通过微信公众号通知超级管理员,引导完成后续步骤。 + */ + APPLYMENT_STATE_AUDITING, + /** + * (已驳回):请按照驳回原因修改申请资料,超级管理员用微信打开“签约链接”,完成绑定微信号,后续申请单进度将通过微信公众号通知超级管理员。 + */ + APPLYMENT_STATE_REJECTED, + /** + * (待账户验证):请超级管理员使用微信打开返回的“签约链接”,根据页面指引完成账户验证。 + */ + APPLYMENT_STATE_TO_BE_CONFIRMED, + /** + * (待签约):请超级管理员使用微信打开返回的“签约链接”,根据页面指引完成签约。 + */ + APPLYMENT_STATE_TO_BE_SIGNED, + /** + * (开通权限中):系统开通相关权限中,请耐心等待。 + */ + APPLYMENT_STATE_SIGNING, + /** + * (已完成):商户入驻申请已完成。 + */ + APPLYMENT_STATE_FINISHED, + /** + * (已作废):申请单已被撤销。 + */ + APPLYMENT_STATE_CANCELED + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/BankAccountTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/BankAccountTypeEnum.java new file mode 100644 index 0000000000..739ad24474 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/BankAccountTypeEnum.java @@ -0,0 +1,18 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + + +/** + * 银行结算账户枚举类 + */ +public enum BankAccountTypeEnum { + /** + * 对公银行账户 + */ + BANK_ACCOUNT_TYPE_CORPORATE, + + /** + * 经营者个人银行卡 + */ + BANK_ACCOUNT_TYPE_PERSONAL, + ; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/CertTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/CertTypeEnum.java new file mode 100644 index 0000000000..f7415fdc3f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/CertTypeEnum.java @@ -0,0 +1,60 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + +/** + * 登记证书的类型枚举 + */ +public enum CertTypeEnum { + /** + * 事业单位法人证书 + */ + CERTIFICATE_TYPE_2388, + /** + * 统一社会信用代码证书 + */ + CERTIFICATE_TYPE_2389, + /** + * 有偿服务许可证(军队医院适用) + */ + CERTIFICATE_TYPE_2390, + /** + * 医疗机构执业许可证(军队医院适用) + */ + CERTIFICATE_TYPE_2391, + /** + * 企业营业执照(挂靠企业的党组织适用) + */ + CERTIFICATE_TYPE_2392, + /** + * 组织机构代码证(政府机关适用) + */ + CERTIFICATE_TYPE_2393, + /** + * 社会团体法人登记证书 + */ + CERTIFICATE_TYPE_2394, + /** + * 民办非企业单位登记证书 + */ + CERTIFICATE_TYPE_2395, + /** + * 基金会法人登记证书 + */ + CERTIFICATE_TYPE_2396, + /** + * 慈善组织公开募捐资格证书 + */ + CERTIFICATE_TYPE_2397, + /** + * 农民专业合作社法人营业执照 + */ + CERTIFICATE_TYPE_2398, + /** + * 宗教活动场所登记证 + */ + CERTIFICATE_TYPE_2399, + /** + * 其他证书/批文/证明 + */ + CERTIFICATE_TYPE_2400, + ; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/IdTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/IdTypeEnum.java new file mode 100644 index 0000000000..d65c502b89 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/IdTypeEnum.java @@ -0,0 +1,29 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + +/** + * 个体户/企业/党政、机关及事业单位/其他组织:可选择任一证件类型。 + * 枚举值 + */ +public enum IdTypeEnum { + /** + * 中国大陆居民-身份证 + */ + IDENTIFICATION_TYPE_IDCARD, + /** + * 其他国家或地区居民-护照 + */ + IDENTIFICATION_TYPE_OVERSEA_PASSPORT, + /** + * 中国香港居民-来往内地通行证 + */ + IDENTIFICATION_TYPE_HONGKONG_PASSPORT, + /** + * 中国澳门居民-来往内地通行证 + */ + IDENTIFICATION_TYPE_MACAO_PASSPORT, + /** + * 中国台湾居民-来往大陆通行证 + */ + IDENTIFICATION_TYPE_TAIWAN_PASSPORT, + ; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SalesScenesTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SalesScenesTypeEnum.java new file mode 100644 index 0000000000..b057dd5740 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SalesScenesTypeEnum.java @@ -0,0 +1,32 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + +/** + * 经营场景类型枚举值 + */ +public enum SalesScenesTypeEnum { + /** + * 线下门店 + */ + SALES_SCENES_STORE, + /** + * 公众号 + */ + SALES_SCENES_MP, + /** + * 小程序 + */ + SALES_SCENES_MINI_PROGRAM, + /** + * 互联网 + */ + SALES_SCENES_WEB, + /** + * APP + */ + SALES_SCENES_APP, + /** + * 企业微信 + */ + SALES_SCENES_WEWORK, + ; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SettlementVerifyResultEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SettlementVerifyResultEnum.java new file mode 100644 index 0000000000..ed2fbc719f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SettlementVerifyResultEnum.java @@ -0,0 +1,23 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + +/** + * 返回特约商户的结算账户-汇款验证结果枚举类 + * + * @author zhouyognshen + */ +public enum SettlementVerifyResultEnum { + /** + * 系统汇款验证中,商户可发起提现尝试。 + */ + VERIFYING, + /** + * 系统成功汇款,该账户可正常发起提现。 + */ + VERIFY_SUCCESS, + /** + * 系统汇款失败,该账户无法发起提现,请检查修改。 + */ + VERIFY_FAIL, + ; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SubjectTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SubjectTypeEnum.java new file mode 100644 index 0000000000..7845c052c2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/enums/SubjectTypeEnum.java @@ -0,0 +1,30 @@ +package com.github.binarywang.wxpay.bean.applyment.enums; + +/** + * 主体类型枚举类 + *
    + *     商户申请接入时如何选择主体类型? https://kf.qq.com/faq/180910IBZVnQ180910naQ77b.html
    + * 
    + * + * @author zhouyongshen + */ +public enum SubjectTypeEnum { + /** + * (个体户):营业执照上的主体类型一般为个体户、个体工商户、个体经营; + */ + SUBJECT_TYPE_INDIVIDUAL, + /** + * (企业):营业执照上的主体类型一般为有限公司、有限责任公司; + */ + SUBJECT_TYPE_ENTERPRISE, + /** + * (党政、机关及事业单位):包括国内各级、各类政府机构、事业单位等(如:公安、党团、司法、交通、旅游、工商税务、市政、医疗、教育、学校等机构); + */ + SUBJECT_TYPE_INSTITUTIONS, + /** + * (其他组织):不属于企业、政府/事业单位的组织机构(如社会团体、民办非企业、基金会),要求机构已办理组织机构代码证。 + */ + SUBJECT_TYPE_OTHERS, + ; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java new file mode 100644 index 0000000000..539ad988b9 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java @@ -0,0 +1,136 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
    + * 查询代金券信息请求对象类
    + * Created by Binary Wang on 2017-7-15.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayCouponInfoQueryRequest extends BaseWxPayRequest { + /** + *
    +   * 字段名:代金券id
    +   * 变量名:coupon_id
    +   * 是否必填:是
    +   * 示例值:1757
    +   * 类型:String
    +   * 说明:代金券id
    +   * 
    + */ + @Required + @XStreamAlias("coupon_id") + private String couponId; + + /** + *
    +   * 字段名:代金券批次号
    +   * 变量名:stock_id
    +   * 是否必填:是
    +   * 示例值:58818
    +   * 类型:String
    +   * 说明:代金劵对应的批次号
    +   * 
    + */ + @Required + @XStreamAlias("stock_id") + private String stockId; + + /** + *
    +   * 字段名:用户openid
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
    +   * 类型:String
    +   * 说明:Openid信息,用户在appid下的openid。
    +   * 
    + */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 字段名:操作员
    +   * 变量名:op_user_id
    +   * 是否必填:否
    +   * 示例值:10000098
    +   * 类型:String(32)
    +   * 说明:操作员帐号, 默认为商户号,可在商户平台配置操作员对应的api权限
    +   * 
    + */ + @XStreamAlias("op_user_id") + private String opUserId; + + /** + *
    +   * 字段名:设备号
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:
    +   * 类型:String(32)
    +   * 说明:微信支付分配的终端设备号
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:协议版本
    +   * 变量名:version
    +   * 是否必填:否
    +   * 示例值:1.0
    +   * 类型:String(32)
    +   * 说明:默认1.0
    +   * 
    + */ + @XStreamAlias("version") + private String version; + + /** + *
    +   * 字段名:协议类型
    +   * 变量名:type
    +   * 是否必填:否
    +   * 示例值:XML
    +   * 类型:String(32)
    +   * 说明:XML【目前仅支持默认XML】
    +   * 
    + */ + @XStreamAlias("type") + private String type; + + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected void storeMap(Map map) { + map.put("coupon_id", couponId); + map.put("stock_id", stockId); + map.put("openid", openid); + map.put("op_user_id", opUserId); + map.put("device_info", deviceInfo); + map.put("version", version); + map.put("type", type); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java new file mode 100644 index 0000000000..51362a416f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java @@ -0,0 +1,250 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 查询代金券信息响应结果类
    + * Created by Binary Wang on 2017-7-15.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxPayCouponInfoQueryResult extends BaseWxPayResult { + /** + *
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:123456sb
    +   * 类型:String(32)
    +   * 说明:微信支付分配的终端设备号,
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:批次ID.
    +   * 变量名:coupon_stock_id
    +   * 是否必填:是
    +   * 示例值:1567
    +   * 类型:String
    +   * 说明:代金券批次Id
    +   * 
    + */ + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
    +   * 字段名:代金券id.
    +   * 变量名:coupon_id
    +   * 是否必填:是
    +   * 示例值:4242
    +   * 类型:String
    +   * 说明:代金券id
    +   * 
    + */ + @XStreamAlias("coupon_id") + private String couponId; + + /** + *
    +   * 字段名:代金券面额.
    +   * 变量名:coupon_value
    +   * 是否必填:是
    +   * 示例值:4
    +   * 类型:Unsinged int
    +   * 说明:代金券面值,单位是分
    +   * 
    + */ + @XStreamAlias("coupon_value") + private Integer couponValue; + + /** + *
    +   * 字段名:代金券使用门槛.
    +   * 变量名:coupon_minimum 微信文档有误
    +   * 是否必填:是
    +   * 示例值:10
    +   * 类型:Unsinged int
    +   * 说明:代金券使用最低限额,单位是分
    +   * 
    + */ + @XStreamAlias("coupon_minimum") + private Integer couponMinimum; + + /** + *
    +   * 字段名:代金券名称.
    +   * 变量名:coupon_name
    +   * 是否必填:是
    +   * 示例值:测试代金券
    +   * 类型:String
    +   * 说明:代金券名称
    +   * 
    + */ + @XStreamAlias("coupon_name") + private String couponName; + + /** + *
    +   * 字段名:代金券状态.
    +   * 变量名:coupon_state
    +   * 是否必填:是
    +   * 示例值:SENDED
    +   * 类型:String
    +   * 说明:代金券状态:SENDED-可用,USED-已实扣,EXPIRED-已过期
    +   * 
    + */ + @XStreamAlias("coupon_state") + private String couponState; + + /** + *
    +   * 字段名:代金券描述.
    +   * 变量名:coupon_desc
    +   * 是否必填:是
    +   * 示例值:微信支付-代金券
    +   * 类型:String
    +   * 说明:代金券描述
    +   * 
    + */ + @XStreamAlias("coupon_desc") + private String couponDesc; + + /** + *
    +   * 字段名:实际优惠金额.
    +   * 变量名:coupon_use_value
    +   * 是否必填:是
    +   * 示例值:0
    +   * 类型:Unsinged int
    +   * 说明:代金券实际使用金额
    +   * 
    + */ + @XStreamAlias("coupon_use_value") + private Integer couponUseValue; + + /** + *
    +   * 字段名:优惠剩余可用额.
    +   * 变量名:coupon_remain_value
    +   * 是否必填:是
    +   * 示例值:4
    +   * 类型:Unsinged int
    +   * 说明:代金券剩余金额:部分使用情况下,可能会存在券剩余金额
    +   * 
    + */ + @XStreamAlias("coupon_remain_value") + private Integer couponRemainValue; + + /** + *
    +   * 字段名:生效开始时间.
    +   * 变量名:begin_time
    +   * 是否必填:是
    +   * 示例值:1943787483
    +   * 类型:String
    +   * 说明:格式为时间戳
    +   * 
    + */ + @XStreamAlias("begin_time") + private String beginTime; + + /** + *
    +   * 字段名:生效结束时间.
    +   * 变量名:end_time
    +   * 是否必填:是
    +   * 示例值:1943787484
    +   * 类型:String
    +   * 说明:格式为时间戳
    +   * 
    + */ + @XStreamAlias("end_time") + private String endTime; + + /** + *
    +   * 字段名:发放时间.
    +   * 变量名:send_time
    +   * 是否必填:是
    +   * 示例值:1943787420
    +   * 类型:String
    +   * 说明:格式为时间戳
    +   * 
    + */ + @XStreamAlias("send_time") + private String sendTime; + + /** + *
    +   * 字段名:消耗方商户id.
    +   * 变量名:consumer_mch_id
    +   * 是否必填:否
    +   * 示例值:10000098
    +   * 类型:String
    +   * 说明:代金券使用后,消耗方商户id
    +   * 
    + */ + @XStreamAlias("consumer_mch_id") + private String consumerMchId; + + /** + *
    +   * 字段名:发放来源.
    +   * 变量名:send_source
    +   * 是否必填:是
    +   * 示例值:FULL_SEND
    +   * 类型:String
    +   * 说明:代金券发放来源:FULL_SEND-满送 NORMAL-普通发放场景
    +   * 
    + */ + @XStreamAlias("send_source") + private String sendSource; + + /** + *
    +   * 字段名:是否允许部分使用.
    +   * 变量名:is_partial_use
    +   * 是否必填:否
    +   * 示例值:1
    +   * 类型:String
    +   * 说明:该代金券是否允许部分使用标识:1-表示支持部分使用
    +   * 
    + */ + @XStreamAlias("is_partial_use") + private String isPartialUse; + + @Override + protected void loadXml(Document d) { + deviceInfo = readXmlString(d, "device_info"); + couponStockId = readXmlString(d, "coupon_stock_id"); + couponId = readXmlString(d, "coupon_id"); + couponValue = readXmlInteger(d, "coupon_value"); + couponMinimum = readXmlInteger(d, "coupon_minimum"); + couponName = readXmlString(d, "coupon_name"); + couponState = readXmlString(d, "coupon_state"); + couponDesc = readXmlString(d, "coupon_desc"); + couponUseValue = readXmlInteger(d, "coupon_use_value"); + couponRemainValue = readXmlInteger(d, "coupon_remain_value"); + beginTime = readXmlString(d, "begin_time"); + endTime = readXmlString(d, "end_time"); + sendTime = readXmlString(d, "send_time"); + consumerMchId = readXmlString(d, "consumer_mch_id"); + sendSource = readXmlString(d, "send_source"); + isPartialUse = readXmlString(d, "is_partial_use"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java new file mode 100644 index 0000000000..a43ce51d99 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java @@ -0,0 +1,149 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
    + * 发送代金券请求对象类
    + * Created by Binary Wang on 2017-7-15.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayCouponSendRequest extends BaseWxPayRequest { + /** + *
    +   * 字段名:代金券批次id
    +   * 变量名:coupon_stock_id
    +   * 是否必填:是
    +   * 示例值:1757
    +   * 类型:String
    +   * 说明:代金券批次id
    +   * 
    + */ + @Required + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
    +   * 字段名:openid记录数
    +   * 变量名:openid_count
    +   * 是否必填:是
    +   * 示例值:1
    +   * 类型:int
    +   * 说明:openid记录数(目前支持num=1)
    +   * 
    + */ + @Required + @XStreamAlias("openid_count") + private Integer openidCount; + + /** + *
    +   * 字段名:商户单据号
    +   * 变量名:partner_trade_no
    +   * 是否必填:是
    +   * 示例值:1000009820141203515766
    +   * 类型:String
    +   * 说明:商户此次发放凭据号(格式:商户id+日期+流水号),商户侧需保持唯一性
    +   * 
    + */ + @Required + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + *
    +   * 字段名:用户openid
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
    +   * 类型:String
    +   * 说明:Openid信息,用户在appid下的openid。
    +   * 
    + */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 字段名:操作员
    +   * 变量名:op_user_id
    +   * 是否必填:否
    +   * 示例值:10000098
    +   * 类型:String(32)
    +   * 说明:操作员帐号, 默认为商户号,可在商户平台配置操作员对应的api权限
    +   * 
    + */ + @XStreamAlias("op_user_id") + private String opUserId; + + /** + *
    +   * 字段名:设备号
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:
    +   * 类型:String(32)
    +   * 说明:微信支付分配的终端设备号
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:协议版本
    +   * 变量名:version
    +   * 是否必填:否
    +   * 示例值:1.0
    +   * 类型:String(32)
    +   * 说明:默认1.0
    +   * 
    + */ + @XStreamAlias("version") + private String version; + + /** + *
    +   * 字段名:协议类型
    +   * 变量名:type
    +   * 是否必填:否
    +   * 示例值:XML
    +   * 类型:String(32)
    +   * 说明:XML【目前仅支持默认XML】
    +   * 
    + */ + @XStreamAlias("type") + private String type; + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected void storeMap(Map map) { + map.put("coupon_stock_id", couponStockId); + map.put("openid_count", openidCount.toString()); + map.put("partner_trade_no", partnerTradeNo); + map.put("openid", openid); + map.put("op_user_id", opUserId); + map.put("device_info", deviceInfo); + map.put("version", version); + map.put("type", type); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java new file mode 100644 index 0000000000..c7e970ad91 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java @@ -0,0 +1,152 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 发送代金券响应结果类
    + * Created by Binary Wang on 2017-7-15.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxPayCouponSendResult extends BaseWxPayResult { + /** + *
    +   * 字段名:设备号
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:123456sb
    +   * 类型:String(32)
    +   * 描述:微信支付分配的终端设备号,
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:代金券批次id
    +   * 变量名:coupon_stock_id
    +   * 是否必填:是
    +   * 示例值:1757
    +   * 类型:String
    +   * 描述:用户在商户appid下的唯一标识
    +   * 
    + */ + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
    +   * 字段名:返回记录数
    +   * 变量名:resp_count
    +   * 是否必填:是
    +   * 示例值:1
    +   * 类型:Int
    +   * 描述:返回记录数
    +   * 
    + */ + @XStreamAlias("resp_count") + private Integer respCount; + + /** + *
    +   * 字段名:成功记录数
    +   * 变量名:success_count
    +   * 是否必填:是
    +   * 示例值:1或者0
    +   * 类型:Int
    +   * 描述:成功记录数
    +   * 
    + */ + @XStreamAlias("success_count") + private Integer successCount; + + /** + *
    +   * 字段名:失败记录数
    +   * 变量名:failed_count
    +   * 是否必填:是
    +   * 示例值:1或者0
    +   * 类型:Int
    +   * 描述:失败记录数
    +   * 
    + */ + @XStreamAlias("failed_count") + private Integer failedCount; + + /** + *
    +   * 字段名:用户标识
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
    +   * 类型:String
    +   * 描述:用户在商户appid下的唯一标识
    +   * 
    + */ + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 字段名:返回码
    +   * 变量名:ret_code
    +   * 是否必填:是
    +   * 示例值:SUCCESS或者FAILED
    +   * 类型:String
    +   * 描述:返回码,SUCCESS/FAILED
    +   * 
    + */ + @XStreamAlias("ret_code") + private String retCode; + + /** + *
    +   * 字段名:代金券id
    +   * 变量名:coupon_id
    +   * 是否必填:是
    +   * 示例值:1870
    +   * 类型:String
    +   * 描述:对一个用户成功发放代金券则返回代金券id,即ret_code为SUCCESS的时候;如果ret_code为FAILED则填写空串""
    +   * 
    + */ + @XStreamAlias("coupon_id") + private String couponId; + + /** + *
    +   * 字段名:返回信息
    +   * 变量名:ret_msg
    +   * 是否必填:是
    +   * 示例值:失败描述信息,例如:“用户已达领用上限”
    +   * 类型:String
    +   * 描述:返回信息,当返回码是FAILED的时候填写,否则填空串“”
    +   * 
    + */ + @XStreamAlias("ret_msg") + private String retMsg; + + @Override + protected void loadXml(Document d) { + deviceInfo = readXmlString(d, "device_info"); + couponStockId = readXmlString(d, "coupon_stock_id"); + respCount = readXmlInteger(d, "resp_count"); + successCount = readXmlInteger(d, "success_count"); + failedCount = readXmlInteger(d, "failed_count"); + openid = readXmlString(d, "openid"); + retCode = readXmlString(d, "ret_code"); + couponId = readXmlString(d, "coupon_id"); + retMsg = readXmlString(d, "ret_msg"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java new file mode 100644 index 0000000000..bae6a563ea --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java @@ -0,0 +1,105 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
    + * 查询代金券批次请求对象类
    + * Created by Binary Wang on 2017-7-15.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayCouponStockQueryRequest extends BaseWxPayRequest { + /** + *
    +   * 字段名:代金券批次id
    +   * 变量名:coupon_stock_id
    +   * 是否必填:是
    +   * 示例值:1757
    +   * 类型:String
    +   * 说明:代金券批次id
    +   * 
    + */ + @Required + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
    +   * 字段名:操作员
    +   * 变量名:op_user_id
    +   * 是否必填:否
    +   * 示例值:10000098
    +   * 类型:String(32)
    +   * 说明:操作员帐号, 默认为商户号,可在商户平台配置操作员对应的api权限
    +   * 
    + */ + @XStreamAlias("op_user_id") + private String opUserId; + + /** + *
    +   * 字段名:设备号
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:
    +   * 类型:String(32)
    +   * 说明:微信支付分配的终端设备号
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:协议版本
    +   * 变量名:version
    +   * 是否必填:否
    +   * 示例值:1.0
    +   * 类型:String(32)
    +   * 说明:默认1.0
    +   * 
    + */ + @XStreamAlias("version") + private String version; + + /** + *
    +   * 字段名:协议类型
    +   * 变量名:type
    +   * 是否必填:否
    +   * 示例值:XML
    +   * 类型:String(32)
    +   * 说明:XML【目前仅支持默认XML】
    +   * 
    + */ + @XStreamAlias("type") + private String type; + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected void storeMap(Map map) { + map.put("coupon_stock_id", couponStockId); + map.put("op_user_id", opUserId); + map.put("device_info", deviceInfo); + map.put("version", version); + map.put("type", type); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java new file mode 100644 index 0000000000..a683a850b8 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java @@ -0,0 +1,211 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 查询代金券批次响应结果类.
    + * Created by Binary Wang on 2017-7-15.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayCouponStockQueryResult extends BaseWxPayResult { + private static final long serialVersionUID = 4644274730788451926L; + /** + *
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:123456sb
    +   * 类型:String(32)
    +   * 说明:微信支付分配的终端设备号
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:代金券批次ID.
    +   * 变量名:coupon_stock_id
    +   * 是否必填:是
    +   * 示例值:1757
    +   * 类型:String
    +   * 说明:代金券批次Id
    +   * 
    + */ + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
    +   * 字段名:代金券名称.
    +   * 变量名:coupon_name
    +   * 是否必填:否
    +   * 示例值:测试代金券
    +   * 类型:String
    +   * 说明:代金券名称
    +   * 
    + */ + @XStreamAlias("coupon_name") + private String couponName; + + /** + *
    +   * 字段名:代金券面额.
    +   * 变量名:coupon_value
    +   * 是否必填:是
    +   * 示例值:5
    +   * 类型:Unsinged int
    +   * 说明:代金券面值,单位是分
    +   * 
    + */ + @XStreamAlias("coupon_value") + private Integer couponValue; + + /** + *
    +   * 字段名:代金券使用最低限额.
    +   * 变量名:coupon_mininumn
    +   * 是否必填:否
    +   * 示例值:10
    +   * 类型:Unsinged int
    +   * 说明:代金券使用最低限额,单位是分
    +   * 
    + */ + @XStreamAlias("coupon_mininumn") + private Integer couponMinimum; + + /** + *
    +   * 字段名:代金券批次状态.
    +   * 变量名:coupon_stock_status
    +   * 是否必填:是
    +   * 示例值:4
    +   * 类型:int
    +   * 说明:批次状态: 1-未激活;2-审批中;4-已激活;8-已作废;16-中止发放;
    +   * 
    + */ + @XStreamAlias("coupon_stock_status") + private Integer couponStockStatus; + + /** + *
    +   * 字段名:代金券数量.
    +   * 变量名:coupon_total
    +   * 是否必填:是
    +   * 示例值:100
    +   * 类型:Unsigned int
    +   * 说明:代金券数量
    +   * 
    + */ + @XStreamAlias("coupon_total") + private Integer couponTotal; + + /** + *
    +   * 字段名:代金券最大领取数量.
    +   * 变量名:max_quota
    +   * 是否必填:否
    +   * 示例值:1
    +   * 类型:Unsigned int
    +   * 说明:代金券每个人最多能领取的数量, 如果为0,则表示没有限制
    +   * 
    + */ + @XStreamAlias("max_quota") + private Integer maxQuota; + + /** + *
    +   * 字段名:代金券已经发送的数量.
    +   * 变量名:is_send_num
    +   * 是否必填:否
    +   * 示例值:0
    +   * 类型:Unsigned int
    +   * 说明:代金券已经发送的数量
    +   * 
    + */ + @XStreamAlias("is_send_num") + private Integer isSendNum; + + /** + *
    +   * 字段名:生效开始时间.
    +   * 变量名:begin_time
    +   * 是否必填:是
    +   * 示例值:1943787483
    +   * 类型:String
    +   * 说明:格式为时间戳
    +   * 
    + */ + @XStreamAlias("begin_time") + private String beginTime; + + /** + *
    +   * 字段名:生效结束时间.
    +   * 变量名:end_time
    +   * 是否必填:是
    +   * 示例值:1943787490
    +   * 类型:String
    +   * 说明:格式为时间戳
    +   * 
    + */ + @XStreamAlias("end_time") + private String endTime; + + /** + *
    +   * 字段名:创建时间.
    +   * 变量名:create_time
    +   * 是否必填:是
    +   * 示例值:1943787420
    +   * 类型:String
    +   * 说明:格式为时间戳
    +   * 
    + */ + @XStreamAlias("create_time") + private String createTime; + + /** + *
    +   * 字段名:代金券预算额度.
    +   * 变量名:coupon_budget
    +   * 是否必填:否
    +   * 示例值:500
    +   * 类型:Unsigned int
    +   * 说明:代金券预算额度
    +   * 
    + */ + @XStreamAlias("coupon_budget") + private Integer couponBudget; + + @Override + protected void loadXml(Document d) { + deviceInfo = readXmlString(d, "device_info"); + couponStockId = readXmlString(d, "coupon_stock_id"); + couponName = readXmlString(d, "coupon_name"); + couponValue = readXmlInteger(d, "coupon_value"); + couponMinimum = readXmlInteger(d, "coupon_mininumn"); + couponStockStatus = readXmlInteger(d, "coupon_stock_status"); + couponTotal = readXmlInteger(d, "coupon_total"); + maxQuota = readXmlInteger(d, "max_quota"); + isSendNum = readXmlInteger(d, "is_send_num"); + beginTime = readXmlString(d, "begin_time"); + endTime = readXmlString(d, "end_time"); + createTime = readXmlString(d, "create_time"); + couponBudget = readXmlInteger(d, "coupon_budget"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java new file mode 100644 index 0000000000..bd021fc571 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java @@ -0,0 +1,797 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + + +/** + *
    + * 电商平台,可使用该接口,帮助其二级商户进件成为微信支付商户。
    + * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_1.shtml
    + * 
    + */ +@Data +@NoArgsConstructor +public class ApplymentsRequest implements Serializable { + private static final long serialVersionUID = -3092662029966103592L; + /** + *
    +   * 字段名:业务申请编号
    +   * 变量名:out_request_no
    +   * 是否必填:是
    +   * 类型:string(124)
    +   * 描述:
    +   *  1、服务商自定义的商户唯一编号。
    +   *  2、每个编号对应一个申请单,每个申请单审核通过后会生成一个微信支付商户号。
    +   *  3、若申请单被驳回,可填写相同的“业务申请编号”,即可覆盖修改原申请单信息 。
    +   *  示例值:APPLYMENT_00000000001
    +   * 
    + */ + @SerializedName(value = "out_request_no") + private String outRequestNo; + + /** + *
    +   * 字段名:主体类型
    +   * 变量名:organization_type
    +   * 是否必填:是
    +   * 类型:string(4)
    +   * 描述:
    +   *  枚举值:
    +   *  2401:小微商户,指无营业执照的商户。
    +   *  4:个体工商户,营业执照上的主体类型一般为个体户、个体工商户、个体经营。
    +   *  2:企业,营业执照上的主体类型一般为有限公司、有限责任公司。
    +   *  3:党政、机关及事业单位,包括国内各级、各类政府机构、事业单位等(如:公安、党团、司法、交通、旅游、工商税务、市政、医疗、教育、学校等机构)。
    +   *  1708:其他组织,不属于企业、政府/事业单位的组织机构(如社会团体、民办非企业、基金会),要求机构已办理组织机构代码证。
    +   *  示例值:2401
    +   * 
    + */ + @SerializedName(value = "organization_type") + private String organizationType; + + /** + *
    +   * 字段名:+营业执照/登记证书信息
    +   * 变量名:business_license_info
    +   * 是否必填:条件选填
    +   * 类型:object
    +   * 描述:
    +   *  1、主体为“小微”时,不填。
    +   *  2、主体为“个体工商户/企业”时,请上传营业执照。
    +   *  3、主体为“党政、机关及事业单位/其他组织”时,请上传登记证书。
    +   * 
    + */ + @SerializedName(value = "business_license_info") + private BusinessLicenseInfo businessLicenseInfo; + + /** + *
    +   * 字段名:+组织机构代码证信息
    +   * 变量名:organization_cert_info
    +   * 是否必填:条件选填
    +   * 类型:object
    +   * 描述:主体为企业/党政、机关及事业单位/其他组织,且证件号码不是18位时必填。
    +   * 
    + */ + @SerializedName(value = "organization_cert_info") + private OrganizationCertInfo organizationCertInfo; + + /** + *
    +   * 字段名:经营者/法人证件类型
    +   * 变量名:id_doc_type
    +   * 是否必填:否
    +   * 类型:string(64)
    +   * 描述:
    +   *  1、主体为“小微”,只可选择:身份证。
    +   *  2、主体为“个体户/企业/党政、机关及事业单位/其他组织”,可选择:任一证件类型。
    +   *  3、若没有填写,系统默认选择:身份证。
    +   *  枚举值:
    +   *  IDENTIFICATION_TYPE_MAINLAND_IDCARD:中国大陆居民-身份证
    +   *  IDENTIFICATION_TYPE_OVERSEA_PASSPORT:其他国家或地区居民-护照
    +   *  IDENTIFICATION_TYPE_HONGKONG:中国香港居民–来往内地通行证
    +   *  IDENTIFICATION_TYPE_MACAO:中国澳门居民–来往内地通行证
    +   *  IDENTIFICATION_TYPE_TAIWAN:中国台湾居民–来往大陆通行证
    +   *  示例值:IDENTIFICATION_TYPE_MACAO
    +   * 
    + */ + @SerializedName(value = "id_doc_type") + private String idDocType; + + /** + *
    +   * 字段名:+经营者/法人身份证信息
    +   * 变量名:id_card_info
    +   * 是否必填:条件选填
    +   * 类型:object
    +   * 描述:
    +   *  请填写经营者/法人的身份证信息
    +   *  证件类型为“身份证”时填写。
    +   *
    +   * 
    + */ + @SerializedName(value = "id_card_info") + @SpecEncrypt + private IdCardInfo idCardInfo; + + /** + *
    +   * 字段名:+经营者/法人其他类型证件信息
    +   * 变量名:id_doc_info
    +   * 是否必填:条件选填
    +   * 类型:object
    +   * 描述:证件类型为“来往内地通行证、来往大陆通行证、护照”时填写。
    +   * 
    + */ + @SerializedName(value = "id_doc_info") + private IdDocInfo idDocInfo; + + /** + *
    +   * 字段名:是否填写结算账户信息
    +   * 变量名:need_account_info
    +   * 是否必填:是
    +   * 类型:bool
    +   * 描述:
    +   *  可根据实际情况,填写“true”或“false”。
    +   *  1、若为“true”,则需填写结算账户信息。
    +   *  2、若为“false”,则无需填写结算账户信息。
    +   *  示例值:true
    +   * 
    + */ + @SerializedName(value = "need_account_info") + private Boolean needAccountInfo; + + /** + *
    +   * 字段名:+结算账户信息
    +   * 变量名:account_info
    +   * 是否必填:条件选填
    +   * 类型:object
    +   * 描述:若"是否填写结算账户信息"填写为“true”, 则必填,填写为“false”不填 。
    +   * 
    + */ + @SerializedName(value = "account_info") + @SpecEncrypt + private AccountInfo accountInfo; + + /** + *
    +   * 字段名:+超级管理员信息
    +   * 变量名:contact_info
    +   * 是否必填:是
    +   * 类型:object
    +   * 描述:
    +   *  请填写店铺的超级管理员信息。
    +   *  超级管理员需在开户后进行签约,并可接收日常重要管理信息和进行资金操作,请确定其为商户法定代表人或负责人。
    +   * 
    + */ + @SerializedName(value = "contact_info") + @SpecEncrypt + private ContactInfo contactInfo; + + /** + *
    +   * 字段名:+店铺信息
    +   * 变量名:sales_scene_info
    +   * 是否必填:是
    +   * 类型:object
    +   * 描述:请填写店铺信息
    +   * 
    + */ + @SerializedName(value = "sales_scene_info") + private SalesSceneInfo salesSceneInfo; + + /** + *
    +   * 字段名:商户简称
    +   * 变量名:merchant_shortname
    +   * 是否必填:是
    +   * 类型:string(64)
    +   * 描述:
    +   *  UTF-8格式,中文占3个字节,即最多16个汉字长度。将在支付完成页向买家展示,需与商家的实际售卖商品相符 。
    +   *  示例值:腾讯
    +   * 
    + */ + @SerializedName(value = "merchant_shortname") + private String merchantShortname; + + /** + *
    +   * 字段名:特殊资质
    +   * 变量名:qualifications
    +   * 是否必填:否
    +   * 类型:string(1024)
    +   * 描述:
    +   *  1、若店铺业务包含互联网售药,则需上传特殊资质-《互联网药品交易服务证》。
    +   *  2、最多可上传5张照片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
    +   *  示例值:[\"jTpGmxUX3FBWVQ5NJInE4d2I6_H7I4\"]
    +   * 
    + */ + @SerializedName(value = "qualifications") + private String qualifications; + + /** + *
    +   * 字段名:补充材料
    +   * 变量名:business_addition_pics
    +   * 是否必填:否
    +   * 类型:string(1024)
    +   * 描述:
    +   *   最多可上传5张照片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
    +   *  示例值:[\"jTpGmg05InE4d2I6_H7I4\"]
    +   * 
    + */ + @SerializedName(value = "business_addition_pics") + private String businessAdditionPics; + + /** + *
    +   * 字段名:补充说明
    +   * 变量名:business_addition_desc
    +   * 是否必填:否
    +   * 类型:string(256)
    +   * 描述:
    +   *   可填写512字以内 。
    +   *  示例值:特殊情况,说明原因
    +   * 
    + */ + @SerializedName(value = "business_addition_desc") + private String businessAdditionDesc; + + @Data + @NoArgsConstructor + public static class BusinessLicenseInfo implements Serializable { + /** + *
    +     * 字段名:证件扫描件
    +     * 变量名:business_license_copy
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、主体为“个体工商户/企业”时,请上传营业执照的证件图片。
    +     *  2、主体为“党政、机关及事业单位/其他组织”时,请上传登记证书的证件图片。
    +     *  3、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
    +     *  4、图片要求:
    +     *  (1)请上传证件的彩色扫描件或彩色数码拍摄件,黑白复印件需加盖公章(公章信息需完整) 。
    +     *  (2)不得添加无关水印(非微信支付商户申请用途的其他水印)。
    +     *  (3)需提供证件的正面拍摄件,完整、照面信息清晰可见。信息不清晰、扭曲、压缩变形、反光、不完整均不接受。
    +     *  (4)不接受二次剪裁、翻拍、PS的证件照片。
    +     *  示例值: 47ZC6GC-vnrbEny__Ie_An5-tCpqxucuxi-vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
    +     * 
    + */ + @SerializedName(value = "business_license_copy") + private String businessLicenseCopy; + + /** + *
    +     * 字段名:证件注册号
    +     * 变量名:business_license_number
    +     * 是否必填:是
    +     * 类型:string(18)
    +     * 描述:
    +     *  1、主体为“个体工商户/企业”时,请填写营业执照上的注册号/统一社会信用代码,须为15位数字或 18位数字|大写字母。
    +     *  2、主体为“党政、机关及事业单位/其他组织”时,请填写登记证书的证书编号。
    +     *  示例值:123456789012345678
    +     *  特殊规则:长度最小15个字节
    +     * 
    + */ + @SerializedName(value = "business_license_number") + private String businessLicenseNumber; + + /** + *
    +     * 字段名:商户名称
    +     * 变量名:merchant_name
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  1、请填写营业执照/登记证书的商家名称,2~110个字符,支持括号 。
    +     *  2、个体工商户/党政、机关及事业单位,不能以“公司”结尾。
    +     *  3、个体工商户,若营业执照上商户名称为空或为“无”,请填写"个体户+经营者姓名",如“个体户张三” 。
    +     *  示例值:腾讯科技有限公司
    +     * 
    + */ + @SerializedName(value = "merchant_name") + private String merchantName; + + /** + *
    +     * 字段名:经营者/法定代表人姓名
    +     * 变量名:legal_person
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  请填写证件的经营者/法定代表人姓名
    +     *  示例值:张三
    +     * 
    + */ + @SerializedName(value = "legal_person") + private String legalPerson; + + /** + *
    +     * 字段名:注册地址
    +     * 变量名:company_address
    +     * 是否必填:条件选填
    +     * 类型:string(128)
    +     * 描述:
    +     *  主体为“党政、机关及事业单位/其他组织”时必填,请填写登记证书的注册地址。
    +     *  示例值:深圳南山区科苑路
    +     * 
    + */ + @SerializedName(value = "company_address") + private String companyAddress; + + /** + *
    +     * 字段名:营业期限
    +     * 变量名:business_time
    +     * 是否必填:条件选填
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、主体为“党政、机关及事业单位/其他组织”时必填,请填写证件有效期。
    +     *  2、若证件有效期为长期,请填写:长期。
    +     *  3、结束时间需大于开始时间。
    +     *  4、有效期必须大于60天,即结束时间距当前时间需超过60天。
    +     *  示例值:[\"2014-01-01\",\"长期\"]
    +     * 
    + */ + @SerializedName(value = "business_time") + private String businessTime; + + } + + @Data + @NoArgsConstructor + public static class OrganizationCertInfo implements Serializable { + /** + *
    +     * 字段名:组织机构代码证照片
    +     * 变量名:organization_copy
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID。
    +     *  示例值:vByf3Gjm7KE53JXv\prrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
    +     * 
    + */ + @SerializedName(value = "organization_copy") + private String organizationCopy; + + /** + *
    +     * 字段名:组织机构代码
    +     * 变量名:organization_number
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、请填写组织机构代码证上的组织机构代码。
    +     *  2、可填写9或10位 数字|字母|连字符。
    +     *  示例值:12345679-A
    +     * 
    + */ + @SerializedName(value = "organization_number") + private String organizationNumber; + + /** + *
    +     * 字段名:组织机构代码有效期限
    +     * 变量名:organization_time
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、请填写组织机构代码证的有效期限,注意参照示例中的格式。
    +     *  2、若证件有效期为长期,请填写:长期。
    +     *  3、结束时间需大于开始时间。
    +     *  4、有效期必须大于60天,即结束时间距当前时间需超过60天。
    +     *  示例值:[\"2014-01-01\",\"长期\"]
    +     * 
    + */ + @SerializedName(value = "organization_time") + private String organizationTime; + + } + + @Data + @NoArgsConstructor + public static class IdCardInfo implements Serializable { + /** + *
    +     * 字段名:身份证人像面照片
    +     * 变量名:id_card_copy
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、请上传经营者/法定代表人的身份证人像面照片。
    +     *  2、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID。
    +     *  示例值:xpnFuAxhBTEO_PvWkfSCJ3zVIn001D8daLC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
    +     * 
    + */ + @SerializedName(value = "id_card_copy") + private String idCardCopy; + + /** + *
    +     * 字段名:身份证国徽面照片
    +     * 变量名:id_card_national
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、请上传经营者/法定代表人的身份证国徽面照片。
    +     *  2、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
    +     *  示例值:vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
    +     * 
    + */ + @SerializedName(value = "id_card_national") + private String idCardNational; + + /** + *
    +     * 字段名:身份证姓名
    +     * 变量名:id_card_name
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、请填写经营者/法定代表人对应身份证的姓名,2~30个中文字符、英文字符、符号。
    +     *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  示例值:pVd1HJ6v/69bDnuC4EL5Kz4jBHLiCa8MRtelw/wDa4SzfeespQO/0kjiwfqdfg==
    +     *  字段加密:使用APIv3定义的方式加密
    +     * 
    + */ + @SerializedName(value = "id_card_name") + @SpecEncrypt + private String idCardName; + + /** + *
    +     * 字段名:身份证号码
    +     * 变量名:id_card_number
    +     * 是否必填:是
    +     * 类型:string(18)
    +     * 描述:
    +     *  1、请填写经营者/法定代表人对应身份证的号码。
    +     *  2、15位数字或17位数字+1位数字|X ,该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  示例值:zV+BEmytMNQCqQ8juwEc4P4TG5xzchG/5IL9DBd+Z0zZXkw==4
    +     *  特殊规则:长度最小15个字节
    +     * 
    + */ + @SerializedName(value = "id_card_number") + @SpecEncrypt + private String idCardNumber; + + /** + *
    +     * 字段名:身份证有效期限
    +     * 变量名:id_card_valid_time
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  1、请填写身份证有效期的结束时间,注意参照示例中的格式。
    +     *  2、若证件有效期为长期,请填写:长期。
    +     *  3、证件有效期需大于60天。
    +     *  示例值:2026-06-06,长期
    +     * 
    + */ + @SerializedName(value = "id_card_valid_time") + private String idCardValidTime; + + } + + @Data + @NoArgsConstructor + public static class IdDocInfo implements Serializable { + /** + *
    +     * 字段名:证件姓名
    +     * 变量名:id_doc_name
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  请填写经营者/法人姓名。
    +     *  示例值:jTpGmxUX3FBWVQ5NJTZvlKX_gdU4LC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
    +     * 
    + */ + @SerializedName(value = "id_doc_name") + private String idDocName; + + /** + *
    +     * 字段名:证件号码
    +     * 变量名:id_doc_number
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  7~11位 数字|字母|连字符 。
    +     *  示例值:jTpGmxUX3FBWVQ5NJTZvlKX_go0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
    +     * 
    + */ + @SerializedName(value = "id_doc_number") + private String idDocNumber; + + /** + *
    +     * 字段名:证件照片
    +     * 变量名:id_doc_copy
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID。
    +     *  2、2M内的彩色图片,格式可为bmp、png、jpeg、jpg或gif 。
    +     *  示例值:xi-vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
    +     * 
    + */ + @SerializedName(value = "id_doc_copy") + private String idDocCopy; + + /** + *
    +     * 字段名:证件结束日期
    +     * 变量名:doc_period_end
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  1、请按照示例值填写。
    +     *  2、若证件有效期为长期,请填写:长期。
    +     *  3、证件有效期需大于60天 。
    +     *  示例值:2020-01-02
    +     * 
    + */ + @SerializedName(value = "doc_period_end") + private String docPeriodEnd; + + } + + @Data + @NoArgsConstructor + public static class AccountInfo implements Serializable { + /** + *
    +     * 字段名:账户类型
    +     * 变量名:bank_account_type
    +     * 是否必填:是
    +     * 类型:string(2)
    +     * 描述:
    +     *  1、若主体为企业/党政、机关及事业单位/其他组织,可填写:74-对公账户。
    +     *  2、若主体为小微,可填写:75-对私账户。
    +     *  3、若主体为个体工商户,可填写:74-对公账户或75-对私账户。
    +     *  示例值:75
    +     * 
    + */ + @SerializedName(value = "bank_account_type") + private String bankAccountType; + + /** + *
    +     * 字段名:开户银行
    +     * 变量名:account_bank
    +     * 是否必填:是
    +     * 类型:string(10)
    +     * 描述:
    +     *  详细参见开户银行对照表。
    +     *  示例值:工商银行
    +     * 
    + */ + @SerializedName(value = "account_bank") + private String accountBank; + + /** + *
    +     * 字段名:开户名称
    +     * 变量名:account_name
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  1、选择经营者个人银行卡时,开户名称必须与身份证姓名一致。
    +     *  2、选择对公账户时,开户名称必须与营业执照上的“商户名称”一致。
    +     *  3、该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  示例值:AOZdYGISxo4yw96uY1Pk7Rq79Jtt7+I8juwEc4P4TG5xzchG/5IL9DBd+Z0zZXkw==
    +     * 
    + */ + @SerializedName(value = "account_name") + @SpecEncrypt + private String accountName; + + /** + *
    +     * 字段名:开户银行省市编码
    +     * 变量名:bank_address_code
    +     * 是否必填:是
    +     * 类型:string(12)
    +     * 描述:
    +     *  至少精确到市,详细参见省市区编号对照表。
    +     *  示例值:110000
    +     * 
    + */ + @SerializedName(value = "bank_address_code") + private String bankAddressCode; + + /** + *
    +     * 字段名:开户银行联行号
    +     * 变量名:bank_branch_id
    +     * 是否必填:条件选填
    +     * 类型:string(64)
    +     * 描述:
    +     *  1、17家直连银行无需填写,如为其他银行,开户银行全称(含支行)和开户银行联行号二选一。
    +     *  2、详细参见开户银行全称(含支行)对照表。
    +     *  示例值:402713354941
    +     * 
    + */ + @SerializedName(value = "bank_branch_id") + private String bankBranchId; + + /** + *
    +     * 字段名:开户银行全称 (含支行)
    +     * 变量名:bank_name
    +     * 是否必填:条件选填
    +     * 类型:string(128)
    +     * 描述:
    +     *  1、17家直连银行无需填写,如为其他银行,开户银行全称(含支行)和开户银行联行号二选一。
    +     *  2、需填写银行全称,如"深圳农村商业银行XXX支行" 。
    +     *  3、详细参见开户银行全称(含支行)对照表。
    +     *  示例值:施秉县农村信用合作联社城关信用社
    +     * 
    + */ + @SerializedName(value = "bank_name") + private String bankName; + + /** + *
    +     * 字段名:银行帐号
    +     * 变量名:account_number
    +     * 是否必填:是
    +     * 类型:string(128)
    +     * 描述:
    +     *  1、数字,长度遵循系统支持的对公/对私卡号长度要求表。
    +     *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  示例值: d+xT+MQCvrLHUVDWv/8MR/dB7TkXLVfSrUxMPZy6jWWYzpRrEEaYQE8ZRGYoeorwC+w==
    +     * 
    + */ + @SerializedName(value = "account_number") + @SpecEncrypt + private String accountNumber; + + } + + @Data + @NoArgsConstructor + public static class ContactInfo implements Serializable { + /** + *
    +     * 字段名:超级管理员类型
    +     * 变量名:contact_type
    +     * 是否必填:是
    +     * 类型:string(2)
    +     * 描述:
    +     *  1、小微商户,选择:65-法人/经营者。
    +     *  2、个体工商户/企业/党政、机关及事业单位/其他组织,可选择:65-法人/经营者、66- 负责人。 (负责人:经商户授权办理微信支付业务的人员,授权范围包括但不限于签约,入驻过程需完成账户验证)。
    +     *  示例值:65
    +     * 
    + */ + @SerializedName(value = "contact_type") + private String contactType; + + /** + *
    +     * 字段名:超级管理员姓名
    +     * 变量名:contact_name
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、若管理员类型为“法人”,则该姓名需与法人身份证姓名一致。
    +     *  2、若管理员类型为“经办人”,则可填写实际经办人的姓名。
    +     *  3、该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  (后续该管理员需使用实名微信号完成签约)
    +     *  示例值: pVd1HJ6zyvPedzGaV+X3IdGdbDnuC4Eelw/wDa4SzfeespQO/0kjiwfqdfg==
    +     * 
    + */ + @SerializedName(value = "contact_name") + @SpecEncrypt + private String contactName; + + /** + *
    +     * 字段名:超级管理员身份证件号码
    +     * 变量名:contact_id_card_number
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、若管理员类型为法人,则该身份证号码需与法人身份证号码一致。若管理员类型为经办人,则可填写实际经办人的身份证号码。
    +     *  2、可传身份证、来往内地通行证、来往大陆通行证、护照等证件号码。
    +     *  3、超级管理员签约时,校验微信号绑定的银行卡实名信息,是否与该证件号码一致。
    +     *  4、该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  示例值:pVd1HJ6zmty7/mYNxLMpRSvMRtelw/wDa4SzfeespQO/0kjiwfqdfg==
    +     * 
    + */ + @SerializedName(value = "contact_id_card_number") + @SpecEncrypt + private String contactIdCardNumber; + + /** + *
    +     * 字段名:超级管理员手机
    +     * 变量名:mobile_phone
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、请填写管理员的手机号,11位数字, 用于接收微信支付的重要管理信息及日常操作验证码 。
    +     *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  示例值:pVd1HJ6zyvPedzGaV+X3qtmrq9bb9tPROvwia4ibL+F6mfjbzQIzfb3HHLEjZ4YiNWWNeespQO/0kjiwfqdfg==
    +     * 
    + */ + @SerializedName(value = "mobile_phone") + @SpecEncrypt + private String mobilePhone; + + /** + *
    +     * 字段名:超级管理员邮箱
    +     * 变量名:contact_email
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  1、用于接收微信支付的开户邮件及日常业务通知。
    +     *  2、需要带@,遵循邮箱格式校验 。
    +     *  3、该字段需进行加密处理,加密方法详见敏感信息加密说明。
    +     *  示例值:pVd1HJ6zyvPedzGaV+X3qtmrq9bb9tPROvwia4ibL+FWWNUlw/wDa4SzfeespQO/0kjiwfqdfg==
    +     * 
    + */ + @SerializedName(value = "contact_email") + @SpecEncrypt + private String contactEmail; + + } + + @Data + @NoArgsConstructor + public static class SalesSceneInfo implements Serializable { + /** + *
    +     * 字段名:店铺名称
    +     * 变量名:store_name
    +     * 是否必填:是
    +     * 类型:string(256)
    +     * 描述:
    +     *  请填写店铺全称。
    +     *  示例值:爱烧烤
    +     * 
    + */ + @SerializedName(value = "store_name") + private String storeName; + + /** + *
    +     * 字段名:店铺链接
    +     * 变量名:store_url
    +     * 是否必填:二选一
    +     * 类型:string(1024)
    +     * 描述:
    +     *  1、店铺二维码or店铺链接二选一必填。
    +     *  2、请填写店铺主页链接,需符合网站规范。
    +     *  示例值:http://www.qq.com
    +     * 
    + */ + @SerializedName(value = "store_url") + private String storeUrl; + + /** + *
    +     * 字段名:店铺二维码
    +     * 变量名:store_qr_code
    +     * 是否必填:1、店铺二维码 or 店铺链接二选一必填。 2、若为电商小程序,可上传店铺页面的小程序二维码。 3、请填写通过图片上传接口预先上传图片生成好的MediaID,仅能上传1张图片 。 示例值:jTpGmxUX3FBWVQ5NJTZvlKX_gdU4cRz7z5NxpnFuAxhBTEO1D8daLC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
    +     * 类型:string(256)
    +     * 描述:
    +     * 
    + */ + @SerializedName(value = "store_qr_code") + private String storeQrCode; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsResult.java new file mode 100644 index 0000000000..38c8a33745 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsResult.java @@ -0,0 +1,43 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 二级商户进件 提交申请结果响应 + */ +@Data +@NoArgsConstructor +public class ApplymentsResult implements Serializable { + private static final long serialVersionUID = -4549193755252593150L; + /** + *
    +   * 字段名:微信支付申请单号
    +   * 变量名:applyment_id
    +   * 是否必填:是
    +   * 类型:uint64
    +   * 描述:
    +   *  微信支付分配的申请单号 。
    +   *  示例值:2000002124775691
    +   * 
    + */ + @SerializedName(value = "applyment_id") + private String applymentId; + + /** + *
    +   * 字段名:业务申请编号
    +   * 变量名:out_request_no
    +   * 是否必填:是
    +   * 类型:string(124)
    +   * 描述:
    +   *  服务商自定义的商户唯一编号。每个编号对应一个申请单,每个申请单审核通过后会生成一个微信支付商户号。
    +   *  示例值:APPLYMENT_00000000001
    +   * 
    + */ + @SerializedName(value = "out_request_no") + private String outRequestNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java new file mode 100644 index 0000000000..7defd21452 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java @@ -0,0 +1,310 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +@Data +@NoArgsConstructor +public class ApplymentsStatusResult implements Serializable { + /** + *
    +   * 字段名:申请状态
    +   * 变量名:applyment_state
    +   * 是否必填:否
    +   * 类型:string(32)
    +   * 描述:
    +   *  枚举值:
    +   *  CHECKING:资料校验中
    +   *  ACCOUNT_NEED_VERIFY:待账户验证
    +   *  AUDITING:审核中
    +   *  REJECTED:已驳回
    +   *  NEED_SIGN:待签约
    +   *  FINISH:完成
    +   *  FROZEN:已冻结
    +   *  示例值:FINISH
    +   * 
    + */ + @SerializedName(value = "applyment_state") + private String applymentState; + + /** + *
    +   * 字段名:申请状态描述
    +   * 变量名:applyment_state_desc
    +   * 是否必填:否
    +   * 类型:string(32)
    +   * 描述:
    +   *  申请状态描述
    +   *  示例值:“审核中”
    +   * 
    + */ + @SerializedName(value = "applyment_state_desc") + private String applymentStateDesc; + + /** + *
    +   * 字段名:签约链接
    +   * 变量名:sign_url
    +   * 是否必填:否
    +   * 类型:string(256)
    +   * 描述:
    +   *  1、当申请状态为NEED_SIGN时才返回。
    +   *  2、建议将链接转为二维码展示,需让申请单-管理者用微信扫码打开,完成签约。
    +   *  示例值:https://pay.weixin.qq.com/public/apply4ec_sign/s?applymentId=2000002126198476&sign=b207b673049a32c858f3aabd7d27c7ec
    +   * 
    + */ + @SerializedName(value = "sign_url") + private String signUrl; + + /** + *
    +   * 字段名:电商平台二级商户号
    +   * 变量名:sub_mchid
    +   * 是否必填:否
    +   * 类型:string(32)
    +   * 描述:
    +   *  当申请状态为NEED_SIGN或FINISH时才返回。
    +   *  示例值:1542488631
    +   * 
    + */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
    +   * 字段名:+汇款账户验证信息
    +   * 变量名:account_validation
    +   * 是否必填:否
    +   * 类型:object
    +   * 描述:当申请状态为ACCOUNT_NEED_VERIFY 时有返回,可根据指引汇款,完成账户验证。
    +   * 
    + */ + @SerializedName(value = "account_validation") + private AccountValidation accountValidation; + + /** + *
    +   * 字段名:+驳回原因详情
    +   * 变量名:audit_detail
    +   * 是否必填:否
    +   * 类型:array
    +   * 描述:各项资料的审核情况。当申请状态为REJECTED或 FROZEN时才返回。
    +   * 
    + */ + @SerializedName(value = "audit_detail") + private List auditDetail; + + /** + *
    +   * 字段名:法人验证链接
    +   * 变量名:legal_validation_url
    +   * 是否必填:否
    +   * 类型:string(256)
    +   * 描述:
    +   *  1、当申请状态为
    +   *  ACCOUNT_NEED_VERIFY,且通过系统校验的申请单,将返回链接。
    +   *  2、建议将链接转为二维码展示,让商户法人用微信扫码打开,完成账户验证。
    +   *  示例值: https://pay.weixin.qq.com/public/apply4ec_sign/s?applymentId=2000002126198476&sign=b207b673049a32c858f3aabd7d27c7ec
    +   * 
    + */ + @SerializedName(value = "legal_validation_url") + private String legalValidationUrl; + + /** + *
    +   * 字段名:业务申请编号
    +   * 变量名:out_request_no
    +   * 是否必填:是
    +   * 类型:string(124)
    +   * 描述:
    +   *  提交接口填写的业务申请编号。
    +   *  示例值:APPLYMENT_00000000001
    +   * 
    + */ + @SerializedName(value = "out_request_no") + private String outRequestNo; + + /** + *
    +   * 字段名:微信支付申请单号
    +   * 变量名:applyment_id
    +   * 是否必填:否
    +   * 类型:uint64
    +   * 描述:
    +   *  微信支付分配的申请单号。
    +   *  示例值:2000002124775691
    +   * 
    + */ + @SerializedName(value = "applyment_id") + private String applymentId; + + @Data + @NoArgsConstructor + public static class AccountValidation implements Serializable { + private static final long serialVersionUID = 4379880030965808588L; + /** + *
    +     * 字段名:付款户名
    +     * 变量名:account_name
    +     * 是否必填:否
    +     * 类型:uint64
    +     * 描述:
    +     *  需商户使用该户名的账户进行汇款。
    +     *  示例值: rDdICA3ZYXshYqeOSslSjSMf+MhhC4oaujiISFzq3AE+as7mAEDJly+DgRuVs74msmKUH8pl+3oA==
    +     * 
    + */ + @SerializedName(value = "account_name") + private String accountName; + + /** + *
    +     * 字段名:付款卡号
    +     * 变量名:account_no
    +     * 是否必填:否
    +     * 类型:string(128)
    +     * 描述:
    +     *  结算账户为对私时会返回,商户需使用该付款卡号进行汇款。
    +     *  示例值:9nZYDEvBT4rDdICA3ZYXshYqeOSslSjSauAE+as7mAEDJly+DgRuVs74msmKUH8pl+3oA==
    +     * 
    + */ + @SerializedName(value = "account_no") + private String accountNo; + + /** + *
    +     * 字段名:汇款金额
    +     * 变量名:pay_amount
    +     * 是否必填:否
    +     * 类型:string(32)
    +     * 描述:
    +     *  需要汇款的金额(单位:分)。
    +     *  示例值:124
    +     * 
    + */ + @SerializedName(value = "pay_amount") + private String payAmount; + + /** + *
    +     * 字段名:收款卡号
    +     * 变量名:destination_account_number
    +     * 是否必填:否
    +     * 类型:string(128)
    +     * 描述:
    +     *  收款账户的卡号
    +     *  示例值:7222223333322332
    +     * 
    + */ + @SerializedName(value = "destination_account_number") + private String destinationAccountNumber; + + /** + *
    +     * 字段名:收款户名
    +     * 变量名:destination_account_name
    +     * 是否必填:否
    +     * 类型:string(128)
    +     * 描述:
    +     *  收款账户名
    +     *  示例值:财付通支付科技有限公司
    +     * 
    + */ + @SerializedName(value = "destination_account_name") + private String destinationAccountName; + + /** + *
    +     * 字段名:开户银行
    +     * 变量名:destination_account_bank
    +     * 是否必填:否
    +     * 类型:string(128)
    +     * 描述:
    +     *  收款账户的开户银行名称。
    +     *  示例值:招商银行威盛大厦支行
    +     * 
    + */ + @SerializedName(value = "destination_account_bank") + private String destinationAccountBank; + + /** + *
    +     * 字段名:省市信息
    +     * 变量名:city
    +     * 是否必填:否
    +     * 类型:string(128)
    +     * 描述:
    +     *  收款账户的省市。
    +     *  示例值:深圳
    +     * 
    + */ + @SerializedName(value = "city") + private String city; + + /** + *
    +     * 字段名:备注信息
    +     * 变量名:remark
    +     * 是否必填:否
    +     * 类型:string(128)
    +     * 描述:
    +     *  商户汇款时,需要填写的备注信息。
    +     *  示例值:入驻账户验证
    +     * 
    + */ + @SerializedName(value = "remark") + private String remark; + + /** + *
    +     * 字段名:汇款截止时间
    +     * 变量名:deadline
    +     * 是否必填:否
    +     * 类型:string(20)
    +     * 描述:
    +     *  请在此时间前完成汇款。
    +     *  示例值:2018-12-1017:09:01
    +     * 
    + */ + @SerializedName(value = "deadline") + private String deadline; + + } + + @Data + @NoArgsConstructor + public static class AuditDetail implements Serializable { + private static final long serialVersionUID = 5446130564359386809L; + /** + *
    +     * 字段名:参数名称
    +     * 变量名:param_name
    +     * 是否必填:否
    +     * 类型:string(32)
    +     * 描述:
    +     *  提交申请单的资料项名称。
    +     *  示例值:id_card_copy
    +     * 
    + */ + @SerializedName(value = "param_name") + private String paramName; + + /** + *
    +     * 字段名:驳回原因
    +     * 变量名:reject_reason
    +     * 是否必填:否
    +     * 类型:string(32)
    +     * 描述:
    +     *  提交资料项被驳回原因。
    +     *  示例值:身份证背面识别失败,请上传更清晰的身份证图片
    +     * 
    + */ + @SerializedName(value = "reject_reason") + private String rejectReason; + + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryRequest.java new file mode 100644 index 0000000000..83d7419261 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryRequest.java @@ -0,0 +1,27 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + *
    + * 企业付款到银行卡的请求对象
    + * Created by Binary Wang on 2017/12/21.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class EntPayBankQueryRequest extends EntPayQueryRequest { + private static final long serialVersionUID = -479088843124447119L; + + @Override + protected boolean ignoreAppid() { + return true; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryResult.java new file mode 100644 index 0000000000..b7666a545b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryResult.java @@ -0,0 +1,110 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 企业付款到银行卡查询返回结果.
    + * Created by Binary Wang on 2017/12/21.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class EntPayBankQueryResult extends BaseWxPayResult { + private static final long serialVersionUID = -8336631015989500746L; + + /** + * 商户企业付款单号 + */ + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + * 微信企业付款单号. + * 即为微信内部业务单号 + */ + @XStreamAlias("payment_no") + private String paymentNo; + + /** + * 银行卡号. + * 收款用户银行卡号(MD5加密) + */ + @XStreamAlias("bank_no_md5") + private String bankNoMd5; + + /** + * 用户真实姓名. + * 收款人真实姓名(MD5加密) + */ + @XStreamAlias("true_name_md5") + private String trueNameMd5; + + /** + * 付款金额. + */ + @XStreamAlias("amount") + private Integer amount; + + /** + * 代付单状态. + *
    +   * PROCESSING(处理中,如有明确失败,则返回额外失败原因;否则没有错误原因)
    +   * SUCCESS(付款成功)
    +   * FAILED(付款失败)
    +   * BANK_FAIL(银行退票,订单状态由付款成功流转至退票,退票时付款金额和手续费会自动退还)
    +   * 
    + */ + @XStreamAlias("status") + private String status; + + /** + * 手续费金额 + */ + @XStreamAlias("cmms_amt") + private Integer cmmsAmount; + + /** + * 商户下单时间. + * 微信侧订单创建时间 + */ + @XStreamAlias("create_time") + private String createTime; + + /** + * 成功付款时间. + * 微信侧付款成功时间(但无法保证银行不会退票) + */ + @XStreamAlias("pay_succ_time") + private String paySuccessTime; + + /** + * 失败原因. + * 订单失败原因(如:余额不足) + */ + @XStreamAlias("reason") + private String failReason; + + @Override + protected void loadXml(Document d) { + partnerTradeNo = readXmlString(d, "partner_trade_no"); + paymentNo = readXmlString(d, "payment_no"); + bankNoMd5 = readXmlString(d, "bank_no_md5"); + trueNameMd5 = readXmlString(d, "true_name_md5"); + amount = readXmlInteger(d, "amount"); + status = readXmlString(d, "status"); + cmmsAmount = readXmlInteger(d, "cmms_amt"); + createTime = readXmlString(d, "create_time"); + paySuccessTime = readXmlString(d, "pay_succ_time"); + failReason = readXmlString(d, "reason"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankRequest.java new file mode 100644 index 0000000000..04c26403c2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankRequest.java @@ -0,0 +1,140 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
    + *  企业付款到银行卡的请求对象类
    + *  Created by BinaryWang on 2017/12/20.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +@XStreamAlias("xml") +public class EntPayBankRequest extends BaseWxPayRequest { + + /** + *
    +   * 商户企业付款单号.
    +   * 变量名:partner_trade_no
    +   * 是否必填:是
    +   * 示例值:1212121221227
    +   * 类型:string(32)
    +   * 描述:商户订单号,需保持唯一(只允许数字[0~9]或字母[A~Z]和[a~z],最短8位,最长32位)
    +   * 
    + */ + @Required + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + *
    +   * 收款方银行卡号.
    +   * 传值时请传原始值
    +   * 变量名:enc_bank_no
    +   * 是否必填:是
    +   * 示例值:8609cb22e1774a50a930e414cc71eca06121bcd266335cda230d24a7886a8d9f
    +   * 类型:string(64)
    +   * 描述:收款方银行卡号(采用标准RSA算法,公钥由微信侧提供),详见获取RSA加密公钥API
    +   * 
    + */ + @Required + @XStreamAlias("enc_bank_no") + private String encBankNo; + + /** + *
    +   * 收款方用户名.
    +   * 传值时请传原始值
    +   * 变量名:enc_true_name
    +   * 是否必填:是
    +   * 示例值:ca775af5f841bdf424b2e6eb86a6e21e
    +   * 类型:string(64)
    +   * 描述:收款方用户名(采用标准RSA算法,公钥由微信侧提供)详见获取RSA加密公钥API
    +   * 
    + */ + @Required + @XStreamAlias("enc_true_name") + private String encTrueName; + + /** + *
    +   * 收款方开户行.
    +   * 变量名:bank_code
    +   * 是否必填:是
    +   * 示例值:1001
    +   * 类型:string(64)
    +   * 描述:银行卡所在开户行编号,详见银行编号列表
    +   * 
    + */ + @Required + @XStreamAlias("bank_code") + private String bankCode; + + /** + *
    +   * 付款金额.
    +   * 变量名:amount
    +   * 是否必填:是
    +   * 示例值:100000
    +   * 类型:int
    +   * 描述:付款金额:RMB分(支付总额,不含手续费) 注:大于0的整数
    +   * 
    + */ + @Required + @XStreamAlias("amount") + private Integer amount; + + /** + *
    +   * 付款说明.
    +   * 变量名:desc
    +   * 是否必填:否
    +   * 示例值:理财
    +   * 类型:string
    +   * 描述:企业付款到银行卡付款说明,即订单备注(UTF8编码,允许100个字符以内)
    +   * 
    + */ + @Required + @XStreamAlias("desc") + private String description; + + @Override + protected void checkConstraints() throws WxPayException { + } + + @Override + protected String[] getIgnoredParamsForSign() { + return new String[]{"sign_type"}; + } + + @Override + protected void storeMap(Map map) { + map.put("partner_trade_no", partnerTradeNo); + map.put("enc_bank_no", encBankNo); + map.put("enc_true_name", encTrueName); + map.put("bank_code", bankCode); + map.put("amount", amount.toString()); + map.put("desc", description); + } + + @Override + protected boolean ignoreAppid() { + return true; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankResult.java new file mode 100644 index 0000000000..44fc6fa7cb --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankResult.java @@ -0,0 +1,59 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 企业付款到银行卡的响应结果.
    + * Created by Binary Wang on 2017/12/21.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class EntPayBankResult extends BaseWxPayResult { + private static final long serialVersionUID = 3449707749935227689L; + + /** + * 代付金额. + */ + @XStreamAlias("amount") + private Integer amount; + + /** + * 商户企业付款单号. + */ + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + //############以下字段在return_code 和result_code都为SUCCESS的时候有返回############## + /** + * 微信企业付款单号. + * 代付成功后,返回的内部业务单号 + */ + @XStreamAlias("payment_no") + private String paymentNo; + + /** + * 手续费金额. + * RMB:分 + */ + @XStreamAlias("cmms_amt") + private Integer cmmsAmount; + + @Override + protected void loadXml(Document d) { + amount = readXmlInteger(d, "amount"); + partnerTradeNo = readXmlString(d, "partner_trade_no"); + paymentNo = readXmlString(d, "payment_no"); + cmmsAmount = readXmlInteger(d, "cmms_amt"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryRequest.java new file mode 100644 index 0000000000..779d59823b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryRequest.java @@ -0,0 +1,65 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.annotation.Required; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.util.Map; + +/** + *
    + * 企业付款请求对象.
    + * Created by Binary Wang on 2016/10/19.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class EntPayQueryRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 1972288742207813985L; + + /** + *
    +   * 字段名:商户订单号.
    +   * 变量名:partner_trade_no
    +   * 是否必填:是
    +   * 示例值:10000098201411111234567890
    +   * 类型:String
    +   * 描述商户订单号
    +   * 
    + */ + @Required + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + @Override + protected String[] getIgnoredParamsForSign() { + return new String[]{"sign_type"}; + } + + @Override + protected void storeMap(Map map) { + map.put("partner_trade_no", partnerTradeNo); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryResult.java new file mode 100644 index 0000000000..5fe7e05bae --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryResult.java @@ -0,0 +1,98 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 企业付款查询返回结果.
    + * Created by Binary Wang on 2016/10/19.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class EntPayQueryResult extends BaseWxPayResult { + private static final long serialVersionUID = 3948485732447456947L; + + /** + * 商户订单号. + */ + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + * 付款单号. + */ + @XStreamAlias("detail_id") + private String detailId; + + /** + * 转账状态. + */ + @XStreamAlias("status") + private String status; + + /** + * 失败原因. + */ + @XStreamAlias("reason") + private String reason; + + /** + * 收款用户openid. + */ + @XStreamAlias("openid") + private String openid; + + /** + * 收款用户姓名. + */ + @XStreamAlias("transfer_name") + private String transferName; + + /** + * 付款金额. + */ + @XStreamAlias("payment_amount") + private Integer paymentAmount; + + /** + * 发起转账的时间. + */ + @XStreamAlias("transfer_time") + private String transferTime; + + /** + * 企业付款成功时间. + */ + @XStreamAlias("payment_time") + private String paymentTime; + + /** + * 付款描述. + */ + @XStreamAlias("desc") + private String desc; + + @Override + protected void loadXml(Document d) { + partnerTradeNo = readXmlString(d, "partner_trade_no"); + detailId = readXmlString(d, "detail_id"); + status = readXmlString(d, "status"); + reason = readXmlString(d, "reason"); + openid = readXmlString(d, "openid"); + transferName = readXmlString(d, "transfer_name"); + paymentAmount = readXmlInteger(d, "payment_amount"); + transferTime = readXmlString(d, "transfer_time"); + paymentTime = readXmlString(d, "payment_time"); + desc = readXmlString(d, "desc"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryRequest.java new file mode 100644 index 0000000000..74e5b4b1a0 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryRequest.java @@ -0,0 +1,41 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; + +import java.util.Map; + +/** + * 红包发送记录查询请求 + * + * @author wuyong + * @date 2019-12-01 17:19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class EntPayRedpackQueryRequest extends BaseWxPayRequest { + + + /** + * 商户订单号 + */ + @XStreamAlias("mch_billno") + private String mchBillNo; + + + @Override + protected void checkConstraints() throws WxPayException { + + } + + @Override + protected void storeMap(Map map) { + map.put("mch_billno", mchBillNo); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryResult.java new file mode 100644 index 0000000000..6e970e3fe8 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryResult.java @@ -0,0 +1,154 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + * 红包发送记录查询返回 + * + * @author wuyong + * @date 2019-12-01 17:23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class EntPayRedpackQueryResult extends BaseWxPayResult { + + /** + * 商户订单号 + * 商户使用查询API填写的商户单号的原路返回 + */ + @XStreamAlias("mch_billno") + protected String mchBillNo; + + /** + * 红包单号 + * 使用API发放现金红包时返回的红包单号 + */ + @XStreamAlias("detailId") + private String detailId; + /** + * 红包状态 + * SENDING:发放 + * SENT: + * 已发放待领取 + * FAILED:发放失败 + * RECEIVED:已领取 + * RFUND_ING:退款中 REFUND:已退款 + */ + @XStreamAlias("status") + private String status; + + /** + * 发放类型 + * API:通过API接口发放 + */ + @XStreamAlias("send_type") + private String sendType; + + /** + * 红包金额 + * 红包总金额(单位分) + */ + @XStreamAlias("total_amount") + private Integer totalAmount; + + /** + * 失败原因 + * 发送失败原因 + */ + @XStreamAlias("reason") + private Integer reason; + + /** + * 红包发送时间 + */ + @XStreamAlias("send_time") + private String sendTime; + /** + * 红包的退款时间 + */ + @XStreamAlias("refund_time") + private String refundTime; + + /** + * 红包退款金额 + */ + @XStreamAlias("refund_amount") + private Integer refundAmount; + + /** + * 祝福语 + */ + @XStreamAlias("wishing") + private String wishing; + + /** + * 备注 + */ + @XStreamAlias("remark") + private String remark; + + /** + * 活动名称 + */ + @XStreamAlias("act_name") + private String actName; + + /** + * 领取红包的Openid + */ + @XStreamAlias("openid") + private String openid; + + /** + * 金额 + */ + @XStreamAlias("amount") + private Integer amount; + + /** + * 接收时间 + */ + @XStreamAlias("rcv_time") + private Integer rcvTime; + + /** + * 发送者名称 + */ + @XStreamAlias("sender_name") + private Integer senderName; + + /** + * 发送者头像 + * 通过企业微信开放接口上传获取 + */ + @XStreamAlias("sender_header_media_id") + private Integer senderHeaderMediaId; + + @Override + protected void loadXml(Document d) { + mchBillNo = readXmlString(d, "mch_billno"); + detailId = readXmlString(d, "detailId"); + status = readXmlString(d, "status"); + sendType = readXmlString(d, "send_type"); + totalAmount = readXmlInteger(d, "total_amount"); + reason = readXmlInteger(d, "reason"); + sendTime = readXmlString(d, "send_time"); + refundTime = readXmlString(d, "refund_time"); + refundAmount = readXmlInteger(d, "refund_amount"); + wishing = readXmlString(d, "wishing"); + remark = readXmlString(d, "remark"); + actName = readXmlString(d, "act_name"); + openid = readXmlString(d, "openid"); + amount = readXmlInteger(d, "amount"); + rcvTime = readXmlInteger(d, "rcv_time"); + senderName = readXmlInteger(d, "sender_name"); + senderHeaderMediaId = readXmlInteger(d, "sender_header_media_id"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackRequest.java new file mode 100644 index 0000000000..762499e693 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackRequest.java @@ -0,0 +1,165 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + * 发送企业红包 + * + * @author wuyong + * @date 2019-12-1 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class EntPayRedpackRequest extends BaseWxPayRequest { + + private static final long serialVersionUID = 1L; + + @Override + protected void checkConstraints() throws WxPayException { + + } + + /** + * 商户订单号(每个订单号必须唯一) + * 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 + * 必填:是 + */ + @Required + @XStreamAlias("mch_billno") + private String mchBillNo; + + /** + * 微信分配的公众账号ID(企业微信corpid即为此appId) + * 必填:是 + */ + @Required + @XStreamAlias("wxappid") + private String wxAppId; + + /** + * 发送者名称 + * 以个人名义发红包,红包发送者名称(需要utf-8格式)。与agentid互斥,二者只能填一个。 + * 必填:否 + */ + @XStreamAlias("sender_name") + private String senderName; + + /** + * 发送红包的应用id + * 以企业应用的名义发红包,企业应用id,整型,可在企业微信管理端应用的设置页面查看。与sender_name互斥,二者只能填一个。 + * 必填:否 + */ + @XStreamAlias("agentid") + private String agentId; + + /** + * 发送者头像 + * 发送者头像素材id,通过企业微信开放上传素材接口获取 + * 必填:否 + */ + @XStreamAlias("sender_header_media_id") + private String senderHeaderMediaId; + + /** + * 用户openid + * 接受红包的用户.用户在wxappid下的openid。 + * 必填:是 + */ + @Required + @XStreamAlias("re_openid") + private String reOpenid; + + /** + * 金额 + * 单位分,单笔最小金额默认为1元 + * 必填:是 + */ + @Required + @XStreamAlias("total_amount") + private Integer totalAmount; + + /** + * 红包祝福语 + * 必填:是 + */ + @Required + @XStreamAlias("wishing") + private String wishing; + + /** + * 项目名称 + * 必填:是 + */ + @Required + @XStreamAlias("act_name") + private String actName; + + /** + * 备注 + * 必填:是 + */ + @Required + @XStreamAlias("remark") + private String remark; + + /** + * 场景 + * 发放红包使用场景,红包金额大于200时必传 + * PRODUCT_1:商品促销 + * PRODUCT_2:抽奖 + * PRODUCT_3:虚拟物品兑奖 + * PRODUCT_4:企业内部福利 + * PRODUCT_5:渠道分润 + * PRODUCT_6:保险回馈 + * PRODUCT_7:彩票派奖 + * PRODUCT_8:税务刮奖 + */ + @XStreamAlias("scene_id") + private String sceneId; + + + @Override + protected boolean ignoreAppid() { + return true; + } + + @Override + protected boolean ignoreSubAppId() { + return true; + } + + @Override + protected boolean ignoreSubMchId() { + return true; + } + + @Override + protected boolean isWxWorkSign() { + return true; + } + + @Override + protected void storeMap(Map map) { + map.put("mch_billno", mchBillNo); + map.put("wxappid", wxAppId); + map.put("sender_name", senderName); + map.put("agentid", agentId); + map.put("sender_header_media_id", senderHeaderMediaId); + map.put("re_openid", reOpenid); + map.put("total_amount", totalAmount.toString()); + map.put("wishing", wishing); + map.put("act_name", actName); + map.put("remark", remark); + map.put("scene_id", sceneId); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackResult.java new file mode 100644 index 0000000000..8086ef9604 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackResult.java @@ -0,0 +1,93 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; + +/** + * 企业微信红包返回 + * + * @author wuyong + * @date 2019-12-01 11:31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class EntPayRedpackResult extends BaseWxPayResult implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 商户订单号 + * 商户订单号(每个订单号必须唯一)组成:mch_id+yyyymmdd+10位一天内不能重复的数字 + */ + @XStreamAlias("mch_billno") + private String mchBillNo; + + /** + * 商户号 + * 微信支付分配的商户号 + */ + @XStreamAlias("mch_id") + private String mchId; + + /** + * 公众账号appid + * 商户appid,接口传入的所有appid应该为公众号的appid,不能为APP的appid + */ + @XStreamAlias("wxappid") + private String wxAppId; + + /** + * 用户openid + * 接受收红包的用户在wxappid下的openid + */ + @XStreamAlias("re_openid") + private String reOpenid; + + /** + * 付款金额 + * 付款金额,单位分 + */ + @XStreamAlias("totalAmount") + private String totalAmount; + + /** + * 微信单号 + * 红包订单的微信单号 + */ + @XStreamAlias("sendListid") + private String sendListId; + + /** + * 发送者名称 + * 红包发送者名称(需要utf-8格式) + */ + @XStreamAlias("sender_name") + private String senderName; + + /** + * 发送者头像 + * 发送者头像素材id,通过企业微信开放上传素材接口获取 + */ + @XStreamAlias("sender_header_media_id") + private String senderHeaderMediaId; + + @Override + protected void loadXml(Document d) { + mchBillNo = readXmlString(d, "mch_billno"); + mchId = readXmlString(d, "mch_id"); + wxAppId = readXmlString(d, "wxappid"); + reOpenid = readXmlString(d, "re_openid"); + totalAmount = readXmlString(d, "totalAmount"); + sendListId = readXmlString(d, "sendListid"); + senderName = readXmlString(d, "sender_name"); + senderHeaderMediaId = readXmlString(d, "sender_header_media_id"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequest.java new file mode 100644 index 0000000000..79ca491ecf --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequest.java @@ -0,0 +1,213 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
    + * 企业付款请求对象.
    + * Created by Binary Wang on 2016/10/02.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class EntPayRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 8647710192770447579L; + + /** + *
    +   * 字段名:公众账号appid.
    +   * 变量名:mch_appid
    +   * 是否必填:是
    +   * 示例值:wx8888888888888888
    +   * 类型:String
    +   * 描述:微信分配的公众账号ID(企业号corpid即为此appId)
    +   * 
    + */ + @XStreamAlias("mch_appid") + private String mchAppid; + + /** + *
    +   * 字段名:商户号.
    +   * 变量名:mchid
    +   * 是否必填:是
    +   * 示例值:1900000109
    +   * 类型:String(32)
    +   * 描述:微信支付分配的商户号
    +   * 
    + */ + @XStreamAlias("mchid") + private String mchId; + + /** + *
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:13467007045764
    +   * 类型:String(32)
    +   * 描述:微信支付分配的终端设备号
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:商户订单号.
    +   * 变量名:partner_trade_no
    +   * 是否必填:是
    +   * 示例值:10000098201411111234567890
    +   * 类型:String
    +   * 描述:商户订单号
    +   * 
    + */ + @Required + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + *
    +   * 字段名:需保持唯一性 用户openid.
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 示例值:oxTWIuGaIt6gTKsQRLau2M0yL16E
    +   * 类型:String
    +   * 描述:商户appid下,某用户的openid
    +   * 
    + */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 字段名:校验用户姓名选项.
    +   * 变量名:check_name
    +   * 是否必填:是
    +   * 示例值:OPTION_CHECK
    +   * 类型:String
    +   * 描述:NO_CHECK:不校验真实姓名 
    +   * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
    +   * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
    +   * 
    + */ + @Required + @XStreamAlias("check_name") + private String checkName; + + /** + *
    +   * 字段名:收款用户姓名.
    +   * 变量名:re_user_name
    +   * 是否必填:可选
    +   * 示例值:马花花
    +   * 类型:String
    +   * 描述:收款用户真实姓名。
    +   * 如果check_name设置为FORCE_CHECK或OPTION_CHECK,  则必填用户真实姓名
    +   * 
    + */ + @XStreamAlias("re_user_name") + private String reUserName; + + /** + *
    +   * 字段名:金额.
    +   * 变量名:amount
    +   * 是否必填:是
    +   * 示例值:10099
    +   * 类型:int
    +   * 描述:企业付款金额, 单位为分
    +   * 
    + */ + @Required + @XStreamAlias("amount") + private Integer amount; + + /** + *
    +   * 字段名:企业付款描述信息.
    +   * 变量名:desc
    +   * 是否必填:是
    +   * 示例值:理赔
    +   * 类型:String
    +   * 描述:企业付款操作说明信息。必填。
    +   * 
    + */ + @Required + @XStreamAlias("desc") + private String description; + + /** + *
    +   * 字段名:Ip地址.
    +   * 变量名:spbill_create_ip
    +   * 是否必填:是
    +   * 示例值:192.168.0.1
    +   * 类型:String(32)
    +   * 描述:调用接口的机器Ip地址
    +   * 
    + */ + @Required + @XStreamAlias("spbill_create_ip") + private String spbillCreateIp; + + @Override + protected void checkConstraints() { + + } + + @Override + public String getAppid() { + return this.mchAppid; + } + + @Override + public void setAppid(String appid) { + this.mchAppid = appid; + } + + @Override + public String getMchId() { + return this.mchId; + } + + @Override + public void setMchId(String mchId) { + this.mchId = mchId; + } + + @Override + protected String[] getIgnoredParamsForSign() { + return new String[]{"sign_type"}; + } + + @Override + protected void storeMap(Map map) { + map.put("mch_appid", mchAppid); + map.put("mchid", mchId); + map.put("device_info", deviceInfo); + map.put("partner_trade_no", partnerTradeNo); + map.put("openid", openid); + map.put("check_name", checkName); + map.put("re_user_name", reUserName); + map.put("amount", amount.toString()); + map.put("desc", description); + map.put("spbill_create_ip", spbillCreateIp); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayResult.java new file mode 100644 index 0000000000..7c09caea2c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayResult.java @@ -0,0 +1,71 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 企业付款返回结果
    + * Created by Binary Wang on 2016/10/02.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class EntPayResult extends BaseWxPayResult { + private static final long serialVersionUID = 8523569987269603097L; + + /** + * 商户号. + */ + @XStreamAlias("mchid") + private String mchId; + + /** + * 商户appid. + */ + @XStreamAlias("mch_appid") + private String mchAppid; + + /** + * 设备号. + */ + @XStreamAlias("device_info") + private String deviceInfo; + + //############以下字段在return_code 和result_code都为SUCCESS的时候有返回############## + /** + * 商户订单号. + */ + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + * 微信订单号. + */ + @XStreamAlias("payment_no") + private String paymentNo; + + /** + * 微信支付成功时间. + */ + @XStreamAlias("payment_time") + private String paymentTime; + + @Override + protected void loadXml(Document d) { + mchId = readXmlString(d, "mchid"); + mchAppid = readXmlString(d, "mch_appid"); + deviceInfo = readXmlString(d, "device_info"); + partnerTradeNo = readXmlString(d, "partner_trade_no"); + paymentNo = readXmlString(d, "payment_no"); + paymentTime = readXmlString(d, "payment_time"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/GetPublicKeyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/GetPublicKeyResult.java new file mode 100644 index 0000000000..7a1f518406 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/GetPublicKeyResult.java @@ -0,0 +1,38 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.w3c.dom.Document; + +/** + *
    + *  企业付款获取RSA加密公钥接口返回结果类
    + *  Created by BinaryWang on 2017/12/20.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@XStreamAlias("xml") +public class GetPublicKeyResult extends BaseWxPayResult { + /** + * 商户号. + */ + @XStreamAlias("mch_id") + private String mchId; + + /** + * 密钥 + */ + @XStreamAlias("pub_key") + private String pubKey; + + @Override + protected void loadXml(Document d) { + mchId = readXmlString(d, "mch_id"); + pubKey = readXmlString(d, "pub_key"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/media/ImageUploadResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/media/ImageUploadResult.java new file mode 100644 index 0000000000..8b7f53a2bf --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/media/ImageUploadResult.java @@ -0,0 +1,28 @@ +package com.github.binarywang.wxpay.bean.media; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * 媒体文件上传返回结果对象 + * @author zhouyongshen + */ +@NoArgsConstructor +@Data +public class ImageUploadResult { + + public static ImageUploadResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, ImageUploadResult.class); + } + /** + * 媒体文件标识 Id + * + * 微信返回的媒体文件标识Id。 + * 示例值:6uqyGjGrCf2GtyXP8bxrbuH9-aAoTjH-rKeSl3Lf4_So6kdkQu4w8BYVP3bzLtvR38lxt4PjtCDXsQpzqge_hQEovHzOhsLleGFQVRF-U_0 + * + */ + @SerializedName("media_id") + private String mediaId; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java new file mode 100644 index 0000000000..3b1cfe7a84 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponse.java @@ -0,0 +1,90 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import com.thoughtworks.xstream.annotations.XStreamOmitField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; + +/** + * 微信支付订单和退款的异步通知共用的响应类. + * + * @author someone + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayNotifyResponse { + @XStreamOmitField + private static final transient String FAIL = "FAIL"; + @XStreamOmitField + private static final transient String SUCCESS = "SUCCESS"; + + @XStreamAlias("return_code") + @XStreamConverter(value = XStreamCDataConverter.class) + private String returnCode; + @XStreamConverter(value = XStreamCDataConverter.class) + @XStreamAlias("return_msg") + private String returnMsg; + + /** + * Fail string. + * + * @param msg the msg + * @return the string + */ + public static String fail(String msg) { + WxPayNotifyResponse response = new WxPayNotifyResponse(FAIL, msg); + XStream xstream = XStreamInitializer.getInstance(); + xstream.autodetectAnnotations(true); + return xstream.toXML(response).replace("\n", "").replace(" ", ""); + } + + /** + * Success string. + * + * @param msg the msg + * @return the string + */ + public static String success(String msg) { + WxPayNotifyResponse response = new WxPayNotifyResponse(SUCCESS, msg); + XStream xstream = XStreamInitializer.getInstance(); + xstream.autodetectAnnotations(true); + return xstream.toXML(response).replace("\n", "").replace(" ", ""); + } + + /** + * Fail string. + * + * @param msg the msg + * @return the string + */ + public static String failResp(String msg) { + return generateXml(FAIL, msg); + } + + /** + * Success string. + * + * @param msg the msg + * @return the string + */ + public static String successResp(String msg) { + return generateXml(SUCCESS, msg); + } + + + /** + * 使用格式化字符串生成xml字符串 + */ + private static String generateXml(String code, String msg) { + return String.format("", code, msg); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java new file mode 100644 index 0000000000..c8ea11d761 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyCoupon.java @@ -0,0 +1,43 @@ +package com.github.binarywang.wxpay.bean.notify; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + * 支付异步通知代金券详细. + * + * @author aimilin + */ +@Data +@NoArgsConstructor +public class WxPayOrderNotifyCoupon implements Serializable { + private static final long serialVersionUID = -4165343733538156097L; + + private String couponId; + private String couponType; + private Integer couponFee; + + /** + * To map map. + * + * @param index the index + * @return the map + */ + public Map toMap(int index) { + Map map = new HashMap<>(); + map.put("coupon_id_" + index, this.getCouponId()); + map.put("coupon_type_" + index, this.getCouponType()); + map.put("coupon_fee_" + index, this.getCouponFee() + ""); + return map; + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java new file mode 100644 index 0000000000..03e4b359be --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java @@ -0,0 +1,403 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.converter.WxPayOrderNotifyResultConverter; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.util.SignUtils; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.w3c.dom.Document; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 支付结果通知. + * 文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8 + * https://pay.weixin.qq.com/wiki/doc/api/external/native.php?chapter=9_7 + * + * @author aimilin6688 + * @since 2.5.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxPayOrderNotifyResult extends BaseWxPayResult { + private static final long serialVersionUID = 5389718115223345496L; + + /** + *
    +   * 字段名:营销详情.
    +   * 变量名:promotion_detail
    +   * 是否必填:否,单品优惠才有
    +   * 类型:String(6000)
    +   * 示例值:[{"promotion_detail":[{"promotion_id":"109519","name":"单品惠-6","scope":"SINGLE","type":"DISCOUNT","amount":5,"activity_id":"931386","wxpay_contribute":0,"merchant_contribute":0,"other_contribute":5,"goods_detail":[{"goods_id":"a_goods1","goods_remark":"商品备注","quantity":7,"price":1,"discount_amount":4},{"goods_id":"a_goods2","goods_remark":"商品备注","quantity":1,"price":2,"discount_amount":1}]}]}
    +   * 描述:单品优惠专用参数,详见https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_203&index=4
    +   * 
    + */ + @XStreamAlias("promotion_detail") + private String promotionDetail; + + /** + *
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:013467007045764
    +   * 描述:微信支付分配的终端设备号,
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:用户标识.
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:wxd930ea5d5a258f4f
    +   * 描述:用户在商户appid下的唯一标识
    +   * 
    + */ + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 字段名:是否关注公众账号.
    +   * 变量名:is_subscribe
    +   * 是否必填:否
    +   * 类型:String(1)
    +   * 示例值:Y
    +   * 描述:用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    +   * 
    + */ + @XStreamAlias("is_subscribe") + private String isSubscribe; + + /** + *
    +   * 字段名:用户子标识.
    +   * 变量名:sub_openid
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:wxd930ea5d5a258f4f
    +   * 描述:用户在子商户appid下的唯一标识
    +   * 
    + */ + @XStreamAlias("sub_openid") + private String subOpenid; + + /** + *
    +   * 字段名:是否关注子公众账号.
    +   * 变量名:sub_is_subscribe
    +   * 是否必填:否
    +   * 类型:String(1)
    +   * 示例值:Y
    +   * 描述:用户是否关注子公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    +   * 
    + */ + @XStreamAlias("sub_is_subscribe") + private String subIsSubscribe; + + + /** + *
    +   * 字段名:交易类型.
    +   * 变量名:trade_type
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:JSAPI
    +   * JSA描述:PI、NATIVE、APP
    +   * 
    + */ + @XStreamAlias("trade_type") + private String tradeType; + + /** + *
    +   * 字段名:付款银行.
    +   * 变量名:bank_type
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:CMC
    +   * 描述:银行类型,采用字符串类型的银行标识,银行类型见银行列表
    +   * 
    + */ + @XStreamAlias("bank_type") + private String bankType; + + /** + *
    +   * 字段名:订单金额.
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:订单总金额,单位为分
    +   * 
    + */ + @XStreamAlias("total_fee") + private Integer totalFee; + /** + *
    +   * 字段名:应结订单金额.
    +   * 变量名:settlement_total_fee
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +   * 
    + */ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + /** + *
    +   * 字段名:货币种类.
    +   * 变量名:fee_type
    +   * 是否必填:否
    +   * 类型:String(8)
    +   * 示例值:CNY
    +   * 描述:货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + */ + @XStreamAlias("fee_type") + private String feeType; + /** + *
    +   * 字段名:现金支付金额.
    +   * 变量名:cash_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:现金支付金额订单现金支付金额,详见支付金额
    +   * 
    + */ + @XStreamAlias("cash_fee") + private Integer cashFee; + /** + *
    +   * 字段名:现金支付货币类型.
    +   * 变量名:cash_fee_type
    +   * 是否必填:否
    +   * 类型:String(16)
    +   * 示例值:CNY
    +   * 描述:货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + */ + @XStreamAlias("cash_fee_type") + private String cashFeeType; + /** + *
    +   * 字段名:总代金券金额.
    +   * 变量名:coupon_fee
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:10
    +   * 描述:代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
    +   * 
    + */ + @XStreamAlias("coupon_fee") + private Integer couponFee; + + /** + *
    +   * 字段名:代金券使用数量.
    +   * 变量名:coupon_count
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:1
    +   * 描述:代金券使用数量
    +   * 
    + */ + @XStreamAlias("coupon_count") + private Integer couponCount; + + private List couponList; + + /** + *
    +   * 字段名:微信支付订单号.
    +   * 变量名:transaction_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1217752501201407033233368018
    +   * 描述:微信支付订单号
    +   * 
    + */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
    +   * 字段名:商户订单号.
    +   * 变量名:out_trade_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1212321211201407033568112322
    +   * 描述:商户系统的订单号,与请求一致。
    +   * 
    + */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + /** + *
    +   * 字段名:商家数据包.
    +   * 变量名:attach
    +   * 是否必填:否
    +   * 类型:String(128)
    +   * 示例值:123456
    +   * 描述:商家数据包,原样返回
    +   * 
    + */ + @XStreamAlias("attach") + private String attach; + + /** + *
    +   * 字段名:支付完成时间.
    +   * 变量名:time_end
    +   * 是否必填:是
    +   * 类型:String(14)
    +   * 示例值:20141030133525
    +   * 描述:支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    +   * 
    + */ + @XStreamAlias("time_end") + private String timeEnd; + + /** + *
    +   * 字段名:接口版本号.
    +   * 变量名:version
    +   * 类型:String(32)
    +   * 示例值:1.0
    +   * 更多信息,详见文档:https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_101&index=1
    +   * 
    + */ + @XStreamAlias("version") + private String version; + + /** + *
    +   * 字段名:汇率.
    +   * 变量名:rate_value
    +   * 类型:String(16)
    +   * 示例值:650000000
    +   * 标价币种与支付币种的兑换比例乘以10的8次方即为此值,例如美元兑换人民币的比例为6.5,则rate_value=650000000
    +   * 
    + */ + @XStreamAlias("rate_value") + private String rateValue; + + /** + *
    +   * 字段名:签名类型.
    +   * 变量名:sign_type
    +   * 类型:String(32)
    +   * 示例值:HMAC-SHA256
    +   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    +   * 
    + */ + @XStreamAlias("sign_type") + private String signType; + + @Override + public void checkResult(WxPayService wxPayService, String signType, boolean checkSuccess) throws WxPayException { + //防止伪造成功通知 + if (WxPayConstants.ResultCode.SUCCESS.equals(getReturnCode()) && getSign() == null) { + throw new WxPayException("伪造的通知!"); + } + + super.checkResult(wxPayService, signType, checkSuccess); + } + + /** + * From xml wx pay order notify result. + * + * @param xmlString the xml string + * @return the wx pay order notify result + */ + public static WxPayOrderNotifyResult fromXML(String xmlString) { + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(WxPayOrderNotifyResult.class); + xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); + WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString); + result.setXmlString(xmlString); + return result; + } + + @Override + public Map toMap() { + Map resultMap = SignUtils.xmlBean2Map(this); + if (this.getCouponCount() != null && this.getCouponCount() > 0) { + for (int i = 0; i < this.getCouponCount(); i++) { + WxPayOrderNotifyCoupon coupon = couponList.get(i); + resultMap.putAll(coupon.toMap(i)); + } + } + return resultMap; + } + + @Override + protected void loadXml(Document d) { + promotionDetail = readXmlString(d, "promotion_detail"); + deviceInfo = readXmlString(d, "device_info"); + openid = readXmlString(d, "openid"); + isSubscribe = readXmlString(d, "is_subscribe"); + subOpenid = readXmlString(d, "sub_openid"); + subIsSubscribe = readXmlString(d, "sub_is_subscribe"); + tradeType = readXmlString(d, "trade_type"); + bankType = readXmlString(d, "bank_type"); + totalFee = readXmlInteger(d, "total_fee"); + settlementTotalFee = readXmlInteger(d, "settlement_total_fee"); + feeType = readXmlString(d, "fee_type"); + cashFee = readXmlInteger(d, "cash_fee"); + cashFeeType = readXmlString(d, "cash_fee_type"); + couponFee = readXmlInteger(d, "coupon_fee"); + couponCount = readXmlInteger(d, "coupon_count"); + transactionId = readXmlString(d, "transaction_id"); + outTradeNo = readXmlString(d, "out_trade_no"); + attach = readXmlString(d, "attach"); + timeEnd = readXmlString(d, "time_end"); + version = readXmlString(d, "version"); + rateValue = readXmlString(d, "rate_value"); + signType = readXmlString(d, "sign_type"); + + composeCoupons(); + } + + /** + * 通过xml组装couponList属性内容. + */ + protected void composeCoupons() { + if (this.couponCount == null || this.couponCount == 0) { + return; + } + this.couponList = new ArrayList(couponCount); + for (int i = 0; i < this.couponCount; i++) { + WxPayOrderNotifyCoupon coupon = new WxPayOrderNotifyCoupon(); + coupon.setCouponId(this.getXmlValue("xml/coupon_id_" + i)); + coupon.setCouponType(this.getXmlValue("xml/coupon_type_" + i)); + coupon.setCouponFee(this.getXmlValueAsInt("xml/coupon_fee_" + i)); + couponList.add(coupon); + } + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java new file mode 100644 index 0000000000..ae86b8c854 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java @@ -0,0 +1,334 @@ +package com.github.binarywang.wxpay.bean.notify; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.w3c.dom.Document; + +/** + *
    + *  退款结果通知对象.
    + *  Created by BinaryWang on 2017/8/27.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayRefundNotifyResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 4651725860079259186L; + + /** + * 从xml字符串创建bean对象. + * + * @param xmlString xml字符串 + * @param mchKey 商户密钥 + * @return the wx pay refund notify result + * @throws WxPayException the wx pay exception + */ + public static WxPayRefundNotifyResult fromXML(String xmlString, String mchKey) throws WxPayException { + WxPayRefundNotifyResult result = BaseWxPayResult.fromXML(xmlString, WxPayRefundNotifyResult.class); + if (WxPayConstants.ResultCode.FAIL.equals(result.getReturnCode())) { + return result; + } + + String reqInfoString = result.getReqInfoString(); + try { + final String keyMd5String = DigestUtils.md5Hex(mchKey).toLowerCase(); + SecretKeySpec key = new SecretKeySpec(keyMd5String.getBytes(StandardCharsets.UTF_8), "AES"); + + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key); + result.setReqInfo(ReqInfo.fromXML(new String(cipher.doFinal(Base64.decodeBase64(reqInfoString)), + StandardCharsets.UTF_8))); + } catch (Exception e) { + throw new WxPayException("解密退款通知加密信息时出错", e); + } + + return result; + } + + /** + *
    +   * 字段名:加密信息.
    +   * 变量名:req_info
    +   * 是否必填:是
    +   * 类型:String(1024)
    +   * 描述:加密信息请用商户证书与商户秘钥进行解密
    +   * 
    + */ + @XStreamAlias("req_info") + private String reqInfoString; + + private ReqInfo reqInfo; + + // 解密后的reqInfo 字符串 + private transient String decryptedReqInfo; + + @Override + protected void loadXml(Document d) { + reqInfoString = readXmlString(d, "req_info"); + } + + /** + * 解密并解析reqInfo + * + * @param mchKey + * @throws WxPayException + */ + public void decryptReqInfo(String mchKey) throws WxPayException { + //如果是失败,直接返回,不用解析 + if (WxPayConstants.ResultCode.FAIL.equals(getReturnCode())) { + return; + } + try { + final String keyMd5String = DigestUtils.md5Hex(mchKey).toLowerCase(); + SecretKeySpec key = new SecretKeySpec(keyMd5String.getBytes(StandardCharsets.UTF_8), "AES"); + + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key); + decryptedReqInfo = new String(cipher.doFinal(Base64.decodeBase64(reqInfoString)), StandardCharsets.UTF_8); + loadReqInfo(decryptedReqInfo); + } catch (Exception e) { + throw new WxPayException("解密退款通知加密信息时出错", e); + } + } + + // 本方法独立出来方便测试 + protected void loadReqInfo(String decryptedReqInfo) { + Document document = openXML(decryptedReqInfo); + reqInfo = new ReqInfo(); + reqInfo.loadXML(document); + } + + /** + * 加密信息字段解密后的内容. + */ + @Data + @NoArgsConstructor + @XStreamAlias("root") + public static class ReqInfo { + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + *
    +     * 字段名:微信订单号.
    +     * 变量名:transaction_id
    +     * 是否必填:是
    +     * 类型:String(32)
    +     * 示例值:1217752501201407033233368018
    +     * 描述:微信订单号
    +     * 
    + */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
    +     * 字段名:商户订单号.
    +     * 变量名:out_trade_no
    +     * 是否必填:是
    +     * 类型:String(32)
    +     * 示例值:1217752501201407033233368018
    +     * 描述:商户系统内部的订单号
    +     * 
    + */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
    +     * 字段名:微信退款单号.
    +     * 变量名:refund_id
    +     * 是否必填:是
    +     * 类型:String(28)
    +     * 示例值:1217752501201407033233368018
    +     * 描述:微信退款单号
    +     * 
    + */ + @XStreamAlias("refund_id") + private String refundId; + + /** + *
    +     * 字段名:商户退款单号.
    +     * 变量名:out_refund_no
    +     * 是否必填:是
    +     * 类型:String(64)
    +     * 示例值:1217752501201407033233368018
    +     * 描述:商户退款单号
    +     * 
    + */ + @XStreamAlias("out_refund_no") + private String outRefundNo; + + /** + *
    +     * 字段名:订单金额.
    +     * 变量名:total_fee
    +     * 是否必填:是
    +     * 类型:Int
    +     * 示例值:100
    +     * 描述:订单总金额,单位为分,只能为整数,详见支付金额
    +     * 
    + */ + @XStreamAlias("total_fee") + private Integer totalFee; + + /** + *
    +     * 字段名:结订单金额.
    +     * 变量名:settlement_total_fee
    +     * 是否必填:否
    +     * 类型:Int
    +     * 示例值:100
    +     * 描述:当该订单有使用非充值券时,返回此字段。应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +     * 
    + */ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + + /** + *
    +     * 字段名:申请退款金额.
    +     * 变量名:refund_fee
    +     * 是否必填:是
    +     * 类型:Int
    +     * 示例值:100
    +     * 描述:退款总金额,单位为分
    +     * 
    + */ + @XStreamAlias("refund_fee") + private Integer refundFee; + + /** + *
    +     * 字段名:退款金额.
    +     * 变量名:settlement_refund_fee
    +     * 是否必填:是
    +     * 类型:Int
    +     * 示例值:100
    +     * 描述:退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
    +     * 
    + */ + @XStreamAlias("settlement_refund_fee") + private Integer settlementRefundFee; + + /** + *
    +     * 字段名:退款状态.
    +     * 变量名:refund_status
    +     * 是否必填:是
    +     * 类型:String(16)
    +     * 示例值:SUCCESS
    +     * 描述:SUCCESS-退款成功,CHANGE-退款异常,REFUNDCLOSE—退款关闭
    +     * 
    + */ + @XStreamAlias("refund_status") + private String refundStatus; + + /** + *
    +     * 字段名:退款成功时间.
    +     * 变量名:success_time
    +     * 是否必填:否
    +     * 类型: String(20)
    +     * 示例值:2017-12-15 09:46:01
    +     * 资金退款至用户帐号的时间,格式2017-12-15 09:46:01
    +     * 
    + */ + @XStreamAlias("success_time") + private String successTime; + + /** + *
    +     * 字段名:退款入账账户.
    +     * 变量名:refund_recv_accout
    +     * 是否必填:是
    +     * 类型:String(64)
    +     * 示例值:招商银行信用卡0403
    +     * 描述:取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱 ,3)退还商户: 商户基本账户,商户结算银行账户,4)退回支付用户零钱通: 支付用户零钱通
    +     * 
    + */ + @XStreamAlias("refund_recv_accout") + private String refundRecvAccout; + + /** + *
    +     * 字段名:退款资金来源.
    +     * 变量名:refund_account
    +     * 是否必填:是
    +     * 类型:String(30)
    +     * 示例值:REFUND_SOURCE_RECHARGE_FUNDS
    +     * 描述:REFUND_SOURCE_RECHARGE_FUNDS 可用余额退款/基本账户,REFUND_SOURCE_UNSETTLED_FUNDS 未结算资金退款
    +     * 
    + */ + @XStreamAlias("refund_account") + private String refundAccount; + + /** + *
    +     * 字段名:退款发起来源.
    +     * 变量名:refund_request_source
    +     * 是否必填:是
    +     * 类型:String(30)
    +     * 示例值:API
    +     * 描述:API接口,VENDOR_PLATFORM商户平台
    +     * 
    + */ + @XStreamAlias("refund_request_source") + private String refundRequestSource; + + /** + * 从xml字符串构造ReqInfo对象. + * + * @param xmlString xml字符串 + * @return ReqInfo对象 + */ + public static ReqInfo fromXML(String xmlString) { + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(ReqInfo.class); + return (ReqInfo) xstream.fromXML(xmlString); + } + + public void loadXML(Document d) { + transactionId = readXmlString(d, "transaction_id"); + outTradeNo = readXmlString(d, "out_trade_no"); + refundId = readXmlString(d, "refund_id"); + outRefundNo = readXmlString(d, "out_refund_no"); + totalFee = readXmlInteger(d, "total_fee"); + settlementTotalFee = readXmlInteger(d, "settlement_total_fee"); + refundFee = readXmlInteger(d, "refund_fee"); + settlementRefundFee = readXmlInteger(d, "settlement_refund_fee"); + refundStatus = readXmlString(d, "refund_status"); + successTime = readXmlString(d, "success_time"); + refundRecvAccout = readXmlString(d, "refund_recv_accout"); + refundAccount = readXmlString(d, "refund_account"); + refundRequestSource = readXmlString(d, "refund_request_source"); + } + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResult.java new file mode 100644 index 0000000000..f2081f9a2b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResult.java @@ -0,0 +1,56 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 扫码支付通知回调类.
    + * 具体定义,请查看文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxScanPayNotifyResult extends BaseWxPayResult { + private static final long serialVersionUID = 3381324564266118986L; + + /** + * 用户标识. + */ + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 是否关注公众账号.
    +   * 仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注
    +   * 
    + */ + @XStreamAlias("is_subscribe") + private String isSubscribe; + + /** + *
    +   * 商品ID.
    +   * 商户定义的商品id 或者订单号
    +   * 
    + */ + @XStreamAlias("product_id") + private String productId; + + @Override + protected void loadXml(Document d) { + openid = readXmlString(d, "openid"); + isSubscribe = readXmlString(d, "is_subscribe"); + productId = readXmlString(d, "product_id"); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java new file mode 100644 index 0000000000..17037b303a --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java @@ -0,0 +1,36 @@ +package com.github.binarywang.wxpay.bean.order; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * APP支付调用统一下单接口后的组装所需参数的实现类
    + * 参考 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12
    + * Created by Binary Wang on 2017-9-1.
    + * 
    + * + * @author Binary Wang + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxPayAppOrderResult implements Serializable { + private static final long serialVersionUID = 5408678833978707228L; + + private String sign; + private String prepayId; + private String partnerId; + private String appId; + /** + * 由于package为java保留关键字,因此改为packageValue. 前端使用时记得要更改为package + */ + private String packageValue; + private String timeStamp; + private String nonceStr; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java new file mode 100644 index 0000000000..3ec6a7e09b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java @@ -0,0 +1,37 @@ +package com.github.binarywang.wxpay.bean.order; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 微信公众号支付进行统一下单后组装所需参数的类
    + * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
    + * Created by Binary Wang on 2017-9-1.
    + * 
    + * + * @author Binary Wang + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxPayMpOrderResult implements Serializable { + private static final long serialVersionUID = -7966682379048446567L; + + private String appId; + private String timeStamp; + private String nonceStr; + /** + * 由于package为java保留关键字,因此改为packageValue. 前端使用时记得要更改为package + */ + @XStreamAlias("package") + private String packageValue; + private String signType; + private String paySign; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMwebOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMwebOrderResult.java new file mode 100644 index 0000000000..046885e624 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMwebOrderResult.java @@ -0,0 +1,26 @@ +package com.github.binarywang.wxpay.bean.order; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 微信H5支付统一下单后发起支付拼接所需参数实现类.
    + * Created by Binary Wang on 2018-4-21.
    + * 
    + * + * @author Binary Wang + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WxPayMwebOrderResult implements Serializable { + private static final long serialVersionUID = 8866329695767762066L; + + @XStreamAlias("mwebUrl") + private String mwebUrl; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java new file mode 100644 index 0000000000..a94615d159 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayNativeOrderResult.java @@ -0,0 +1,24 @@ +package com.github.binarywang.wxpay.bean.order; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + * 微信扫码支付统一下单后发起支付拼接所需参数实现类
    + * Created by Binary Wang on 2017-9-1.
    + * 
    + * + * @author Binary Wang + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WxPayNativeOrderResult implements Serializable { + private static final long serialVersionUID = 887792717425241444L; + + private String codeUrl; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/Detail.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/Detail.java new file mode 100644 index 0000000000..b52c2abc1b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/Detail.java @@ -0,0 +1,36 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 明细. + * + * @author doger.wang + * @date 2020-05-19 + */ +@Data +@NoArgsConstructor +public class Detail implements Serializable { + private static final long serialVersionUID = -3901373259400050385L; + /** + * seq : 1 + * amount : 900 + * paid_type : NEWTON + * paid_time : 20091225091210 + * transaction_id : 15646546545165651651 + */ + @SerializedName("seq") + private int seq; + @SerializedName("amount") + private int amount; + @SerializedName("paid_type") + private String paidType; + @SerializedName("paid_time") + private String paidTime; + @SerializedName("transaction_id") + private String transactionId; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/Location.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/Location.java new file mode 100644 index 0000000000..b3c82f7d96 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/Location.java @@ -0,0 +1,27 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 服务位置信息. + * + * @author doger.wang + * @date 2020-05-19 + */ +@Data +@NoArgsConstructor +public class Location implements Serializable { + private static final long serialVersionUID = -4510224826631515344L; + /** + * start_location : 嗨客时尚主题展餐厅 + * end_location : 嗨客时尚主题展餐厅 + */ + @SerializedName("start_location") + private String startLocation; + @SerializedName("end_location") + private String endLocation; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PayScoreNotifyData.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PayScoreNotifyData.java new file mode 100644 index 0000000000..81d5568bcd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PayScoreNotifyData.java @@ -0,0 +1,56 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信支付分确认订单跟支付回调对象 + * + * @author doger.wang + * @date 2020/5/14 12:18 + */ +@NoArgsConstructor +@Data +public class PayScoreNotifyData implements Serializable { + private static final long serialVersionUID = -8538014389773390989L; + + /** + * id : EV-2018022511223320873 + * create_time : 20180225112233 + * resource_type : encrypt-resource + * event_type : PAYSCORE.USER_CONFIRM + * resource : {"algorithm":"AEAD_AES_256_GCM","ciphertext":"...","nonce":"...","associated_data":""} + */ + @SerializedName("id") + private String id; + @SerializedName("create_time") + private String createTime; + @SerializedName("resource_type") + private String resourceType; + @SerializedName("event_type") + private String eventType; + @SerializedName("resource") + private Resource resource; + + @Data + public static class Resource implements Serializable { + private static final long serialVersionUID = 8530711804335261449L; + /** + * algorithm : AEAD_AES_256_GCM + * ciphertext : ... + * nonce : ... + * associated_data : + */ + @SerializedName("algorithm") + private String algorithm; + @SerializedName("ciphertext") + private String cipherText; + @SerializedName("nonce") + private String nonce; + @SerializedName("associated_data") + private String associatedData; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostDiscount.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostDiscount.java new file mode 100644 index 0000000000..ebd2cf2b39 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostDiscount.java @@ -0,0 +1,31 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 后付费商户优惠. + * + * @author doger.wang + * @date 2020-05-19 + */ +@Data +@NoArgsConstructor +public class PostDiscount implements Serializable { + private static final long serialVersionUID = 2764537888242763379L; + /** + * name : 满20减1元 + * description : 不与其他优惠叠加 + */ + @SerializedName("name") + private String name; + @SerializedName("description") + private String description; + @SerializedName("count") + private int count; + @SerializedName("amount") + private int amount; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostPayment.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostPayment.java new file mode 100644 index 0000000000..fef0b5ab8b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/PostPayment.java @@ -0,0 +1,33 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 后付费项目. + * + * @author doger.wang + * @date 2020-05-19 + */ +@Data +@NoArgsConstructor +public class PostPayment implements Serializable { + private static final long serialVersionUID = 2007722927556382895L; + /** + * name : 就餐费用服务费 + * amount : 4000 + * description : 就餐人均100元服务费:100/小时 + * count : 1 + */ + @SerializedName("name") + private String name; + @SerializedName("amount") + private int amount; + @SerializedName("description") + private String description; + @SerializedName("count") + private int count; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/RiskFund.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/RiskFund.java new file mode 100644 index 0000000000..c6bd840186 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/RiskFund.java @@ -0,0 +1,30 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 订单风险金信息. + * + * @author doger.wang + * @date 2020-05-19 + */ +@Data +@NoArgsConstructor +public class RiskFund implements Serializable { + private static final long serialVersionUID = -3583406084396059152L; + /** + * name : ESTIMATE_ORDER_COST + * amount : 10000 + * description : 就餐的预估费用 + */ + @SerializedName("name") + private String name; + @SerializedName("amount") + private int amount; + @SerializedName("description") + private String description; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/TimeRange.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/TimeRange.java new file mode 100644 index 0000000000..a0a27693d7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/TimeRange.java @@ -0,0 +1,29 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 服务时间范围. + * + * @author doger.wang + * @date 2020-05-19 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TimeRange implements Serializable { + private static final long serialVersionUID = 8169562173656314930L; + /** + * start_time : 20091225091010 + * end_time : 20091225121010 + */ + @SerializedName("start_time") + private String startTime; + @SerializedName("end_time") + private String endTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequest.java new file mode 100644 index 0000000000..c4fd494382 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequest.java @@ -0,0 +1,84 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author doger.wang + * @date 2020/5/12 16:36 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxPayScoreRequest implements Serializable { + private static final long serialVersionUID = 364764508076146082L; + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + + /** + * out_order_no : 1234323JKHDFE1243252 + * appid : wxd678efh567hg6787 + * service_id : 500001 + * service_introduction : 某某酒店 + * post_payments : [{"name":"就餐费用服务费","amount":4000,"description":"就餐人均100元服务费:100/小时","count":1}] + * post_discounts : [{"name":"满20减1元","description":"不与其他优惠叠加"}] + * time_range : {"start_time":"20091225091010","end_time":"20091225121010"} + * location : {"start_location":"嗨客时尚主题展餐厅","end_location":"嗨客时尚主题展餐厅"} + * risk_fund : {"name":"ESTIMATE_ORDER_COST","amount":10000,"description":"就餐的预估费用"} + * attach : Easdfowealsdkjfnlaksjdlfkwqoi&wl3l2sald + * notify_url : https://api.test.com + * openid : oUpF8uMuAJO_M2pxb1Q9zNjWeS6o + * need_user_confirm : true + */ + @SerializedName("out_order_no") + private String outOrderNo; + @SerializedName("appid") + private String appid; + @SerializedName("service_id") + private String serviceId; + @SerializedName("service_introduction") + private String serviceIntroduction; + @SerializedName("time_range") + private TimeRange timeRange; + @SerializedName("location") + private Location location; + @SerializedName("risk_fund") + private RiskFund riskFund; + @SerializedName("attach") + private String attach; + @SerializedName("notify_url") + private String notifyUrl; + @SerializedName("openid") + private String openid; + @SerializedName("need_user_confirm") + private boolean needUserConfirm; + @SerializedName("profit_sharing") + private boolean profitSharing; + @SerializedName("post_payments") + private List postPayments; + @SerializedName("post_discounts") + private List postDiscounts; + @SerializedName("total_amount") + private int totalAmount; + @SerializedName("reason") + private String reason; + @SerializedName("goods_tag") + private String goodsTag; + @SerializedName("type") + private String type; + @SerializedName("detail") + private Detail detail; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreResult.java new file mode 100644 index 0000000000..58665bf55e --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreResult.java @@ -0,0 +1,114 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * @author doger.wang + * @date 2020/5/12 17:05 + */ +@NoArgsConstructor +@Data +public class WxPayScoreResult implements Serializable { + private static final long serialVersionUID = 8809250065540275770L; + + public static WxPayScoreResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxPayScoreResult.class); + } + + /** + * appid : wxd678efh567hg6787 + * mchid : 1230000109 + * out_order_no : 1234323JKHDFE1243252 + * service_id : 500001 + * service_introduction : 某某酒店 + * state : CREATED + * state_description : MCH_COMPLETE + * post_payments : [{"name":"就餐费用服务费","amount":4000,"description":"就餐人均100元服务费:100/小时","count":1}] + * post_discounts : [{"name":"满20减1元","description":"不与其他优惠叠加"}] + * risk_fund : {"name":" ESTIMATE_ORDER_COST","amount":10000,"description":"就餐的预估费用"} + * time_range : {"start_time":"20091225091010","end_time":"20091225121010"} + * location : {"start_location":"嗨客时尚主题展餐厅","end_location":"嗨客时尚主题展餐厅"} + * attach : Easdfowealsdkjfnlaksjdlfkwqoi&wl3l2sald + * notify_url : https://api.test.com + * order_id : 15646546545165651651 + * package : DJIOSQPYWDxsjdldeuwhdodwxasd_dDiodnwjh9we + */ + @SerializedName("appid") + private String appid; + @SerializedName("mchid") + private String mchid; + @SerializedName("out_order_no") + private String outOrderNo; + @SerializedName("service_id") + private String serviceId; + @SerializedName("service_introduction") + private String serviceIntroduction; + @SerializedName("state") + private String state; + @SerializedName("state_description") + private String stateDescription; + @SerializedName("risk_fund") + private RiskFund riskFund; + @SerializedName("time_range") + private TimeRange timeRange; + @SerializedName("location") + private Location location; + @SerializedName("attach") + private String attach; + @SerializedName("notify_url") + private String notifyUrl; + @SerializedName("order_id") + private String orderId; + @SerializedName("package") + private String packageX; + @SerializedName("post_payments") + private List postPayments; + @SerializedName("post_discounts") + private List postDiscounts; + @SerializedName("need_collection") + private boolean needCollection; + /** + * 收款信息 + */ + @SerializedName("collection") + private Collection collection; + /** + * 用于跳转的sign注意区分需确认模式和无需确认模式的数据差别。创单接口会返回,查询请自行组装 + */ + @SerializedName("payScoreSignInfo") + private Map payScoreSignInfo; + + /** + * 收款信息 + */ + @Data + @NoArgsConstructor + public static class Collection implements Serializable { + private static final long serialVersionUID = 2279516555276133086L; + /** + * state : USER_PAID + * total_amount : 3900 + * paying_amount : 3000 + * paid_amount : 900 + * details : [{"seq":1,"amount":900,"paid_type":"NEWTON","paid_time":"20091225091210","transaction_id":"15646546545165651651"}] + */ + @SerializedName("state") + private String state; + @SerializedName("total_amount") + private int totalAmount; + @SerializedName("paying_amount") + private int payingAmount; + @SerializedName("paid_amount") + private int paidAmount; + @SerializedName("details") + private List details; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java new file mode 100644 index 0000000000..3bff328b80 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java @@ -0,0 +1,83 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + * @author Wang GuangXin 2019/10/23 14:02 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingFinishRequest extends BaseWxPayRequest { + + private static final long serialVersionUID = -4265779954583596627L; + + /** + *
    +   * 字段名:微信订单号.
    +   * 变量名:transaction_id
    +   * 是否必填:是
    +   * String(32)
    +   * 示例值:4208450740201411110007820472
    +   * 描述:微信支付订单号
    +   * 
    + */ + @XStreamAlias("transaction_id") + @Required + private String transactionId; + + /** + *
    +   * 字段名:商户分账单号.
    +   * 变量名:out_order_no
    +   * 是否必填:是
    +   * String(64)
    +   * 示例值:P20150806125346
    +   * 描述:商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
    +   * 
    + */ + @XStreamAlias("out_order_no") + @Required + private String outOrderNo; + + /** + *
    +   * 字段名:分账完结描述.
    +   * 变量名:out_order_no
    +   * 是否必填:是
    +   * String(80)
    +   * 示例值:分账已完成
    +   * 描述:分账完结的原因描述
    +   * 
    + */ + @XStreamAlias("description") + @Required + private String description; + + @Override + protected void checkConstraints() { + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } + + @Override + protected boolean ignoreSubAppId() { + return true; + } + + @Override + protected void storeMap(Map map) { + map.put("transaction_id", transactionId); + map.put("out_order_no", outOrderNo); + map.put("description", description); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java new file mode 100644 index 0000000000..d342153a94 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java @@ -0,0 +1,74 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import lombok.experimental.Accessors; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + * @author Wang GuangXin 2019/10/22 15:44 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingQueryRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 3566332883053157102L; + /** + *
    +   * 字段名:微信支付订单号.
    +   * 变量名:transaction_id
    +   * 是否必填:是
    +   * String(32)
    +   * 示例值:4208450740201411110007820472
    +   * 描述:微信支付订单号
    +   * 
    + */ + @XStreamAlias("transaction_id") + @Required + private String transactionId; + + /** + *
    +   * 字段名:商户分账单号.
    +   * 变量名:out_order_no
    +   * 是否必填:是
    +   * String(64)
    +   * 示例值:P20150806125346
    +   * 描述:查询分账结果,输入申请分账时的商户分账单号; 查询分账完结的执行结果,输入发起分账完结时的商户分账单号
    +   * 
    + */ + @XStreamAlias("out_order_no") + @Required + private String outOrderNo; + + @Override + protected void checkConstraints() throws WxPayException { + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } + + @Override + public boolean ignoreAppid() { + return true; + } + + @Override + protected boolean ignoreSubAppId() { + return true; + } + + @Override + protected void storeMap(Map map) { + map.put("transaction_id", transactionId); + map.put("out_order_no", outOrderNo); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java new file mode 100644 index 0000000000..9d0d78e597 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java @@ -0,0 +1,137 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.util.List; + +/** + * @author Wang GuangXin 2019/10/22 15:51 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingQueryResult extends BaseWxPayResult { + private static final long serialVersionUID = 2548673608075775067L; + /** + * 微信订单号 + */ + @XStreamAlias("transaction_id") + private String transactionId; + /** + * 商户分账单号 + */ + @XStreamAlias("out_order_no") + private String outOrderNo; + /** + * 微信分账单号 + */ + @XStreamAlias("order_id") + private String orderId; + /** + * 分账单状态 + */ + @XStreamAlias("status") + private String status; + /** + * 关单原因 + */ + @XStreamAlias("close_reason") + private String closeReason; + /** + * 分账接收方列表 + */ + @XStreamAlias("receivers") + private String receiversJson; + /** + * 分账接收方列表json转换后的对象 + */ + private List receivers; + /** + * 分账金额 + */ + @XStreamAlias("amount") + private Integer amount; + /** + * 分账描述 + */ + @XStreamAlias("description") + private String description; + + public List formatReceivers() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); + Gson gson = gsonBuilder.create(); + final List receivers = gson.fromJson(receiversJson, new TypeToken>() { + }.getType()); + this.receivers = receivers; + return receivers; + } + + @Override + protected void loadXml(Document d) { + transactionId = readXmlString(d, "transaction_id"); + outOrderNo = readXmlString(d, "out_order_no"); + orderId = readXmlString(d, "order_id"); + status = readXmlString(d, "status"); + closeReason = readXmlString(d, "close_reason"); + receiversJson = readXmlString(d, "receivers"); + amount = readXmlInteger(d, "amount"); + description = readXmlString(d, "description"); + } + + @Data + public class Receiver { + /** + * 分账接收方类型 + */ + private String type; + /** + * 分账接收方帐号 + */ + private String account; + /** + * 分账金额 + */ + private Integer amount; + /** + * 分账描述 + */ + private String description; + /** + * 分账结果 + */ + private String result; + /** + * 分账完成时间 + */ + private String finishTime; + /** + * 分账失败原因 + */ + private String failReason; + + @Override + public String toString() { + return "Receivers{" + + "type='" + type + '\'' + + ", account='" + account + '\'' + + ", amount=" + amount + + ", description='" + description + '\'' + + ", result='" + result + '\'' + + ", finishTime='" + finishTime + '\'' + + ", failReason='" + failReason + '\'' + + '}'; + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java new file mode 100644 index 0000000000..db64854395 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + * 添加/删除分账接受方请求对象 + * + * @author Wang GuangXin 2019/10/22 13:41 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReceiverRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 2628263563539120323L; + /** + *
    +   * 字段名:分账接收方.
    +   * 变量名:receiver
    +   * 是否必填:是
    +   * String(2048)
    +   * 示例值:{
    +   *    "type": "MERCHANT_ID",
    +   *    "account": "190001001",
    +   *    "name": "示例商户全称",
    +   *    "relation_type": "STORE_OWNER"
    +   *    }
    +   * 描述:分账接收方对象,json格式
    +   * 
    + */ + @XStreamAlias("receiver") + @Required + private String receiver; + + @Override + protected void checkConstraints() throws WxPayException { + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } + + @Override + protected void storeMap(Map map) { + map.put("receiver", receiver); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java new file mode 100644 index 0000000000..278c2d7d01 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java @@ -0,0 +1,30 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + * @author Wang GuangXin 2019/10/22 14:54 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReceiverResult extends BaseWxPayResult { + private static final long serialVersionUID = 876204163877798066L; + /** + * 分账接收方. + */ + @XStreamAlias("receiver") + private String receiver; + + @Override + protected void loadXml(Document d) { + receiver = readXmlString(d, "receiver"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java new file mode 100644 index 0000000000..e3b1f5690c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java @@ -0,0 +1,93 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + * @author Wang GuangXin 2019/10/21 17:57 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 212049937430575842L; + + /** + *
    +   * 字段名:微信订单号.
    +   * 变量名:transaction_id
    +   * 是否必填:是
    +   * String(32)
    +   * 示例值:4208450740201411110007820472
    +   * 描述:微信支付订单号
    +   * 
    + */ + @XStreamAlias("transaction_id") + @Required + private String transactionId; + + /** + *
    +   * 字段名:商户分账单号.
    +   * 变量名:out_order_no
    +   * 是否必填:是
    +   * String(64)
    +   * 示例值:P20150806125346
    +   * 描述:商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
    +   * 
    + */ + @XStreamAlias("out_order_no") + @Required + private String outOrderNo; + + /** + *
    +   * 字段名:分账接收方列表.
    +   * 变量名:receivers
    +   * 是否必填:是
    +   * String(10240)
    +   * 示例值:[
    +   *     {
    +   *          "type": "MERCHANT_ID",
    +   *          "account":"190001001",
    +   *          "amount":100,
    +   *          "description": "分到商户"
    +   * },
    +   *     {
    +   *          "type": "PERSONAL_WECHATID",
    +   *          "account":"86693952",
    +   *          "amount":888,
    +   *          "description": "分到个人"
    +   * }
    +   * ]
    +   * 描述:分账接收方列表,不超过50个json对象,不能设置分账方作为分账接受方,使用Json格式
    +   * 
    + */ + @XStreamAlias("receivers") + @Required + private String receivers; + + + @Override + protected void checkConstraints() throws WxPayException { + // 目前仅支持HMAC-SHA256. + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } + + @Override + protected void storeMap(Map map) { + map.put("transaction_id", transactionId); + map.put("out_order_no", outOrderNo); + map.put("receivers", receivers); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingResult.java new file mode 100644 index 0000000000..3128450228 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingResult.java @@ -0,0 +1,41 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + * @author Wang GuangXin 2019/10/22 10:06 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingResult extends BaseWxPayResult { + /** + * 微信订单号. + */ + @XStreamAlias("transaction_id") + private String transactionId; + /** + * 商户分账单号. + */ + @XStreamAlias("out_order_no") + private String outOrderNo; + /** + * 微信分账单号. + */ + @XStreamAlias("order_id") + private String orderId; + + @Override + protected void loadXml(Document d) { + transactionId = readXmlString(d, "transaction_id"); + outOrderNo = readXmlString(d, "out_order_no"); + orderId = readXmlString(d, "order_id"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java new file mode 100644 index 0000000000..734c805401 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java @@ -0,0 +1,81 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; + +/** + * @author Wang GuangXin 2019/10/23 15:32 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReturnQueryRequest extends BaseWxPayRequest { + private static final long serialVersionUID = -8838464614726086009L; + /** + *
    +   * 字段名:微信分账单号.
    +   * 变量名:order_id
    +   * 是否必填:二选一
    +   * string(64)
    +   * 示例值:3008450740201411110007820472
    +   * 描述:原发起分账请求时,微信返回的微信分账单号,与商户分账单号一一对应。
    +   * 微信分账单号与商户分账单号二选一填写
    +   * 
    + */ + @XStreamAlias("order_id") + private String orderId; + + /** + *
    +   * 字段名:商户分账单号.
    +   * 变量名:out_order_no
    +   * 是否必填:二选一
    +   * Sstring(64)
    +   * 示例值:P20180806125346
    +   * 描述:原发起分账请求时使用的商户后台系统的分账单号。
    +   * 微信分账单号与商户分账单号二选一填写
    +   * 
    + */ + @XStreamAlias("out_order_no") + private String outOrderNo; + + /** + *
    +   * 字段名:商户回退单号.
    +   * 变量名:out_return_no
    +   * 是否必填:是
    +   * string(64)
    +   * 示例值:R20190516001
    +   * 描述:调用回退接口提供的商户系统内部的回退单号
    +   * 
    + */ + @Required + @XStreamAlias("out_return_no") + private String outReturnNo; + + @Override + protected void checkConstraints() throws WxPayException { + if (StringUtils.isBlank(orderId) && StringUtils.isBlank(outOrderNo)) { + throw new WxPayException("order_id 和 outOrderNo 必须有一个存在"); + } + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } + + @Override + protected void storeMap(Map map) { + map.put("order_id", orderId); + map.put("out_order_no", outOrderNo); + map.put("out_return_no", outReturnNo); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java new file mode 100644 index 0000000000..3e389a467a --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java @@ -0,0 +1,146 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; + +/** + * @author Wang GuangXin 2019/10/23 14:27 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReturnRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 5926280401474809744L; + /** + *
    +   * 字段名:微信分账单号.
    +   * 变量名:order_id
    +   * 是否必填:二选一
    +   * string(64)
    +   * 示例值:3008450740201411110007820472
    +   * 描述:原发起分账请求时,微信返回的微信分账单号,与商户分账单号一一对应。
    +   * 微信分账单号与商户分账单号二选一填写
    +   * 
    + */ + @XStreamAlias("order_id") + private String orderId; + + /** + *
    +   * 字段名:商户分账单号.
    +   * 变量名:out_order_no
    +   * 是否必填:二选一
    +   * Sstring(64)
    +   * 示例值:P20180806125346
    +   * 描述:原发起分账请求时使用的商户后台系统的分账单号。
    +   * 微信分账单号与商户分账单号二选一填写
    +   * 
    + */ + @XStreamAlias("out_order_no") + private String outOrderNo; + + /** + *
    +   * 字段名:商户回退单号.
    +   * 变量名:out_return_no
    +   * 是否必填:是
    +   * string(64)
    +   * 示例值:R20190516001
    +   * 描述:此回退单号是商户在自己后台生成的一个新的回退单号,在商户后台唯一
    +   * 只能是数字、大小写字母_-|*@ ,同一回退单号多次请求等同一次。
    +   * 
    + */ + @Required + @XStreamAlias("out_return_no") + private String outReturnNo; + + /** + *
    +   * 字段名:回退方类型.
    +   * 变量名:return_account_type
    +   * 是否必填:是
    +   * String(32)
    +   * 示例值:MERCHANT_ID
    +   * 描述:枚举值:
    +   * MERCHANT_ID:商户ID
    +   * 暂时只支持从商户接收方回退分账金额
    +   * 
    + */ + @Required + @XStreamAlias("return_account_type") + private String returnAccountType; + + /** + *
    +   * 字段名:回退方账号.
    +   * 变量名:return_account
    +   * 是否必填:是
    +   * String(64)
    +   * 示例值:86693852
    +   * 描述:回退方类型是MERCHANT_ID时,填写商户ID
    +   * 只能对原分账请求中成功分给商户接收方进行回退
    +   * 
    + */ + @Required + @XStreamAlias("return_account") + private String returnAccount; + + /** + *
    +   * 字段名:回退金额.
    +   * 变量名:return_amount
    +   * 是否必填:是
    +   * int
    +   * 示例值:888
    +   * 描述:需要从分账接收方回退的金额,单位为分,只能为整数,不能超过原始分账单分出给该接收方的金额
    +   * 
    + */ + @Required + @XStreamAlias("return_amount") + private Integer returnAmount; + + /** + *
    +   * 字段名:回退描述.
    +   * 变量名:description
    +   * 是否必填:是
    +   * String(80)
    +   * 示例值:用户退款
    +   * 描述:分账回退的原因描述
    +   * 
    + */ + @Required + @XStreamAlias("description") + private String description; + + + @Override + protected void checkConstraints() throws WxPayException { + if (StringUtils.isBlank(orderId) && StringUtils.isBlank(outOrderNo)) { + throw new WxPayException("order_id 和 outOrderNo 必须有一个存在"); + } + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } + + @Override + protected void storeMap(Map map) { + map.put("order_id", orderId); + map.put("out_order_no", outOrderNo); + map.put("out_return_no", outReturnNo); + map.put("return_account_type", returnAccountType); + map.put("return_account", returnAccount); + map.put("return_amount", returnAmount.toString()); + map.put("description", description); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java new file mode 100644 index 0000000000..bfa7353296 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java @@ -0,0 +1,90 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + * @author Wang GuangXin 2019/10/23 14:41 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReturnResult extends BaseWxPayResult { + private static final long serialVersionUID = 718554909816994568L; + /** + * 微信分账单号 + */ + @XStreamAlias("order_id") + private String orderId; + /** + * 商户分账单号 + */ + @XStreamAlias("out_order_no") + private String outOrderNo; + /** + * 商户回退单号 + */ + @XStreamAlias("out_return_no") + private String outReturnNo; + /** + * 微信回退单号 + */ + @XStreamAlias("return_no") + private String returnNo; + /** + * 回退方类型 + */ + @XStreamAlias("return_account_type") + private String returnAccountType; + /** + * 回退方账号 + */ + @XStreamAlias("return_account") + private String returnAccount; + /** + * 回退金额 + */ + @XStreamAlias("return_amount") + private Integer returnAmount; + /** + * 回退描述 + */ + @XStreamAlias("description") + private String description; + /** + * 回退结果 + */ + @XStreamAlias("result") + private String result; + /** + * 失败原因 + */ + @XStreamAlias("fail_reason") + private String failReason; + /** + * 完成时间 + */ + @XStreamAlias("finish_time") + private String finishTime; + + @Override + protected void loadXml(Document d) { + orderId = readXmlString(d, "order_id"); + outOrderNo = readXmlString(d, "out_order_no"); + outReturnNo = readXmlString(d, "out_return_no"); + returnNo = readXmlString(d, "return_no"); + returnAccountType = readXmlString(d, "return_account_type"); + returnAccount = readXmlString(d, "return_account"); + returnAmount = readXmlInteger(d, "return_amount"); + description = readXmlString(d, "description"); + result = readXmlString(d, "result"); + failReason = readXmlString(d, "fail_reason"); + finishTime = readXmlString(d, "finish_time"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/Receiver.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/Receiver.java new file mode 100644 index 0000000000..671e2951d4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/Receiver.java @@ -0,0 +1,128 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.io.Serializable; + +/** + * @author Wang GuangXin 2019/10/22 11:07 + * @version 1.0 + */ +public class Receiver implements Serializable { + private String type; + private String account; + private Integer amount; + private String description; + private String name; + private String relationType; + private String customRelation; + + /** + * 此构造函数用于单次分账 + * + * @param type MERCHANT_ID:商户ID + * PERSONAL_WECHATID:个人微信号PERSONAL_OPENID:个人openid(由父商户APPID转换得到)PERSONAL_SUB_OPENID: 个人sub_openid(由子商户APPID转换得到) + * @param account 类型是MERCHANT_ID时,是商户ID + * 类型是PERSONAL_WECHATID时,是个人微信号 + * 类型是PERSONAL_OPENID时,是个人openid + * 类型是PERSONAL_SUB_OPENID时,是个人sub_openid + * @param amount 分账金额,单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额 + * @param description 分账的原因描述,分账账单中需要体现 + */ + public Receiver(String type, String account, Integer amount, String description) { + this.type = type; + this.account = account; + this.amount = amount; + this.description = description; + } + + /** + * 此构造用于添加分账方 + * + * @param type MERCHANT_ID:商户ID + * PERSONAL_WECHATID:个人微信号PERSONAL_OPENID:个人openid(由父商户APPID转换得到)PERSONAL_SUB_OPENID: 个人sub_openid(由子商户APPID转换得到) + * @param account 类型是MERCHANT_ID时,是商户ID + * 类型是PERSONAL_WECHATID时,是个人微信号 + * 类型是PERSONAL_OPENID时,是个人openid + * 类型是PERSONAL_SUB_OPENID时,是个人sub_openid + * @param name 分账接收方类型是MERCHANT_ID时,是商户全称(必传) + * 分账接收方类型是PERSONAL_NAME 时,是个人姓名(必传) + * 分账接收方类型是PERSONAL_OPENID时,是个人姓名(选传,传则校验) + * 分账接收方类型是PERSONAL_SUB_OPENID时,是个人姓名(选传,传则校验) + * @param relationType 子商户与接收方的关系。 + * 本字段值为枚举: + * SERVICE_PROVIDER:服务商 + * STORE:门店 + * STAFF:员工 + * STORE_OWNER:店主 + * PARTNER:合作伙伴 + * HEADQUARTER:总部 + * BRAND:品牌方 + * DISTRIBUTOR:分销商 + * USER:用户 + * SUPPLIER:供应商 + * CUSTOM:自定义 + * @param customRelation 子商户与接收方具体的关系,本字段最多10个字。 + * 当字段relation_type的值为CUSTOM时,本字段必填 + * 当字段relation_type的值不为CUSTOM时,本字段无需填写 + */ + public Receiver(String type, String account, String name, String relationType, String customRelation) { + this.type = type; + this.account = account; + this.name = name; + this.relationType = relationType; + this.customRelation = customRelation; + } + + /** + * 用于删除分账接受方 + * + * @param type MERCHANT_ID:商户ID + * PERSONAL_WECHATID:个人微信号PERSONAL_OPENID:个人openid(由父商户APPID转换得到)PERSONAL_SUB_OPENID: 个人sub_openid(由子商户APPID转换得到) + * @param account 类型是MERCHANT_ID时,是商户ID + * 类型是PERSONAL_WECHATID时,是个人微信号 + * 类型是PERSONAL_OPENID时,是个人openid + * 类型是PERSONAL_SUB_OPENID时,是个人sub_openid + */ + public Receiver(String type, String account) { + this.type = type; + this.account = account; + } + + public String toJSONString() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); + Gson gson = gsonBuilder.create(); + return gson.toJson(this); + } + + public String getType() { + return type; + } + + public String getAccount() { + return account; + } + + public Integer getAmount() { + return amount; + } + + public String getDescription() { + return description; + } + + public String getName() { + return name; + } + + public String getRelationType() { + return relationType; + } + + public String getCustomRelation() { + return customRelation; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ReceiverList.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ReceiverList.java new file mode 100644 index 0000000000..d3d8c07d37 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ReceiverList.java @@ -0,0 +1,51 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.google.gson.Gson; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * @author Wang GuangXin 2019/10/22 11:01 + * @version 1.0 + */ + +public class ReceiverList implements Serializable { + private static final long serialVersionUID = -1316860887694489921L; + ArrayList list; + + private ReceiverList() { + } + + /** + * 获取一个实例 + * @return + */ + public static ReceiverList getInstance() { + ReceiverList receiverList = new ReceiverList(); + receiverList.list = new ArrayList(); + return receiverList; + } + + /** + * 添加一个分账条目 + * 注意微信上限为50个 + * @param receiver + * @return + */ + public ReceiverList add(Receiver receiver) { + this.list.add(receiver); + return this; + } + + + /** + * 转为JSON格式 + * @return + */ + public String toJSONString() { + Gson gson = new Gson(); + return gson.toJson(this.list); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/BaseWxPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/BaseWxPayRequest.java new file mode 100644 index 0000000000..f19935e7e1 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/BaseWxPayRequest.java @@ -0,0 +1,380 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.util.SignUtils; +import com.github.binarywang.wxpay.util.XmlConfig; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.experimental.Accessors; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.BeanUtils; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.apache.commons.lang3.StringUtils; +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import static com.github.binarywang.wxpay.constant.WxPayConstants.SignType.ALL_SIGN_TYPES; + +/** + *
    + *  微信支付请求对象共用的参数存放类
    + * Created by Binary Wang on 2016-10-24.
    + * 
    + * + * @author Binary Wang + */ +@Data +@Accessors(chain = true) +public abstract class BaseWxPayRequest implements Serializable { + private static final long serialVersionUID = -4766915659779847060L; + + /** + *
    +   * 字段名:公众账号ID.
    +   * 变量名:appid
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:wxd678efh567hg6787
    +   * 描述:微信分配的公众账号ID(企业号corpid即为此appId)
    +   * 
    + */ + @XStreamAlias("appid") + protected String appid; + /** + *
    +   * 字段名:商户号.
    +   * 变量名:mch_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1230000109
    +   * 描述:微信支付分配的商户号
    +   * 
    + */ + @XStreamAlias("mch_id") + protected String mchId; + /** + *
    +   * 字段名:服务商模式下的子商户公众账号ID.
    +   * 变量名:sub_appid
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:wxd678efh567hg6787
    +   * 描述:微信分配的子商户公众账号ID
    +   * 
    + */ + @XStreamAlias("sub_appid") + protected String subAppId; + /** + *
    +   * 字段名:服务商模式下的子商户号.
    +   * 变量名:sub_mch_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1230000109
    +   * 描述:微信支付分配的子商户号,开发者模式下必填
    +   * 
    + */ + @XStreamAlias("sub_mch_id") + protected String subMchId; + /** + *
    +   * 字段名:随机字符串.
    +   * 变量名:nonce_str
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:5K8264ILTKCH16CQ2502SI8ZNMTM67VS
    +   * 描述:随机字符串,不长于32位。推荐随机数生成算法
    +   * 
    + */ + @XStreamAlias("nonce_str") + protected String nonceStr; + /** + *
    +   * 字段名:签名.
    +   * 变量名:sign
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:C380BEC2BFD727A4B6845133519F3AD6
    +   * 描述:签名,详见签名生成算法
    +   * 
    + */ + @XStreamAlias("sign") + protected String sign; + + /** + *
    +   * 签名类型.
    +   * sign_type
    +   * 否
    +   * String(32)
    +   * HMAC-SHA256
    +   * 签名类型,目前支持HMAC-SHA256和MD5
    +   * 
    + */ + @XStreamAlias("sign_type") + private String signType; + + + /** + * 企业微信签名 + */ + @XStreamAlias("workwx_sign") + private String workWxSign; + + public String getWorkWxSign() { + return workWxSign; + } + + public void setWorkWxSign(String workWxSign) { + this.workWxSign = workWxSign; + } + + /** + * 将单位为元转换为单位为分. + * + * @param yuan 将要转换的元的数值字符串 + * @return the integer + */ + public static Integer yuanToFen(String yuan) { + return new BigDecimal(yuan).setScale(2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).intValue(); + } + + /** + * 检查请求参数内容,包括必填参数以及特殊约束. + */ + private void checkFields() throws WxPayException { + //check required fields + try { + BeanUtils.checkRequiredFields(this); + } catch (WxErrorException e) { + throw new WxPayException(e.getError().getErrorMsg(), e); + } + + //check other parameters + this.checkConstraints(); + } + + /** + * 检查约束情况. + * + * @throws WxPayException the wx pay exception + */ + protected abstract void checkConstraints() throws WxPayException; + + /** + * 是否需要nonce_str + */ + protected boolean needNonceStr() { + return true; + } + + /** + * 如果配置中已经设置,可以不设置值. + * + * @param appid 微信公众号appid + */ + public void setAppid(String appid) { + this.appid = appid; + } + + /** + * 如果配置中已经设置,可以不设置值. + * + * @param mchId 微信商户号 + */ + public void setMchId(String mchId) { + this.mchId = mchId; + } + + /** + * 默认采用时间戳为随机字符串,可以不设置. + * + * @param nonceStr 随机字符串 + */ + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + * To xml string. + * + * @return the string + */ + public String toXML() { + //涉及到服务商模式的两个参数,在为空值时置为null,以免在请求时将空值传给微信服务器 + this.setSubAppId(StringUtils.trimToNull(this.getSubAppId())); + this.setSubMchId(StringUtils.trimToNull(this.getSubMchId())); + if (XmlConfig.fastMode) { + return toFastXml(); + } + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(this.getClass()); + return xstream.toXML(this); + } + + /** + * 使用快速算法组装xml + */ + private String toFastXml() { + try { + Document document = DocumentHelper.createDocument(); + Element root = document.addElement(xmlRootTagName()); + + Map signParams = getSignParams(); + signParams.put("sign", sign); + for (Map.Entry entry : signParams.entrySet()) { + if (entry.getValue() == null) { + continue; + } + Element elm = root.addElement(entry.getKey()); + elm.addText(entry.getValue()); + } + + return document.asXML(); + } catch (Exception e) { + throw new RuntimeException("generate xml error", e); + } + } + + /** + * 返回xml结构的根节点名称 + * + * @return 默认返回"xml", 特殊情况可以在子类中覆盖 + */ + protected String xmlRootTagName() { + return "xml"; + } + + /** + * 签名时,是否忽略appid. + * + * @return the boolean + */ + protected boolean ignoreAppid() { + return false; + } + + /** + * 签名时,是否忽略sub_appid. + * + * @return the boolean + */ + protected boolean ignoreSubAppId() { + return false; + } + + protected boolean ignoreSubMchId() { + return false; + } + + /** + * 是否是企业微信字段 + */ + protected boolean isWxWorkSign() { + return false; + } + + /** + * 签名时,忽略的参数. + * + * @return the string [ ] + */ + protected String[] getIgnoredParamsForSign() { + return new String[0]; + } + + /** + * 获取签名时需要的参数. + * 注意:不含sign属性 + */ + public Map getSignParams() { + Map map = new HashMap<>(8); + map.put("appid", appid); + map.put("mch_id", mchId); + map.put("sub_appid", subAppId); + map.put("sub_mch_id", subMchId); + map.put("nonce_str", nonceStr); + map.put("sign_type", signType); + + storeMap(map); + return map; + } + + /** + * 将属性组装到一个Map中,供签名和最终发送XML时使用. + * 这里需要将所有的属性全部保存进来,签名的时候会自动调用getIgnoredParamsForSign进行忽略, + * 不用担心。否则最终生成的XML会缺失。 + * + * @param map 传入的属性Map + */ + abstract protected void storeMap(Map map); + + /** + *
    +   * 检查参数,并设置签名.
    +   * 1、检查参数(注意:子类实现需要检查参数的而外功能时,请在调用父类的方法前进行相应判断)
    +   * 2、补充系统参数,如果未传入则从配置里读取
    +   * 3、生成签名,并设置进去
    +   * 
    + * + * @param config 支付配置对象,用于读取相应系统配置信息 + * @throws WxPayException the wx pay exception + */ + public void checkAndSign(WxPayConfig config) throws WxPayException { + this.checkFields(); + + if (!ignoreAppid()) { + if (StringUtils.isBlank(getAppid())) { + this.setAppid(config.getAppId()); + } + } + + if (StringUtils.isBlank(getMchId())) { + this.setMchId(config.getMchId()); + } + + if (!ignoreSubAppId()) { + if (StringUtils.isBlank(getSubAppId())) { + this.setSubAppId(config.getSubAppId()); + } + } + + if (!ignoreSubMchId()) { + if (StringUtils.isBlank(getSubMchId())) { + this.setSubMchId(config.getSubMchId()); + } + } + + if (StringUtils.isBlank(getSignType())) { + if (config.getSignType() != null && !ALL_SIGN_TYPES.contains(config.getSignType())) { + throw new WxPayException("非法的signType配置:" + config.getSignType() + ",请检查配置!"); + } + this.setSignType(StringUtils.trimToNull(config.getSignType())); + } else { + if (!ALL_SIGN_TYPES.contains(this.getSignType())) { + throw new WxPayException("非法的sign_type参数:" + this.getSignType()); + } + } + + if (needNonceStr() && StringUtils.isBlank(getNonceStr())) { + this.setNonceStr(String.valueOf(System.currentTimeMillis())); + } + + //设置签名字段的值 + this.setSign(SignUtils.createSign(this, this.getSignType(), config.getMchKey(), this.getIgnoredParamsForSign())); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java deleted file mode 100644 index f9135c34df..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.binarywang.wxpay.bean.request; - -import com.thoughtworks.xstream.annotations.XStreamAlias; -import me.chanjar.weixin.common.annotation.Required; -import me.chanjar.weixin.common.util.ToStringUtils; - -/** - *
    - * 企业付款请求对象
    - * 注释中各行每个字段描述对应如下:
    - * 
  9. 字段名 - *
  10. 变量名 - *
  11. 是否必填 - *
  12. 类型 - *
  13. 示例值 - *
  14. 描述 - *
  15. - * Created by Binary Wang on 2016/10/19. - * - * @author binarywang (https://github.com/binarywang) - */ -@XStreamAlias("xml") -public class WxEntPayQueryRequest extends WxPayBaseRequest { - /** - *
    -   * 商户订单号
    -   * partner_trade_no
    -   * 是
    -   * 10000098201411111234567890
    -   * String
    -   * 商户订单号
    -   * 
    - */ - @Required - @XStreamAlias("partner_trade_no") - private String partnerTradeNo; - - public String getPartnerTradeNo() { - return this.partnerTradeNo; - } - - public void setPartnerTradeNo(String partnerTradeNo) { - this.partnerTradeNo = partnerTradeNo; - } - - @Override - protected void checkConstraints() { - //do nothing - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java deleted file mode 100644 index fd43fb1aaa..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java +++ /dev/null @@ -1,251 +0,0 @@ -package com.github.binarywang.wxpay.bean.request; - -import com.thoughtworks.xstream.annotations.XStreamAlias; -import me.chanjar.weixin.common.util.ToStringUtils; - -/** - *
    - * 企业付款请求对象
    - * 注释中各行每个字段描述对应如下:
    - * 
  16. 字段名 - *
  17. 变量名 - *
  18. 是否必填 - *
  19. 类型 - *
  20. 示例值 - *
  21. 描述 - *
  22. - * Created by Binary Wang on 2016/10/02. - * - * @author binarywang (https://github.com/binarywang) - */ -@XStreamAlias("xml") -public class WxEntPayRequest extends WxPayBaseRequest { - /** - *
    -   * 公众账号appid
    -   * mch_appid
    -   * 是
    -   * wx8888888888888888
    -   * String
    -   * 微信分配的公众账号ID(企业号corpid即为此appId)
    -   * 
    - */ - @XStreamAlias("mch_appid") - private String mchAppid; - - /** - *
    -   *  商户号
    -   *  mchid
    -   *  是
    -   *  1900000109
    -   *  String(32)
    -   *  微信支付分配的商户号
    -   * 
    - */ - @XStreamAlias("mchid") - private String mchId; - - /** - *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * 13467007045764
    -   * String(32)
    -   * 微信支付分配的终端设备号
    -   * 
    - */ - @XStreamAlias("device_info") - private String deviceInfo; - - /** - *
    -   * 商户订单号
    -   * partner_trade_no
    -   * 是
    -   * 10000098201411111234567890
    -   * String
    -   * 商户订单号
    -   * 
    - */ - @XStreamAlias("partner_trade_no") - private String partnerTradeNo; - - /** - *
    -   * 需保持唯一性 用户openid
    -   * openid
    -   * 是
    -   * oxTWIuGaIt6gTKsQRLau2M0yL16E
    -   * String
    -   * 商户appid下,某用户的openid
    -   * 
    - */ - @XStreamAlias("openid") - private String openid; - - /** - *
    -   * 校验用户姓名选项
    -   * check_name
    -   * 是
    -   * OPTION_CHECK
    -   * String
    -   * NO_CHECK:不校验真实姓名 
    -   * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
    -   * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
    -   * 
    - */ - @XStreamAlias("check_name") - private String checkName; - - /** - *
    -   * 收款用户姓名
    -   * re_user_name
    -   * 可选
    -   * 马花花
    -   * String
    -   * 收款用户真实姓名。
    -   * 如果check_name设置为FORCE_CHECK或OPTION_CHECK,  则必填用户真实姓名
    -   * 
    - */ - @XStreamAlias("re_user_name") - private String reUserName; - - /** - *
    -   * 金额
    -   * amount
    -   * 是
    -   * 10099
    -   * int
    -   * 企业付款金额, 单位为分
    -   * 
    - */ - @XStreamAlias("amount") - private Integer amount; - - /** - *
    -   * 企业付款描述信息
    -   * desc
    -   * 是
    -   * 理赔
    -   * String
    -   * 企业付款操作说明信息。必填。
    -   * 
    - */ - @XStreamAlias("desc") - private String description; - - /** - *
    -   * Ip地址
    -   * spbill_create_ip
    -   * 是
    -   * 192.168.0.1
    -   * String(32)
    -   * 调用接口的机器Ip地址
    -   * 
    - */ - @XStreamAlias("spbill_create_ip") - private String spbillCreateIp; - - - @Override - protected void checkConstraints() { - - } - - @Override - public String getAppid() { - return this.mchAppid; - } - - @Override - public void setAppid(String appid) { - this.mchAppid = appid; - } - - @Override - public String getMchId() { - return this.mchId; - } - - @Override - public void setMchId(String mchId) { - this.mchId = mchId; - } - - public String getDeviceInfo() { - return this.deviceInfo; - } - - public void setDeviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - } - - public String getPartnerTradeNo() { - return this.partnerTradeNo; - } - - public void setPartnerTradeNo(String partnerTradeNo) { - this.partnerTradeNo = partnerTradeNo; - } - - public String getOpenid() { - return this.openid; - } - - public void setOpenid(String openid) { - this.openid = openid; - } - - public String getCheckName() { - return this.checkName; - } - - public void setCheckName(String checkName) { - this.checkName = checkName; - } - - public String getReUserName() { - return this.reUserName; - } - - public void setReUserName(String reUserName) { - this.reUserName = reUserName; - } - - public Integer getAmount() { - return this.amount; - } - - public void setAmount(Integer amount) { - this.amount = amount; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getSpbillCreateIp() { - return this.spbillCreateIp; - } - - public void setSpbillCreateIp(String spbillCreateIp) { - this.spbillCreateIp = spbillCreateIp; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java index 594fa4960d..c9d5505b36 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java @@ -1,16 +1,25 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; + +import java.util.Map; /** *
      * 授权码查询openid接口请求对象类
      * Created by Binary Wang on 2017-3-27.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayAuthcode2OpenidRequest extends WxPayBaseRequest { +public class WxPayAuthcode2OpenidRequest extends BaseWxPayRequest { /** *
    @@ -24,23 +33,14 @@ public class WxPayAuthcode2OpenidRequest extends WxPayBaseRequest {
       @XStreamAlias("auth_code")
       private String authCode;
     
    -  public WxPayAuthcode2OpenidRequest() {
    -  }
    -
    -  public WxPayAuthcode2OpenidRequest(String authCode) {
    -    this.authCode = authCode;
    -  }
    -
    -  public String getAuthCode() {
    -    return this.authCode;
    -  }
    -
    -  public void setAuthCode(String authCode) {
    -    this.authCode = authCode;
    -  }
    -
       @Override
       protected void checkConstraints() {
         // nothing to do
       }
    +
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("auth_code", authCode);
    +  }
    +
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java
    deleted file mode 100644
    index 4d5cbbaa31..0000000000
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java
    +++ /dev/null
    @@ -1,241 +0,0 @@
    -package com.github.binarywang.wxpay.bean.request;
    -
    -import com.github.binarywang.wxpay.config.WxPayConfig;
    -import com.github.binarywang.wxpay.util.SignUtils;
    -import com.thoughtworks.xstream.XStream;
    -import com.thoughtworks.xstream.annotations.XStreamAlias;
    -import me.chanjar.weixin.common.exception.WxErrorException;
    -import me.chanjar.weixin.common.util.BeanUtils;
    -import me.chanjar.weixin.common.util.ToStringUtils;
    -import me.chanjar.weixin.common.util.xml.XStreamInitializer;
    -import org.apache.commons.lang3.StringUtils;
    -
    -import java.math.BigDecimal;
    -
    -/**
    - * 
    - * Created by Binary Wang on 2016-10-24.
    - *  微信支付请求对象共用的参数存放类
    - * 注释中各行每个字段描述对应如下:
    - * 
  23. 字段名 - *
  24. 变量名 - *
  25. 是否必填 - *
  26. 类型 - *
  27. 示例值 - *
  28. 描述 - *
  29. - * - * @author binarywang(Binary Wang) - */ -public abstract class WxPayBaseRequest { - /** - * 检查请求参数内容,包括必填参数以及特殊约束 - */ - protected void checkFields() throws WxErrorException { - //check required fields - BeanUtils.checkRequiredFields(this); - - //check other parameters - this.checkConstraints(); - } - - /** - * 检查约束情况 - */ - protected abstract void checkConstraints(); - - /** - *
    -   * 公众账号ID
    -   * appid
    -   * 是
    -   * String(32)
    -   * wxd678efh567hg6787
    -   * 微信分配的公众账号ID(企业号corpid即为此appId)
    -   * 
    - */ - @XStreamAlias("appid") - protected String appid; - /** - *
    -   * 商户号
    -   * mch_id
    -   * 是
    -   * String(32)
    -   * 1230000109
    -   * 微信支付分配的商户号
    -   * 
    - */ - @XStreamAlias("mch_id") - protected String mchId; - - /** - *
    -   * 服务商模式下的子商户公众账号ID
    -   * sub_appid
    -   * 是
    -   * String(32)
    -   * wxd678efh567hg6787
    -   * 微信分配的子商户公众账号ID
    -   * 
    - */ - @XStreamAlias("sub_appid") - protected String subAppId; - - /** - *
    -   * 服务商模式下的子商户号
    -   * sub_mch_id
    -   * 是
    -   * String(32)
    -   * 1230000109
    -   * 微信支付分配的子商户号,开发者模式下必填
    -   * 
    - */ - @XStreamAlias("sub_mch_id") - protected String subMchId; - /** - *
    -   * 随机字符串
    -   * nonce_str
    -   * 是
    -   * String(32)
    -   * 5K8264ILTKCH16CQ2502SI8ZNMTM67VS
    -   * 随机字符串,不长于32位。推荐随机数生成算法
    -   * 
    - */ - @XStreamAlias("nonce_str") - protected String nonceStr; - /** - *
    -   * 签名
    -   * sign
    -   * 是
    -   * String(32)
    -   * C380BEC2BFD727A4B6845133519F3AD6
    -   * 签名,详见签名生成算法
    -   * 
    - */ - @XStreamAlias("sign") - protected String sign; - - /** - * 将单位为元转换为单位为分 - * - * @param yuan 将要转换的元的数值字符串 - */ - public static Integer yuanToFee(String yuan) { - return new BigDecimal(yuan).setScale(2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).intValue(); - } - - public String getAppid() { - return this.appid; - } - - /** - * 如果配置中已经设置,可以不设置值 - * - * @param appid 微信公众号appid - */ - public void setAppid(String appid) { - this.appid = appid; - } - - public String getMchId() { - return this.mchId; - } - - /** - * 如果配置中已经设置,可以不设置值 - * - * @param mchId 微信商户号 - */ - public void setMchId(String mchId) { - this.mchId = mchId; - } - - public String getNonceStr() { - return this.nonceStr; - } - - /** - * 默认采用时间戳为随机字符串,可以不设置 - * - * @param nonceStr 随机字符串 - */ - public void setNonceStr(String nonceStr) { - this.nonceStr = nonceStr; - } - - public String getSign() { - return this.sign; - } - - public void setSign(String sign) { - this.sign = sign; - } - - public String getSubAppId() { - return subAppId; - } - - public void setSubAppId(String subAppId) { - this.subAppId = subAppId; - } - - public String getSubMchId() { - return subMchId; - } - - public void setSubMchId(String subMchId) { - this.subMchId = subMchId; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String toXML() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(this.getClass()); - return xstream.toXML(this); - } - - /** - *
    -   * 检查参数,并设置签名
    -   * 1、检查参数(注意:子类实现需要检查参数的而外功能时,请在调用父类的方法前进行相应判断)
    -   * 2、补充系统参数,如果未传入则从配置里读取
    -   * 3、生成签名,并设置进去
    -   * 
    - * @param config 支付配置对象,用于读取相应系统配置信息 - */ - public void checkAndSign(WxPayConfig config) throws WxErrorException { - this.checkFields(); - - if (StringUtils.isBlank(getAppid())) { - this.setAppid(config.getAppId()); - } - - if (StringUtils.isBlank(getMchId())) { - this.setMchId(config.getMchId()); - } - - if (StringUtils.isBlank(getSubAppId())) { - this.setSubAppId(config.getSubAppId()); - } - - if (StringUtils.isBlank(getSubMchId())) { - this. setSubMchId(config.getSubMchId()); - } - - if (StringUtils.isBlank(getNonceStr())) { - this.setNonceStr(String.valueOf(System.currentTimeMillis())); - } - - //设置签名字段的值 - this.setSign(SignUtils.createSign(this, config.getMchKey())); - } - -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java new file mode 100644 index 0000000000..34a707bed7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java @@ -0,0 +1,30 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +import java.util.Map; + +/** + *
    + *  支付请求默认对象类
    + *  Created by BinaryWang on 2017/6/18.
    + * 
    + * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayDefaultRequest extends BaseWxPayRequest { + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected boolean ignoreAppid() { + return true; + } + + @Override + protected void storeMap(Map map) { + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java index ed02060f83..383dbe5b7c 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java @@ -1,22 +1,33 @@ package com.github.binarywang.wxpay.bean.request; +import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; import me.chanjar.weixin.common.annotation.Required; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import java.util.Arrays; +import java.util.Map; /** *
      *   微信支付下载对账单请求参数类
      * Created by Binary Wang on 2017-01-11.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayDownloadBillRequest extends WxPayBaseRequest { - private static final String[] BILL_TYPE = new String[]{"ALL", "REFUND", "SUCCESS"}; +public class WxPayDownloadBillRequest extends BaseWxPayRequest { + private static final String[] BILL_TYPES = new String[]{BillType.ALL, BillType.SUCCESS, BillType.REFUND, BillType.RECHARGE_REFUND}; + private static final String TAR_TYPE_GZIP = "GZIP"; /** *
    @@ -31,19 +42,6 @@ public class WxPayDownloadBillRequest extends WxPayBaseRequest {
       @XStreamAlias("device_info")
       private String deviceInfo;
     
    -  /**
    -   * 
    -   * 签名类型
    -   * sign_type
    -   * 否
    -   * String(32)
    -   * HMAC-SHA256
    -   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    -   * 
    - */ - @XStreamAlias("sign_type") - private String signType; - /** *
        * 账单类型
    @@ -87,55 +85,24 @@ public class WxPayDownloadBillRequest extends WxPayBaseRequest {
       @XStreamAlias("tar_type")
       private String tarType;
     
    -  public String getDeviceInfo() {
    -    return deviceInfo;
    -  }
    -
    -  public void setDeviceInfo(String deviceInfo) {
    -    this.deviceInfo = deviceInfo;
    -  }
    -
    -  public String getSignType() {
    -    return signType;
    -  }
    -
    -  public void setSignType(String signType) {
    -    this.signType = signType;
    -  }
    -
    -  public String getBillType() {
    -    return billType;
    -  }
    -
    -  public void setBillType(String billType) {
    -    this.billType = billType;
    -  }
    -
    -  public String getBillDate() {
    -    return billDate;
    -  }
    -
    -  public void setBillDate(String billDate) {
    -    this.billDate = billDate;
    -  }
    -
    -  public String getTarType() {
    -    return tarType;
    -  }
    -
    -  public void setTarType(String tarType) {
    -    this.tarType = tarType;
    -  }
    -
       @Override
    -  protected void checkConstraints() {
    -    if (StringUtils.isNotBlank(this.getTarType()) && !"GZIP".equals(this.getTarType())) {
    -      throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP");
    +  protected void checkConstraints() throws WxPayException {
    +    if (StringUtils.isNotBlank(this.getTarType()) && !TAR_TYPE_GZIP.equals(this.getTarType())) {
    +      throw new WxPayException("tar_type值如果存在,只能为GZIP");
         }
     
    -    if (!ArrayUtils.contains(BILL_TYPE, this.getBillType())) {
    -      throw new IllegalArgumentException(String.format("bill_tpye目前必须为%s其中之一,实际值:%s",
    -        Arrays.toString(BILL_TYPE), this.getBillType()));
    +    if (!ArrayUtils.contains(BILL_TYPES, this.getBillType())) {
    +      throw new WxPayException(String.format("bill_type目前必须为%s其中之一,实际值:%s",
    +        Arrays.toString(BILL_TYPES), this.getBillType()));
         }
       }
    +
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("device_info", deviceInfo);
    +    map.put("bill_type", billType);
    +    map.put("bill_date", billDate);
    +    map.put("tar_type", tarType);
    +  }
    +
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java
    new file mode 100644
    index 0000000000..efb14fc7c0
    --- /dev/null
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java
    @@ -0,0 +1,98 @@
    +package com.github.binarywang.wxpay.bean.request;
    +
    +import com.github.binarywang.wxpay.constant.WxPayConstants.AccountType;
    +import com.github.binarywang.wxpay.exception.WxPayException;
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.*;
    +import me.chanjar.weixin.common.annotation.Required;
    +import org.apache.commons.lang3.ArrayUtils;
    +import org.apache.commons.lang3.StringUtils;
    +
    +import java.util.Arrays;
    +import java.util.Map;
    +
    +/**
    + * 
    + *   微信支付下载资金账单请求参数类
    + * Created by cwivan on 2018-08-02.
    + * 
    + * + * @author cwivan + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayDownloadFundFlowRequest extends BaseWxPayRequest { + private static final String[] ACCOUNT_TYPES = new String[]{AccountType.BASIC, AccountType.OPERATION, AccountType.FEES}; + private static final String SIGN_TYPE_HMAC_SHA256 = "HMAC-SHA256"; + private static final String TAR_TYPE_GZIP = "GZIP"; + + /** + *
    +   * 对账单日期
    +   * bill_date
    +   * 是
    +   * String(8)
    +   * 20140603
    +   * 下载对账单的日期,格式:20140603
    +   * 
    + */ + @Required + @XStreamAlias("bill_date") + private String billDate; + + /** + *
    +   * 资金账户类型
    +   * account_type
    +   * 是
    +   * Basic
    +   * String(8)
    +   * --Basic,基本账户
    +   * --Operation,运营账户
    +   * --Fees,手续费账户
    +   * 
    + */ + @Required + @XStreamAlias("account_type") + private String accountType; + + /** + *
    +   * 压缩账单
    +   * tar_type
    +   * 否
    +   * String(8)
    +   * GZIP
    +   * 非必传参数,固定值:GZIP,返回格式为.gzip的压缩包账单。不传则默认为数据流形式。
    +   * 
    + */ + @XStreamAlias("tar_type") + private String tarType; + + @Override + protected void checkConstraints() throws WxPayException { + if (StringUtils.isNotBlank(this.getTarType()) && !TAR_TYPE_GZIP.equals(this.getTarType())) { + throw new WxPayException("tar_type值如果存在,只能为GZIP"); + } + + if (!ArrayUtils.contains(ACCOUNT_TYPES, this.getAccountType())) { + throw new WxPayException(String.format("account_type必须为%s其中之一,实际值:%s", + Arrays.toString(ACCOUNT_TYPES), this.getAccountType())); + } + /** + * 目前仅支持HMAC-SHA256 + */ + this.setSignType(SIGN_TYPE_HMAC_SHA256); + } + + @Override + protected void storeMap(Map map) { + map.put("bill_date", billDate); + map.put("account_type", accountType); + map.put("tar_type", tarType); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFaceAuthInfoRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFaceAuthInfoRequest.java new file mode 100644 index 0000000000..1e81b0ad41 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFaceAuthInfoRequest.java @@ -0,0 +1,142 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import lombok.experimental.Accessors; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
    + *  获取微信刷脸调用凭证请求对象类
    + *  详见文档:https://pay.weixin.qq.com/wiki/doc/wxfacepay/develop/sdk-android.html#获取数据-getwxpayfacerawdata
    + * Created by Jmdhappy on 2019-09-04.
    + * 
    + * + * @author XxPay + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayFaceAuthInfoRequest extends BaseWxPayRequest { + private static final long serialVersionUID = -2909189635374300870L; + + /** + *
    +   * 字段名:门店编号
    +   * 变量名:store_id
    +   * 是否必填:是
    +   * 类型:string(32)
    +   * 示例值:1001
    +   * 描述:门店编号, 由商户定义, 各门店唯一
    +   * 
    + */ + @Required + @XStreamAlias("store_id") + private String storeId; + + /** + *
    +   * 字段名:门店名称
    +   * 变量名:store_name
    +   * 是否必填:是
    +   * 类型:string(128)
    +   * 示例值:骏易科技
    +   * 描述:门店名称,由商户定义。(可用于展示)
    +   * 
    + */ + @Required + @XStreamAlias("store_name") + private String storeName; + + /** + *
    +   * 字段名:终端设备编号
    +   * 变量名:device_id
    +   * 是否必填:是
    +   * 类型:string(32)
    +   * 示例值:
    +   * 描述:终端设备编号,由商户定义。
    +   * 
    + */ + @Required + @XStreamAlias("device_id") + private String deviceId; + + /** + *
    +   * 字段名:附加字段
    +   * 变量名:attach
    +   * 是否必填:是
    +   * 类型:string
    +   * 示例值:
    +   * 描述:附加字段。字段格式使用Json
    +   * 
    + */ + @XStreamAlias("attach") + private String attach; + + /** + *
    +   * 字段名:初始化数据
    +   * 变量名:attach
    +   * 是否必填:是
    +   * 类型:string(2048)
    +   * 示例值:
    +   * 描述:初始化数据。由微信人脸SDK的接口返回。
    +   * 
    + */ + @Required + @XStreamAlias("rawdata") + private String rawdata; + + /** + *
    +   * 字段名:当前时间
    +   * 变量名:now
    +   * 是否必填:否
    +   * 类型:String(10)
    +   * 示例值:1239878956
    +   * 描述:取当前时间,10位unix时间戳。 例如:1239878956
    +   * 
    + */ + @Required + @XStreamAlias("now") + private String now; + + /** + *
    +   * 字段名:接口版本号.
    +   * 变量名:version
    +   * 是否必填:是
    +   * 类型:String
    +   * 示例值:1.0
    +   * 描述:版本号。固定为1
    +   * 
    + */ + @Required + @XStreamAlias("version") + private String version; + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected void storeMap(Map map) { + map.put("now", now); + map.put("version", version); + map.put("rawdata", rawdata); + map.put("store_id", storeId); + map.put("store_name", storeName); + map.put("device_id", deviceId); + map.put("attach", attach); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFacepayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFacepayRequest.java new file mode 100644 index 0000000000..e9821c506c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFacepayRequest.java @@ -0,0 +1,194 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +import java.util.Map; + +/** + *
    + *  提交刷脸支付请求对象类
    + *  详见文档:微信人脸支付商户开发文档
    + * Created by Jmdhappy on 2019-09-05.
    + * 
    + * + * @author XxPay + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayFacepayRequest extends BaseWxPayRequest { + + /** + *
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:013467007045764
    +   * 描述:终端设备号(商户自定义,如门店编号)
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:商品描述.
    +   * 变量名:body
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:image形象店-深圳腾大- QQ公仔
    +   * 描述:商品或支付单简要描述,格式要求:门店品牌名-城市分店名-实际商品名称
    +   * 
    + **/ + @Required + @XStreamAlias("body") + private String body; + + /** + *
    +   * 字段名:商品详情.
    +   * 变量名:detail
    +   * 是否必填:否
    +   * 类型:String(8192)
    +   * 示例值:
    +   * 描述:商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。
    + **/ + @XStreamAlias("detail") + private String detail; + + /** + *
    +   * 字段名:附加数据.
    +   * 变量名:attach
    +   * 是否必填:否
    +   * 类型:String(127)
    +   * 示例值:说明
    +   * 描述:附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    +   * 
    + **/ + @XStreamAlias("attach") + private String attach; + + /** + *
    +   * 字段名:商户订单号.
    +   * 变量名:out_trade_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1217752501201407033233368018
    +   * 描述:商户系统内部的订单号,32个字符内、可包含字母;更换授权码必须要换新的商户订单号 其他说明见商户订单号
    +   * 
    + **/ + @Required + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
    +   * 字段名:总金额.
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:888
    +   * 描述:订单总金额,单位为分,只能为整数,详见支付金额
    +   * 
    + **/ + @Required + @XStreamAlias("total_fee") + private Integer totalFee; + + /** + *
    +   * 字段名:货币类型.
    +   * 变量名:fee_type
    +   * 是否必填:否
    +   * 类型:String(16)
    +   * 示例值:CNY
    +   * 描述:符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + **/ + @XStreamAlias("fee_type") + private String feeType; + + /** + *
    +   * 字段名:终端IP.
    +   * 变量名:spbill_create_ip
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:127.0.0.1
    +   * 描述:调用微信支付API的机器IP
    +   * 
    + **/ + @Required + @XStreamAlias("spbill_create_ip") + private String spbillCreateIp; + + /** + *
    +   * 字段名:商品标记.
    +   * 变量名:goods_tag
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:1234
    +   * 描述:商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    +   * 
    + **/ + @XStreamAlias("goods_tag") + private String goodsTag; + + /** + *
    +   * 字段名:用户标识.
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    +   * 描述:用户在商户appid 下的唯一标识
    +   * 
    + */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 字段名:人脸凭证.
    +   * 变量名:face_code
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:
    +   * 描述:人脸凭证,用于刷脸支付
    +   * 
    + **/ + @Required + @XStreamAlias("face_code") + private String faceCode; + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected void storeMap(Map map) { + map.put("device_info", deviceInfo); + map.put("body", body); + map.put("detail", detail); + map.put("attach", attach); + map.put("out_trade_no", outTradeNo); + map.put("total_fee", totalFee.toString()); + map.put("fee_type", feeType); + map.put("spbill_create_ip", spbillCreateIp); + map.put("goods_tag", goodsTag); + map.put("openid", openid); + map.put("face_code", faceCode); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java index a025337806..779dbdce79 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java @@ -1,37 +1,64 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; import me.chanjar.weixin.common.annotation.Required; +import java.util.Map; + /** *
    - *  提交刷卡支付请求对象类
    + *  提交付款码支付请求对象类
      * Created by Binary Wang on 2017-3-23.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayMicropayRequest extends WxPayBaseRequest { +public class WxPayMicropayRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 100577773033376092L; + /** *
    -   * 签名类型
    -   * sign_type
    -   * 否
    -   * String(32)
    -   * HMAC-SHA256
    -   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    -   **/
    -  @XStreamAlias("sign_type")
    -  private String signType;
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:013467007045764
    +   * 描述:终端设备号(商户自定义,如门店编号)
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 字段名:接口版本号.
    +   * 变量名:version
    +   * 是否必填:单品优惠必填
    +   * 类型:String(32)
    +   * 示例值:1.0
    +   * 描述:单品优惠新增字段,区分原接口,固定填写1.0
    +   * 更多信息,详见文档:https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_101&index=1
    +   * 
    + */ + @XStreamAlias("version") + private String version; /** *
    -   * 商品描述
    -   * body
    -   * 是
    -   * String(128)
    -   * image形象店-深圳腾大- QQ公仔
    -   * 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
    +   * 字段名:商品描述.
    +   * 变量名:body
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:image形象店-深圳腾大- QQ公仔
    +   * 描述:商品简单描述,该字段须严格按照规范传递,具体请见参数规定
    +   * 
    **/ @Required @XStreamAlias("body") @@ -39,36 +66,38 @@ public class WxPayMicropayRequest extends WxPayBaseRequest { /** *
    -   * 商品详情
    -   * detail
    -   * 否
    -   * String(6000)
    -   *
    -   * 单品优惠功能字段,需要接入请见详细说明
    +   * 字段名:商品详情.
    +   * 变量名:detail
    +   * 是否必填:否
    +   * 类型:String(6000)
    +   * 示例值:
    +   * 描述:单品优惠功能字段,需要接入请见详细说明
    **/ @XStreamAlias("detail") private String detail; /** *
    -   * 附加数据
    -   * attach
    -   * 否
    -   * String(127)
    -   * 说明
    -   * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    +   * 字段名:附加数据.
    +   * 变量名:attach
    +   * 是否必填:否
    +   * 类型:String(127)
    +   * 示例值:说明
    +   * 描述:附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    +   * 
    **/ @XStreamAlias("attach") private String attach; /** *
    -   * 商户订单号
    -   * out_trade_no
    -   * 是
    -   * String(32)
    -   * 1217752501201407033233368018
    -   * 商户系统内部的订单号,32个字符内、可包含字母,其他说明见商户订单号
    +   * 字段名:商户订单号.
    +   * 变量名:out_trade_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1217752501201407033233368018
    +   * 描述:商户系统内部的订单号,32个字符内、可包含字母,其他说明见商户订单号
    +   * 
    **/ @Required @XStreamAlias("out_trade_no") @@ -76,12 +105,13 @@ public class WxPayMicropayRequest extends WxPayBaseRequest { /** *
    -   * 订单金额
    -   * total_fee
    -   * 是
    -   * Int
    -   * 888
    -   * 订单总金额,单位为分,只能为整数,详见支付金额
    +   * 字段名:订单金额.
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:888
    +   * 描述:订单总金额,单位为分,只能为整数,详见支付金额
    +   * 
    **/ @Required @XStreamAlias("total_fee") @@ -89,24 +119,26 @@ public class WxPayMicropayRequest extends WxPayBaseRequest { /** *
    -   * 货币类型
    -   * fee_type
    -   * 否
    -   * String(16)
    -   * CNY
    -   * 符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 字段名:货币类型.
    +   * 变量名:fee_type
    +   * 是否必填:否
    +   * 类型:String(16)
    +   * 示例值:CNY
    +   * 描述:符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    **/ @XStreamAlias("fee_type") private String feeType; /** *
    -   * 终端IP
    -   * spbill_create_ip
    -   * 是
    -   * String(16)
    -   * 8.8.8.8
    -   * 调用微信支付API的机器IP
    +   * 字段名:终端IP.
    +   * 变量名:spbill_create_ip
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:8.8.8.8
    +   * 描述:调用微信支付API的机器IP
    +   * 
    **/ @Required @XStreamAlias("spbill_create_ip") @@ -114,267 +146,135 @@ public class WxPayMicropayRequest extends WxPayBaseRequest { /** *
    -   * 商品标记
    -   * goods_tag
    -   * 否
    -   * String(32)
    -   * 1234
    -   * 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    +   * 字段名:商品标记.
    +   * 变量名:goods_tag
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:1234
    +   * 描述:商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    +   * 
    **/ @XStreamAlias("goods_tag") private String goodsTag; /** *
    -   * 指定支付方式
    -   * limit_pay
    -   * 否
    -   * String(32)
    -   * no_credit
    -   * no_credit--指定不能使用信用卡支付
    +   * 字段名:指定支付方式.
    +   * 变量名:limit_pay
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:no_credit
    +   * 描述:no_credit--指定不能使用信用卡支付
    +   * 
    **/ @XStreamAlias("limit_pay") private String limitPay; /** *
    -   * 授权码
    -   * auth_code
    -   * 是
    -   * String(128)
    -   * 120061098828009406
    -   * 扫码支付授权码,设备读取用户微信中的条码或者二维码信息注:用户刷卡条形码规则:18位纯数字,以10、11、12、13、14、15开头)
    +   * 字段名:交易起始时间.
    +   * 变量名:time_start
    +   * 是否必填:否
    +   * 类型:String(14)
    +   * 示例值:20091225091010
    +   * 描述:订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    +   * 
    + */ + @XStreamAlias("time_start") + private String timeStart; + + /** + *
    +   * 字段名:交易结束时间.
    +   * 变量名:time_expire
    +   * 是否必填:否
    +   * 类型:String(14)
    +   * 示例值:20091227091010
    +   * 描述:订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
    +   * 注意:最短失效时间间隔必须大于5分钟
    +   * 
    + */ + @XStreamAlias("time_expire") + private String timeExpire; + + /** + *
    +   * 字段名:电子发票入口开放标识	.
    +   * 变量名:receipt
    +   * 是否必填:否
    +   * 类型:String(8)
    +   * 示例值:Y
    +   * 描述:Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效
    +   * 
    + **/ + @XStreamAlias("receipt") + private String receipt; + + /** + *
    +   * 字段名:授权码.
    +   * 变量名:auth_code
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:120061098828009406
    +   * 描述:扫码支付授权码,设备读取用户微信中的条码或者二维码信息注:用户刷卡条形码规则:18位纯数字,以10、11、12、13、14、15开头)
    +   * 
    **/ @Required @XStreamAlias("auth_code") private String authCode; - private WxPayMicropayRequest(Builder builder) { - setSignType(builder.signType); - setBody(builder.body); - setAppid(builder.appid); - setDetail(builder.detail); - setMchId(builder.mchId); - setAttach(builder.attach); - setSubAppId(builder.subAppId); - setOutTradeNo(builder.outTradeNo); - setSubMchId(builder.subMchId); - setTotalFee(builder.totalFee); - setNonceStr(builder.nonceStr); - setFeeType(builder.feeType); - setSign(builder.sign); - setSpbillCreateIp(builder.spbillCreateIp); - setGoodsTag(builder.goodsTag); - setLimitPay(builder.limitPay); - setAuthCode(builder.authCode); - } - - public static Builder newBuilder() { - return new Builder(); - } - - public String getSignType() { - return this.signType; - } - - public void setSignType(String signType) { - this.signType = signType; - } - - public String getBody() { - return this.body; - } - - public void setBody(String body) { - this.body = body; - } - - public String getDetail() { - return this.detail; - } - - public void setDetail(String detail) { - this.detail = detail; - } - - public String getAttach() { - return this.attach; - } - - public void setAttach(String attach) { - this.attach = attach; - } - - public String getOutTradeNo() { - return this.outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public Integer getTotalFee() { - return this.totalFee; - } - - public void setTotalFee(Integer totalFee) { - this.totalFee = totalFee; - } - - public String getFeeType() { - return this.feeType; - } - - public void setFeeType(String feeType) { - this.feeType = feeType; - } - - public String getSpbillCreateIp() { - return this.spbillCreateIp; - } - - public void setSpbillCreateIp(String spbillCreateIp) { - this.spbillCreateIp = spbillCreateIp; - } - - public String getGoodsTag() { - return this.goodsTag; - } - - public void setGoodsTag(String goodsTag) { - this.goodsTag = goodsTag; - } - - public String getLimitPay() { - return this.limitPay; - } - - public void setLimitPay(String limitPay) { - this.limitPay = limitPay; - } - - public String getAuthCode() { - return this.authCode; - } + /** + *
    +   * 字段名:场景信息.
    +   * 变量名:scene_info
    +   * 是否必填:否
    +   * 类型:String(256)
    +   * 示例值:{"store_info" : {
    +   * "id": "SZTX001",
    +   * "name": "腾大餐厅",
    +   * "area_code": "440305",
    +   * "address": "科技园中一路腾讯大厦" }}
    +   * 描述:该字段用于上报场景信息,目前支持上报实际门店信息。该字段为JSON对象数据,对象格式为{"store_info":{"id": "门店ID","name": "名称","area_code": "编码","address": "地址" }}
    +   * 
    + */ + @XStreamAlias("scene_info") + private String sceneInfo; - public void setAuthCode(String authCode) { - this.authCode = authCode; - } + /** + *
    +   * 字段名:是否指定服务商分账.
    +   * 变量名:profit_sharing
    +   * 是否必填:否
    +   * 详情:Y-是,需要分账  N-否,不分账,字母要求大写,不传默认不分账
    +   * 详细参考 https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=24_3&index=3
    +   * 
    + */ + @XStreamAlias("profit_sharing") + private String profitSharing; @Override protected void checkConstraints() { //do nothing } - public static final class Builder { - private String signType; - private String body; - private String appid; - private String detail; - private String mchId; - private String attach; - private String subAppId; - private String outTradeNo; - private String subMchId; - private Integer totalFee; - private String nonceStr; - private String feeType; - private String sign; - private String spbillCreateIp; - private String goodsTag; - private String limitPay; - private String authCode; - - private Builder() { - } - - public Builder signType(String signType) { - this.signType = signType; - return this; - } - - public Builder body(String body) { - this.body = body; - return this; - } - - public Builder appid(String appid) { - this.appid = appid; - return this; - } - - public Builder detail(String detail) { - this.detail = detail; - return this; - } - - public Builder mchId(String mchId) { - this.mchId = mchId; - return this; - } - - public Builder attach(String attach) { - this.attach = attach; - return this; - } - - public Builder subAppId(String subAppId) { - this.subAppId = subAppId; - return this; - } - - public Builder outTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - return this; - } - - public Builder subMchId(String subMchId) { - this.subMchId = subMchId; - return this; - } - - public Builder totalFee(Integer totalFee) { - this.totalFee = totalFee; - return this; - } - - public Builder nonceStr(String nonceStr) { - this.nonceStr = nonceStr; - return this; - } - - public Builder feeType(String feeType) { - this.feeType = feeType; - return this; - } - - public Builder sign(String sign) { - this.sign = sign; - return this; - } - - public Builder spbillCreateIp(String spbillCreateIp) { - this.spbillCreateIp = spbillCreateIp; - return this; - } - - public Builder goodsTag(String goodsTag) { - this.goodsTag = goodsTag; - return this; - } - - public Builder limitPay(String limitPay) { - this.limitPay = limitPay; - return this; - } - - public Builder authCode(String authCode) { - this.authCode = authCode; - return this; - } - - public WxPayMicropayRequest build() { - return new WxPayMicropayRequest(this); - } + @Override + protected void storeMap(Map map) { + map.put("version", version); + map.put("body", body); + map.put("detail", detail); + map.put("attach", attach); + map.put("out_trade_no", outTradeNo); + map.put("total_fee", totalFee.toString()); + map.put("fee_type", feeType); + map.put("spbill_create_ip", spbillCreateIp); + map.put("goods_tag", goodsTag); + map.put("limit_pay", limitPay); + map.put("time_start", timeStart); + map.put("time_expire", timeExpire); + map.put("auth_code", authCode); + map.put("scene_info", sceneInfo); + map.put("profit_sharing",profitSharing); } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java index 21968ac983..3758653a03 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java @@ -1,16 +1,25 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; + +import java.util.Map; /** *
      *  关闭订单请求对象类
      * Created by Binary Wang on 2016-10-27.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayOrderCloseRequest extends WxPayBaseRequest { +public class WxPayOrderCloseRequest extends BaseWxPayRequest { /** *
    @@ -25,16 +34,14 @@ public class WxPayOrderCloseRequest extends WxPayBaseRequest {
       @XStreamAlias("out_trade_no")
       private String outTradeNo;
     
    -  public String getOutTradeNo() {
    -    return this.outTradeNo;
    -  }
    +  @Override
    +  protected void checkConstraints() {
     
    -  public void setOutTradeNo(String outTradeNo) {
    -    this.outTradeNo = outTradeNo;
       }
     
       @Override
    -  protected void checkConstraints() {
    -
    +  protected void storeMap(Map map) {
    +    map.put("out_trade_no", outTradeNo);
       }
    +
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
    index 499727995e..ffcd52d171 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
    @@ -1,8 +1,12 @@
     package com.github.binarywang.wxpay.bean.request;
     
    +import com.github.binarywang.wxpay.exception.WxPayException;
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.*;
     import org.apache.commons.lang3.StringUtils;
     
    +import java.util.Map;
    +
     /**
      * 
      * 订单查询请求对象
    @@ -16,10 +20,30 @@
      * 
  30. 描述 *
  31. * - * @author binarywang(Binary Wang) + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayOrderQueryRequest extends WxPayBaseRequest { +public class WxPayOrderQueryRequest extends BaseWxPayRequest { + + /** + *
    +   * 字段名:接口版本号.
    +   * 变量名:version
    +   * 是否必填:单品优惠必填
    +   * 类型:String(32)
    +   * 示例值:1.0
    +   * 描述:单品优惠新增字段,区分原接口,固定填写1.0,
    +   * 查单接口上传version后查询结果才返回单品信息,不上传不返回单品信息。
    +   * 更多信息,详见文档:https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_102&index=2
    +   * 
    + */ + @XStreamAlias("version") + private String version; /** *
    @@ -47,27 +71,19 @@ public class WxPayOrderQueryRequest extends WxPayBaseRequest {
       @XStreamAlias("out_trade_no")
       private String outTradeNo;
     
    -  public String getTransactionId() {
    -    return this.transactionId;
    -  }
    -
    -  public void setTransactionId(String transactionId) {
    -    this.transactionId = transactionId;
    -  }
    -
    -  public String getOutTradeNo() {
    -    return this.outTradeNo;
    -  }
    -
    -  public void setOutTradeNo(String outTradeNo) {
    -    this.outTradeNo = outTradeNo;
    -  }
    -
       @Override
    -  protected void checkConstraints() {
    +  protected void checkConstraints() throws WxPayException {
         if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) ||
           (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo))) {
    -      throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一");
    +      throw new WxPayException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一");
         }
       }
    +
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("version", version);
    +    map.put("transaction_id", transactionId);
    +    map.put("out_trade_no", outTradeNo);
    +  }
    +
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
    index 6aef2aa0fa..73c5034696 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
    @@ -1,17 +1,27 @@
     package com.github.binarywang.wxpay.bean.request;
     
    +import com.github.binarywang.wxpay.exception.WxPayException;
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.*;
     import org.apache.commons.lang3.StringUtils;
     
    +import java.util.Map;
    +
     /**
      * 
      * 撤销订单请求类
      * Created by Binary Wang on 2017-3-23.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayOrderReverseRequest extends WxPayBaseRequest { +public class WxPayOrderReverseRequest extends BaseWxPayRequest { /** *
    @@ -38,126 +48,17 @@ public class WxPayOrderReverseRequest extends WxPayBaseRequest {
       @XStreamAlias("out_trade_no")
       private String outTradeNo;
     
    -  /**
    -   * 
    -   * 签名类型
    -   * sign_type
    -   * 否
    -   * String(32)
    -   * HMAC-SHA256
    -   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    -   **/
    -  @XStreamAlias("sign_type")
    -  private String signType;
    -
    -  private WxPayOrderReverseRequest(Builder builder) {
    -    setTransactionId(builder.transactionId);
    -    setAppid(builder.appid);
    -    setOutTradeNo(builder.outTradeNo);
    -    setMchId(builder.mchId);
    -    setSignType(builder.signType);
    -    setSubAppId(builder.subAppId);
    -    setSubMchId(builder.subMchId);
    -    setNonceStr(builder.nonceStr);
    -    setSign(builder.sign);
    -  }
    -
    -  public static Builder newBuilder() {
    -    return new Builder();
    -  }
    -
    -  public String getTransactionId() {
    -    return this.transactionId;
    -  }
    -
    -  public void setTransactionId(String transactionId) {
    -    this.transactionId = transactionId;
    -  }
    -
    -  public String getOutTradeNo() {
    -    return this.outTradeNo;
    -  }
    -
    -  public void setOutTradeNo(String outTradeNo) {
    -    this.outTradeNo = outTradeNo;
    -  }
    -
    -  public String getSignType() {
    -    return this.signType;
    -  }
    -
    -  public void setSignType(String signType) {
    -    this.signType = signType;
    -  }
    -
       @Override
    -  protected void checkConstraints() {
    +  protected void checkConstraints() throws WxPayException {
         if (StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) {
    -      throw new IllegalArgumentException("transaction_id 和 out_trade_no不能同时为空!");
    +      throw new WxPayException("transaction_id 和 out_trade_no不能同时为空!");
         }
       }
     
    -  public static final class Builder {
    -    private String transactionId;
    -    private String appid;
    -    private String outTradeNo;
    -    private String mchId;
    -    private String signType;
    -    private String subAppId;
    -    private String subMchId;
    -    private String nonceStr;
    -    private String sign;
    -
    -    private Builder() {
    -    }
    -
    -    public Builder transactionId(String transactionId) {
    -      this.transactionId = transactionId;
    -      return this;
    -    }
    -
    -    public Builder appid(String appid) {
    -      this.appid = appid;
    -      return this;
    -    }
    -
    -    public Builder outTradeNo(String outTradeNo) {
    -      this.outTradeNo = outTradeNo;
    -      return this;
    -    }
    -
    -    public Builder mchId(String mchId) {
    -      this.mchId = mchId;
    -      return this;
    -    }
    -
    -    public Builder signType(String signType) {
    -      this.signType = signType;
    -      return this;
    -    }
    -
    -    public Builder subAppId(String subAppId) {
    -      this.subAppId = subAppId;
    -      return this;
    -    }
    -
    -    public Builder subMchId(String subMchId) {
    -      this.subMchId = subMchId;
    -      return this;
    -    }
    -
    -    public Builder nonceStr(String nonceStr) {
    -      this.nonceStr = nonceStr;
    -      return this;
    -    }
    -
    -    public Builder sign(String sign) {
    -      this.sign = sign;
    -      return this;
    -    }
    -
    -    public WxPayOrderReverseRequest build() {
    -      return new WxPayOrderReverseRequest(this);
    -    }
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("transaction_id", transactionId);
    +    map.put("out_trade_no", outTradeNo);
       }
    +
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
    new file mode 100644
    index 0000000000..5c4a9fe2ff
    --- /dev/null
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
    @@ -0,0 +1,99 @@
    +package com.github.binarywang.wxpay.bean.request;
    +
    +import com.github.binarywang.wxpay.exception.WxPayException;
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.*;
    +import me.chanjar.weixin.common.annotation.Required;
    +
    +import java.util.Map;
    +
    +/**
    + * 
    + *  拉取订单评价数据接口的请求参数封装类.
    + *  Created by BinaryWang on 2017/9/2.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayQueryCommentRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 2633600418272768186L; + + /** + *
    +   * 字段名:开始时间.
    +   * 变量名:begin_time
    +   * 是否必填:是
    +   * 类型:String(19)
    +   * 示例值:20170724000000
    +   * 描述:按用户评论时间批量拉取的起始时间,格式为yyyyMMddHHmmss
    +   * 
    + */ + @Required + @XStreamAlias("begin_time") + private String beginTime; + + /** + *
    +   * 字段名:结束时间.
    +   * 变量名:end_time
    +   * 是否必填:是
    +   * 类型:String(19)
    +   * 示例值:20170725000000
    +   * 描述:按用户评论时间批量拉取的结束时间,格式为yyyyMMddHHmmss
    +   * 
    + */ + @Required + @XStreamAlias("end_time") + private String endTime; + + /** + *
    +   * 字段名:位移.
    +   * 变量名:offset
    +   * 是否必填:是
    +   * 类型:uint(64)
    +   * 示例值:0
    +   * 描述:指定从某条记录的下一条开始返回记录。接口调用成功时,会返回本次查询最后一条数据的offset。商户需要翻页时,应该把本次调用返回的offset 作为下次调用的入参。注意offset是评论数据在微信支付后台保存的索引,未必是连续的
    +   * 
    + */ + @Required + @XStreamAlias("offset") + private Integer offset; + + /** + *
    +   * 字段名:条数.
    +   * 变量名:limit
    +   * 是否必填:否
    +   * 类型:uint(32)
    +   * 示例值:100
    +   * 描述:一次拉取的条数, 最大值是200,默认是200
    +   * 
    + */ + @XStreamAlias("limit") + private Integer limit; + + /** + * 检查约束情况. + */ + @Override + protected void checkConstraints() throws WxPayException { + } + + @Override + protected void storeMap(Map map) { + map.put("begin_time", beginTime); + map.put("end_time", endTime); + map.put("offset", offset.toString()); + if (limit != null) { + map.put("limit", limit.toString()); + } + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryExchangeRateRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryExchangeRateRequest.java new file mode 100644 index 0000000000..c4b453b9bd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryExchangeRateRequest.java @@ -0,0 +1,61 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import lombok.experimental.Accessors; + +import java.util.Map; + +/** + * 查询汇率请求. + * + * @author Binary Wang + * @date 2020-05-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class WxPayQueryExchangeRateRequest extends BaseWxPayRequest { + private static final long serialVersionUID = -8796516942563060554L; + /** + * 币种 + * fee_type + * 是 + * String(10) + * USD + * 外币币种 + */ + @XStreamAlias("fee_type") + private String feeType; + + /** + * 日期 + * date + * 是 + * String(14) + * 20150807 + * 格式为yyyyMMdd,如2009年12月25日表示为20091225。时区为GMT+8 beijing + */ + @XStreamAlias("date") + private String date; + + @Override + protected void checkConstraints() throws WxPayException { + + } + + @Override + protected void storeMap(Map map) { + + } + + @Override + protected boolean needNonceStr() { + return false; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java index 191db46975..01ca5cafd4 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java @@ -1,6 +1,9 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; + +import java.util.Map; /** *
    @@ -12,11 +15,23 @@
      *   类型
      *   说明
      * Created by Binary Wang on 2016-11-28.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayRedpackQueryRequest extends WxPayBaseRequest { +public class WxPayRedpackQueryRequest extends BaseWxPayRequest { + + @Override + protected String[] getIgnoredParamsForSign() { + return new String[]{"sub_appid", "sub_mch_id", "sign_type"}; + } + /** * 商户订单号 * mch_billno @@ -39,24 +54,14 @@ public class WxPayRedpackQueryRequest extends WxPayBaseRequest { @XStreamAlias("bill_type") private String billType; - public String getBillType() { - return billType; - } - - public void setBillType(String billType) { - this.billType = billType; - } - - public String getMchBillNo() { - return mchBillNo; - } + @Override + protected void checkConstraints() { - public void setMchBillNo(String mchBillNo) { - this.mchBillNo = mchBillNo; } @Override - protected void checkConstraints() { - + protected void storeMap(Map map) { + map.put("mch_billno", mchBillNo); + map.put("bill_type", billType); } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java index 1efa23db7a..875a702b26 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java @@ -1,16 +1,26 @@ package com.github.binarywang.wxpay.bean.request; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; import org.apache.commons.lang3.StringUtils; +import java.util.Map; + /** *
      * Created by Binary Wang on 2016-11-24.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayRefundQueryRequest extends WxPayBaseRequest { +public class WxPayRefundQueryRequest extends BaseWxPayRequest { /** *
        * 设备号
    @@ -24,19 +34,6 @@ public class WxPayRefundQueryRequest extends WxPayBaseRequest {
       @XStreamAlias("device_info")
       private String deviceInfo;
     
    -  /**
    -   * 
    -   * 签名类型
    -   * sign_type
    -   * 否
    -   * String(32)
    -   * HMAC-SHA256
    -   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    -   * 
    - */ - @XStreamAlias("sign_type") - private String signType; - //************以下四选一************ /** *
    @@ -86,62 +83,22 @@ public class WxPayRefundQueryRequest extends WxPayBaseRequest {
       @XStreamAlias("refund_id")
       private String refundId;
     
    -  public String getDeviceInfo() {
    -    return deviceInfo;
    -  }
    -
    -  public void setDeviceInfo(String deviceInfo) {
    -    this.deviceInfo = deviceInfo;
    -  }
    -
    -  public String getSignType() {
    -    return signType;
    -  }
    -
    -  public void setSignType(String signType) {
    -    this.signType = signType;
    -  }
    -
    -  public String getTransactionId() {
    -    return transactionId;
    -  }
    -
    -  public void setTransactionId(String transactionId) {
    -    this.transactionId = transactionId;
    -  }
    -
    -  public String getOutTradeNo() {
    -    return outTradeNo;
    -  }
    -
    -  public void setOutTradeNo(String outTradeNo) {
    -    this.outTradeNo = outTradeNo;
    -  }
    -
    -  public String getOutRefundNo() {
    -    return outRefundNo;
    -  }
    -
    -  public void setOutRefundNo(String outRefundNo) {
    -    this.outRefundNo = outRefundNo;
    -  }
    -
    -  public String getRefundId() {
    -    return refundId;
    -  }
    -
    -  public void setRefundId(String refundId) {
    -    this.refundId = refundId;
    -  }
    -
       @Override
    -  protected void checkConstraints() {
    +  protected void checkConstraints() throws WxPayException {
         if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)
           && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) ||
           (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo)
             && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) {
    -      throw new IllegalArgumentException("transaction_id,out_trade_no,out_refund_no,refund_id 必须四选一");
    +      throw new WxPayException("transactionId,outRefundNo,transactionId,refundId 必须四选一");
         }
    +  }
     
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("device_info", deviceInfo);
    +    map.put("transaction_id", transactionId);
    +    map.put("out_trade_no", outTradeNo);
    +    map.put("out_refund_no", outRefundNo);
    +    map.put("refund_id", refundId);
       }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
    index c7fc8c269c..e145644d91 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
    @@ -1,159 +1,148 @@
     package com.github.binarywang.wxpay.bean.request;
     
     import com.github.binarywang.wxpay.config.WxPayConfig;
    +import com.github.binarywang.wxpay.constant.WxPayConstants.RefundAccountSource;
    +import com.github.binarywang.wxpay.exception.WxPayException;
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import com.thoughtworks.xstream.annotations.XStreamConverter;
    +import lombok.*;
    +import lombok.experimental.Accessors;
     import me.chanjar.weixin.common.annotation.Required;
    -import me.chanjar.weixin.common.exception.WxErrorException;
    +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
     import org.apache.commons.lang3.ArrayUtils;
     import org.apache.commons.lang3.StringUtils;
     
     import java.util.Arrays;
    +import java.util.Map;
     
     /**
      * 
      * 微信支付-申请退款请求参数
    - * 注释中各行每个字段描述对应如下:
    - * 
  32. 字段名 - *
  33. 变量名 - *
  34. 是否必填 - *
  35. 类型 - *
  36. 示例值 - *
  37. 描述 * Created by Binary Wang on 2016-10-08. *
  38. * - * @author binarywang(Binary Wang) + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayRefundRequest extends WxPayBaseRequest { - private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", - "REFUND_SOURCE_UNSETTLED_FUNDS"}; +@Accessors(chain = true) +public class WxPayRefundRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 522565152886671848L; - @Override - public void checkAndSign(WxPayConfig config) throws WxErrorException { - if (StringUtils.isBlank(this.getOpUserId())) { - this.setOpUserId(config.getMchId()); - } - - super.checkAndSign(config); - } + private static final String[] REFUND_ACCOUNT = new String[]{ + RefundAccountSource.RECHARGE_FUNDS, RefundAccountSource.UNSETTLED_FUNDS}; /** *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * String(32)
    -   * 13467007045764
    -   * 终端设备号
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:13467007045764
    +   * 描述:终端设备号
        * 
    */ @XStreamAlias("device_info") private String deviceInfo; - /** *
    -   * 微信订单号
    -   * transaction_id
    -   * 跟out_trade_no二选一
    -   * String(28)
    -   * 1217752501201400000000000000
    -   * 微信生成的订单号,在支付通知中有返回
    +   * 字段名:微信订单号.
    +   * 变量名:transaction_id
    +   * 是否必填:跟out_trade_no二选一
    +   * 类型:String(28)
    +   * 示例值:1217752501201400000000000000
    +   * 描述:微信生成的订单号,在支付通知中有返回
        * 
    */ @XStreamAlias("transaction_id") private String transactionId; - /** *
    -   * 商户订单号
    -   * out_trade_no
    -   * 跟transaction_id二选一
    -   * String(32)
    -   * 1217752501201400000000000000
    -   * 商户侧传给微信的订单号
    +   * 字段名:商户订单号.
    +   * 变量名:out_trade_no
    +   * 是否必填:跟transaction_id二选一
    +   * 类型:String(32)
    +   * 示例值:1217752501201400000000000000
    +   * 描述:商户侧传给微信的订单号
        * 
    */ @XStreamAlias("out_trade_no") private String outTradeNo; - /** *
    -   * 商户退款单号
    -   * out_refund_no
    -   * 是
    -   * String(32)
    -   * 1217752501201400000000000000
    -   * 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
    +   * 字段名:商户退款单号.
    +   * 变量名:out_refund_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1217752501201400000000000000
    +   * 描述:商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
        * 
    */ @Required @XStreamAlias("out_refund_no") private String outRefundNo; - /** *
    -   * 订单金额
    -   * total_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 订单总金额,单位为分,只能为整数,详见支付金额
    +   * 字段名:订单金额.
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:订单总金额,单位为分,只能为整数,详见支付金额
        * 
    */ @Required @XStreamAlias("total_fee") private Integer totalFee; - /** *
    -   * 退款金额
    -   * refund_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 退款总金额,订单总金额,单位为分,只能为整数,详见支付金额
    +   * 字段名:退款金额.
    +   * 变量名:refund_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:退款总金额,订单总金额,单位为分,只能为整数,详见支付金额
        * 
    */ @Required @XStreamAlias("refund_fee") private Integer refundFee; - /** *
    -   * 货币种类
    -   * refund_fee_type
    -   * 否
    -   * String(8)
    -   * CNY
    -   * 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 字段名:货币种类.
    +   * 变量名:refund_fee_type
    +   * 是否必填:否
    +   * 类型:String(8)
    +   * 示例值:CNY
    +   * 描述:货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
        * 
    */ @XStreamAlias("refund_fee_type") private String refundFeeType; - /** *
    -   * 操作员
    -   * op_user_id
    -   * 是
    -   * String(32)
    -   * 1900000109
    -   * 操作员帐号, 默认为商户号
    +   * 字段名:操作员.
    +   * 变量名:op_user_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1900000109
    +   * 描述:操作员帐号, 默认为商户号
        * 
    */ - //@Required @XStreamAlias("op_user_id") private String opUserId; - /** *
    -   * 退款资金来源
    -   * refund_account
    -   * 否
    -   * String(30)
    -   * REFUND_SOURCE_RECHARGE_FUNDS
    -   * 仅针对老资金流商户使用,
    +   * 字段名:退款资金来源.
    +   * 变量名:refund_account
    +   * 是否必填:否
    +   * 类型:String(30)
    +   * 示例值:REFUND_SOURCE_RECHARGE_FUNDS
    +   * 描述:仅针对老资金流商户使用,
        * 
  39. REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款), *
  40. REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款 *
  41. @@ -161,214 +150,117 @@ public void checkAndSign(WxPayConfig config) throws WxErrorException { @XStreamAlias("refund_account") private String refundAccount; - public WxPayRefundRequest() { - } - - private WxPayRefundRequest(Builder builder) { - setDeviceInfo(builder.deviceInfo); - setAppid(builder.appid); - setTransactionId(builder.transactionId); - setMchId(builder.mchId); - setOutTradeNo(builder.outTradeNo); - setSubAppId(builder.subAppId); - setSubMchId(builder.subMchId); - setOutRefundNo(builder.outRefundNo); - setNonceStr(builder.nonceStr); - setTotalFee(builder.totalFee); - setSign(builder.sign); - setRefundFee(builder.refundFee); - setRefundFeeType(builder.refundFeeType); - setOpUserId(builder.opUserId); - setRefundAccount(builder.refundAccount); - } - - public static Builder newBuilder() { - return new Builder(); - } - - public String getDeviceInfo() { - return this.deviceInfo; - } - - public void setDeviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - } - - public String getTransactionId() { - return this.transactionId; - } - - public void setTransactionId(String transactionId) { - this.transactionId = transactionId; - } - - public String getOutTradeNo() { - return this.outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public String getOutRefundNo() { - return this.outRefundNo; - } - - public void setOutRefundNo(String outRefundNo) { - this.outRefundNo = outRefundNo; - } - - public Integer getTotalFee() { - return this.totalFee; - } - - public void setTotalFee(Integer totalFee) { - this.totalFee = totalFee; - } - - public Integer getRefundFee() { - return this.refundFee; - } - - public void setRefundFee(Integer refundFee) { - this.refundFee = refundFee; - } - - public String getRefundFeeType() { - return this.refundFeeType; - } - - public void setRefundFeeType(String refundFeeType) { - this.refundFeeType = refundFeeType; - } + /** + *
    +   * 字段名:退款原因.
    +   * 变量名:refund_account
    +   * 是否必填:否
    +   * 类型:String(80)
    +   * 示例值:商品已售完
    +   * 描述:若商户传入,会在下发给用户的退款消息中体现退款原因
    +   * 
    + */ + @XStreamAlias("refund_desc") + private String refundDesc; - public String getOpUserId() { - return this.opUserId; - } + /** + *
    +   * 字段名:退款结果通知url.
    +   * 变量名:notify_url
    +   * 是否必填:否
    +   * 类型:String(256)
    +   * 示例值:https://weixin.qq.com/notify/
    +   * 描述:	异步接收微信支付退款结果通知的回调地址,通知URL必须为外网可访问的url,不允许带参数
    +   * 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效。
    +   * 
    + */ + @XStreamAlias("notify_url") + private String notifyUrl; - public void setOpUserId(String opUserId) { - this.opUserId = opUserId; - } + /** + *
    +   * 字段名:商品详情
    +   * 变量名:detail
    +   * 类型:否
    +   * 示例值:String(6000)
    +   * 退款包含的商品列表信息detail字段列表说明:
    +   *
    +   * 字段名	变量名	必填	类型	示例值	描述
    +   * 商品列表	goods_detail	是	String	示例见下文	商品信息,使用Json数组格式提交
    +   * 商品列表goods_detail字段列表说明:
    +   *
    +   * 字段名	变量名	必填	类型	示例值	描述
    +   * 商品编码	goods_id	是	String(32)	商品编码	由半角的大小写字母、数字、中划线、下划线中的一种或几种组成
    +   * 微信侧商品编码	wxpay_goods_id	否	String(32)	1001	微信支付定义的统一商品编号(没有可不传)
    +   * 商品名称	goods_name	否	String(256)	iPhone6s 16G	商品的实际名称
    +   * 商品退款金额	refund_amount	是	int	528800	商品退款金额
    +   * 商品退货数量	refund_quantity	是	int	1	单品的退款数量
    +   * 商品单价	price	是	int	528800	单位为:分。如果商户有优惠,需传输商户优惠后的单价(例如:用户对一笔100元的订单使用了商场发的优惠券100-50,则活动商品的单价应为原单价-50)
    +   * detail字段值举例如下:
    +   *
    +   * {
    +   * "goods_detail": [
    +   * {
    +   * "goods_id": "商品编码",
    +   * "wxpay_goods_id": "1001",
    +   * "goods_name": "iPhone6s 16G",
    +   * "refund_amount": 528800,
    +   * "refund_quantity": 1,
    +   * "price": 528800
    +   * },
    +   * {
    +   * "goods_id": "商品编码",
    +   * "wxpay_goods_id": "1001",
    +   * "goods_name": "iPhone6s 16G",
    +   * "refund_amount": 528800,
    +   * "refund_quantity": 1,
    +   * "price": 608800
    +   * }
    +   * ]
    +   * }
    +   * 描述:退款包含的商品列表信息,全额退款可不传,必须按照规范上传,JSON格式
    +   * 
    + */ + @XStreamAlias("detail") + @XStreamConverter(value = XStreamCDataConverter.class) + private String detail; - public String getRefundAccount() { - return this.refundAccount; - } + @Override + public void checkAndSign(WxPayConfig config) throws WxPayException { + if (StringUtils.isBlank(this.getOpUserId())) { + this.setOpUserId(config.getMchId()); + } - public void setRefundAccount(String refundAccount) { - this.refundAccount = refundAccount; + super.checkAndSign(config); } @Override - protected void checkConstraints() { + protected void checkConstraints() throws WxPayException { if (StringUtils.isNotBlank(this.getRefundAccount())) { if (!ArrayUtils.contains(REFUND_ACCOUNT, this.getRefundAccount())) { - throw new IllegalArgumentException(String.format("refund_account目前必须为%s其中之一,实际值:%s", - Arrays.toString(REFUND_ACCOUNT), this.getRefundAccount())); + throw new WxPayException( + String.format("refund_account目前必须为%s其中之一,实际值:%s", Arrays.toString(REFUND_ACCOUNT), this.getRefundAccount())); } } if (StringUtils.isBlank(this.getOutTradeNo()) && StringUtils.isBlank(this.getTransactionId())) { - throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个"); + throw new WxPayException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个"); } } - public static final class Builder { - private String deviceInfo; - private String appid; - private String transactionId; - private String mchId; - private String outTradeNo; - private String subAppId; - private String subMchId; - private String outRefundNo; - private String nonceStr; - private Integer totalFee; - private String sign; - private Integer refundFee; - private String refundFeeType; - private String opUserId; - private String refundAccount; - - private Builder() { - } - - public Builder deviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - return this; - } - - public Builder appid(String appid) { - this.appid = appid; - return this; - } - - public Builder transactionId(String transactionId) { - this.transactionId = transactionId; - return this; - } - - public Builder mchId(String mchId) { - this.mchId = mchId; - return this; - } - - public Builder outTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - return this; - } - - public Builder subAppId(String subAppId) { - this.subAppId = subAppId; - return this; - } - - public Builder subMchId(String subMchId) { - this.subMchId = subMchId; - return this; - } - - public Builder outRefundNo(String outRefundNo) { - this.outRefundNo = outRefundNo; - return this; - } - - public Builder nonceStr(String nonceStr) { - this.nonceStr = nonceStr; - return this; - } - - public Builder totalFee(Integer totalFee) { - this.totalFee = totalFee; - return this; - } - - public Builder sign(String sign) { - this.sign = sign; - return this; - } - - public Builder refundFee(Integer refundFee) { - this.refundFee = refundFee; - return this; - } - - public Builder refundFeeType(String refundFeeType) { - this.refundFeeType = refundFeeType; - return this; - } - - public Builder opUserId(String opUserId) { - this.opUserId = opUserId; - return this; - } - - public Builder refundAccount(String refundAccount) { - this.refundAccount = refundAccount; - return this; - } - - public WxPayRefundRequest build() { - return new WxPayRefundRequest(this); - } + @Override + protected void storeMap(Map map) { + map.put("device_info", deviceInfo); + map.put("transaction_id", transactionId); + map.put("out_trade_no", outTradeNo); + map.put("out_refund_no", outRefundNo); + map.put("total_fee", totalFee.toString()); + map.put("refund_fee", refundFee.toString()); + map.put("refund_fee_type", refundFeeType); + map.put("op_user_id", opUserId); + map.put("refund_account", refundAccount); + map.put("refund_desc", refundDesc); + map.put("notify_url", notifyUrl); } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java index 8b3f88b9c9..56cd490a55 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java @@ -1,8 +1,11 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; import me.chanjar.weixin.common.annotation.Required; +import java.util.Map; + /** *
      * 微信支付-交易保障请求参数
    @@ -15,13 +18,20 @@
      * 
  42. 描述 *
  43. * - * @author binarywang(Binary Wang) + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayReportRequest extends WxPayBaseRequest { +public class WxPayReportRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 2667579776376527663L; + /** *
    -   * 设备号
    +   * 设备号.
        * device_info
        * 否
        * String(32)
    @@ -34,20 +44,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
     
       /**
        * 
    -   * 签名类型
    -   * sign_type
    -   * 否
    -   * String(32)
    -   * HMAC-SHA256
    -   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    -   * 
    - */ - @XStreamAlias("sign_type") - private String signType; - - /** - *
    -   * 接口URL
    +   * 接口URL.
        * interface_url
        * 是
        * String(127)
    @@ -64,7 +61,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String interfaceUrl;
       /**
        * 
    -   * 接口耗时
    +   * 接口耗时.
        * execute_time
        * 是
        * Int
    @@ -77,7 +74,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private Integer executeTime;
       /**
        * 
    -   * 返回状态码
    +   * 返回状态码.
        * return_code
        * 是
        * String(16)
    @@ -90,7 +87,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String returnCode;
       /**
        * 
    -   * 返回信息
    +   * 返回信息.
        * return_msg
        * 否
        * String(128)
    @@ -102,7 +99,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String returnMsg;
       /**
        * 
    -   * 业务结果
    +   * 业务结果.
        * result_code
        * 是
        * String(16)
    @@ -115,7 +112,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String resultCode;
       /**
        * 
    -   * 错误代码
    +   * 错误代码.
        * err_code
        * 否
        * String(32)
    @@ -127,7 +124,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String errCode;
       /**
        * 
    -   * 错误代码描述
    +   * 错误代码描述.
        * err_code_des
        * 否
        * String(128)
    @@ -139,7 +136,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String errCodeDes;
       /**
        * 
    -   * 商户订单号
    +   * 商户订单号.
        * out_trade_no
        * 否
        * String(32)
    @@ -151,7 +148,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String outTradeNo;
       /**
        * 
    -   * 访问接口IP
    +   * 访问接口IP.
        * user_ip
        * 是
        * String(16)
    @@ -164,7 +161,7 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       private String userIp;
       /**
        * 
    -   * 商户上报时间
    +   * 商户上报时间.
        * time
        * 否
        * String(14)
    @@ -175,104 +172,23 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       @XStreamAlias("time")
       private String time;
     
    -  public String getDeviceInfo() {
    -    return deviceInfo;
    -  }
    -
    -  public void setDeviceInfo(String deviceInfo) {
    -    this.deviceInfo = deviceInfo;
    -  }
    -
    -  public String getSignType() {
    -    return signType;
    -  }
    -
    -  public void setSignType(String signType) {
    -    this.signType = signType;
    -  }
    -
    -  public String getInterfaceUrl() {
    -    return interfaceUrl;
    -  }
    -
    -  public void setInterfaceUrl(String interfaceUrl) {
    -    this.interfaceUrl = interfaceUrl;
    -  }
    -
    -  public Integer getExecuteTime() {
    -    return executeTime;
    -  }
    -
    -  public void setExecuteTime(Integer executeTime) {
    -    this.executeTime = executeTime;
    -  }
    -
    -  public String getReturnCode() {
    -    return returnCode;
    -  }
    -
    -  public void setReturnCode(String returnCode) {
    -    this.returnCode = returnCode;
    -  }
    -
    -  public String getReturnMsg() {
    -    return returnMsg;
    -  }
    -
    -  public void setReturnMsg(String returnMsg) {
    -    this.returnMsg = returnMsg;
    -  }
    -
    -  public String getResultCode() {
    -    return resultCode;
    -  }
    -
    -  public void setResultCode(String resultCode) {
    -    this.resultCode = resultCode;
    -  }
    -
    -  public String getErrCode() {
    -    return errCode;
    -  }
    -
    -  public void setErrCode(String errCode) {
    -    this.errCode = errCode;
    -  }
    -
    -  public String getErrCodeDes() {
    -    return errCodeDes;
    -  }
    -
    -  public void setErrCodeDes(String errCodeDes) {
    -    this.errCodeDes = errCodeDes;
    -  }
    -
    -  public String getOutTradeNo() {
    -    return outTradeNo;
    -  }
    -
    -  public void setOutTradeNo(String outTradeNo) {
    -    this.outTradeNo = outTradeNo;
    -  }
    -
    -  public String getUserIp() {
    -    return userIp;
    -  }
    -
    -  public void setUserIp(String userIp) {
    -    this.userIp = userIp;
    -  }
    -
    -  public String getTime() {
    -    return time;
    -  }
    -
    -  public void setTime(String time) {
    -    this.time = time;
    -  }
    -
       @Override
       protected void checkConstraints() {
         //do nothing
       }
    +
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("device_info", deviceInfo);
    +    map.put("interface_url", interfaceUrl);
    +    map.put("execute_time_", executeTime.toString());
    +    map.put("return_code", returnCode);
    +    map.put("return_msg", returnMsg);
    +    map.put("result_code", resultCode);
    +    map.put("err_code", errCode);
    +    map.put("err_code_des", errCodeDes);
    +    map.put("out_trade_no", outTradeNo);
    +    map.put("user_ip", userIp);
    +    map.put("time", time);
    +  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendMiniProgramRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendMiniProgramRedpackRequest.java
    new file mode 100644
    index 0000000000..fd727ab6aa
    --- /dev/null
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendMiniProgramRedpackRequest.java
    @@ -0,0 +1,149 @@
    +package com.github.binarywang.wxpay.bean.request;
    +
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.*;
    +import lombok.experimental.Accessors;
    +
    +import java.util.Map;
    +
    +/**
    + * 发送小程序红包请求参数对象.
    + *
    + * @author Binary Wang
    + */
    +@Data
    +@EqualsAndHashCode(callSuper = true)
    +@NoArgsConstructor
    +@AllArgsConstructor
    +@Builder
    +@XStreamAlias("xml")
    +@Accessors(chain = true)
    +public class WxPaySendMiniProgramRedpackRequest extends BaseWxPayRequest {
    +  private static final long serialVersionUID = -2035425086824987567L;
    +
    +  @Override
    +  protected String[] getIgnoredParamsForSign() {
    +    return new String[]{"sign_type", "sub_appid"};
    +  }
    +
    +  /**
    +   * mch_billno.
    +   * 商户订单号(每个订单号必须唯一)
    +   * 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。  接口根据商户订单号支持重入,如出现超时可再调用。
    +   */
    +  @XStreamAlias("mch_billno")
    +  private String mchBillNo;
    +
    +  /**
    +   * send_name.
    +   * 商户名称
    +   * 红包发送者名称
    +   */
    +  @XStreamAlias("send_name")
    +  private String sendName;
    +
    +  /**
    +   * re_openid.
    +   * 接受红包的用户   用户在wxappid下的openid
    +   */
    +  @XStreamAlias("re_openid")
    +  private String reOpenid;
    +
    +  /**
    +   * total_amount.
    +   * 红包总额
    +   */
    +  @XStreamAlias("total_amount")
    +  private Integer totalAmount;
    +
    +  /**
    +   * total_num
    +   * 红包发放总人数
    +   */
    +  @XStreamAlias("total_num")
    +  private Integer totalNum;
    +
    +  /**
    +   * wishing.
    +   * 红包祝福语
    +   */
    +  @XStreamAlias("wishing")
    +  private String wishing;
    +
    +  /**
    +   * act_name.
    +   * 活动名称
    +   */
    +  @XStreamAlias("act_name")
    +  private String actName;
    +
    +  /**
    +   * remark.
    +   * 备注
    +   */
    +  @XStreamAlias("remark")
    +  private String remark;
    +
    +  /**
    +   * 通知用户形式	.
    +   * 通过JSAPI方式领取红包,小程序红包固定传MINI_PROGRAM_JSAPI
    +   */
    +  @XStreamAlias("notify_way")
    +  private String notifyWay = "MINI_PROGRAM_JSAPI";
    +
    +  /**
    +   * 
    +   * 发放红包使用场景,红包金额大于200时必传
    +   * PRODUCT_1:商品促销
    +   * PRODUCT_2:抽奖
    +   * PRODUCT_3:虚拟物品兑奖
    +   * PRODUCT_4:企业内部福利
    +   * PRODUCT_5:渠道分润
    +   * PRODUCT_6:保险回馈
    +   * PRODUCT_7:彩票派奖
    +   * PRODUCT_8:税务刮奖
    +   * 
    + */ + @XStreamAlias("scene_id") + private String sceneId; + + /** + * wxappid. + * 微信分配的公众账号ID(企业号corpid即为此appId)。 + * 接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的), + * 不能为APP的appid(在open.weixin.qq.com申请的) + */ + @XStreamAlias("wxappid") + private String wxAppid; + + @Override + protected void checkConstraints() { + + } + + @Override + protected void storeMap(Map map) { + map.put("mch_billno", mchBillNo); + map.put("send_name", sendName); + map.put("re_openid", reOpenid); + map.put("total_amount", totalAmount.toString()); + map.put("total_num", totalNum.toString()); + map.put("wishing", wishing); + map.put("act_name", actName); + map.put("remark", remark); + map.put("notify_way", notifyWay); + map.put("scene_id", sceneId); + map.put("wxappid", wxAppid); + } + + @Override + public String getAppid() { + return this.wxAppid; + } + + @Override + public void setAppid(String appid) { + this.wxAppid = appid; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java index f7082fc3da..4a8bae4f57 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java @@ -1,24 +1,40 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; + +import java.util.Map; /** - * 发送红包请求参数对象 + * 发送红包请求参数对象. * Created by Binary Wang on 2016/9/24. * - * @author binarywang (https://github.com/binarywang) + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPaySendRedpackRequest extends WxPayBaseRequest { +public class WxPaySendRedpackRequest extends BaseWxPayRequest { + private static final long serialVersionUID = -2035425086824987567L; + + @Override + protected String[] getIgnoredParamsForSign() { + return new String[]{"sign_type", "sub_appid"}; + } + /** - * mch_billno - * 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 + * mch_billno. + * 商户订单号(每个订单号必须唯一) + * 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 */ @XStreamAlias("mch_billno") private String mchBillNo; /** - * send_name + * send_name. * 商户名称 * 红包发送者名称 */ @@ -26,14 +42,14 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest { private String sendName; /** - * re_openid + * re_openid. * 接受红包的用户 用户在wxappid下的openid */ @XStreamAlias("re_openid") private String reOpenid; /** - * total_amount + * total_amount. * 红包总额 */ @XStreamAlias("total_amount") @@ -47,7 +63,7 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest { private Integer totalNum; /** - * amt_type + * amt_type. * 红包金额设置方式 * ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额 * 裂变红包必填 @@ -56,14 +72,14 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest { private String amtType; /** - * wishing + * wishing. * 红包祝福语 */ @XStreamAlias("wishing") private String wishing; /** - * client_ip + * client_ip. * 服务器Ip地址 * 调用接口的机器Ip地址 */ @@ -71,29 +87,44 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest { private String clientIp; /** - * act_name + * act_name. * 活动名称 */ @XStreamAlias("act_name") private String actName; /** - * remark + * remark. * 备注 */ @XStreamAlias("remark") private String remark; /** - * wxappid - * 微信分配的公众账号ID(企业号corpid即为此appId)。接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的) + * wxappid. + * 微信分配的公众账号ID(企业号corpid即为此appId)。 + * 接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的), + * 不能为APP的appid(在open.weixin.qq.com申请的) */ @XStreamAlias("wxappid") private String wxAppid; + /** + * 触达用户appid. + *
    +   * msgappid
    +   * wx28b16568a629bb33
    +   * String(32)
    +   * 服务商模式下触达用户时的appid(可填服务商自己的appid或子商户的appid),
    +   * 服务商模式下必填,服务商模式下填入的子商户appid必须在微信支付商户平台中先录入,否则会校验不过。
    +   * 
    + */ + @XStreamAlias("msgappid") + private String msgAppid; + /** *
    -   * scene_id
    +   * scene_id.
        * 场景id
        * PRODUCT_1:商品促销
        * PRODUCT_2:抽奖
    @@ -111,7 +142,7 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest {
     
       /**
        * 
    -   * risk_info
    +   * risk_info.
        * 活动信息
        * posttime:用户操作的时间戳
        * mobile:业务系统账号的手机号,国家代码-手机号。不需要+号
    @@ -127,7 +158,7 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest {
     
       /**
        * 
    -   * consume_mch_id
    +   * consume_mch_id.
        * 资金授权商户号
        * 资金授权商户号
        * 服务商替特约商户发放时使用
    @@ -137,86 +168,6 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest {
       @XStreamAlias("consume_mch_id")
       private String consumeMchId;
     
    -  public String getMchBillNo() {
    -    return mchBillNo;
    -  }
    -
    -  public void setMchBillNo(String mchBillNo) {
    -    this.mchBillNo = mchBillNo;
    -  }
    -
    -  public String getSendName() {
    -    return this.sendName;
    -  }
    -
    -  public void setSendName(String sendName) {
    -    this.sendName = sendName;
    -  }
    -
    -  public String getReOpenid() {
    -    return this.reOpenid;
    -  }
    -
    -  public void setReOpenid(String reOpenid) {
    -    this.reOpenid = reOpenid;
    -  }
    -
    -  public Integer getTotalAmount() {
    -    return this.totalAmount;
    -  }
    -
    -  public void setTotalAmount(Integer totalAmount) {
    -    this.totalAmount = totalAmount;
    -  }
    -
    -  public Integer getTotalNum() {
    -    return this.totalNum;
    -  }
    -
    -  public void setTotalNum(Integer totalNum) {
    -    this.totalNum = totalNum;
    -  }
    -
    -  public String getAmtType() {
    -    return this.amtType;
    -  }
    -
    -  public void setAmtType(String amtType) {
    -    this.amtType = amtType;
    -  }
    -
    -  public String getWishing() {
    -    return this.wishing;
    -  }
    -
    -  public void setWishing(String wishing) {
    -    this.wishing = wishing;
    -  }
    -
    -  public String getClientIp() {
    -    return this.clientIp;
    -  }
    -
    -  public void setClientIp(String clientIp) {
    -    this.clientIp = clientIp;
    -  }
    -
    -  public String getActName() {
    -    return this.actName;
    -  }
    -
    -  public void setActName(String actName) {
    -    this.actName = actName;
    -  }
    -
    -  public String getRemark() {
    -    return this.remark;
    -  }
    -
    -  public void setRemark(String remark) {
    -    this.remark = remark;
    -  }
    -
       @Override
       protected void checkConstraints() {
     
    @@ -232,28 +183,18 @@ public void setAppid(String appid) {
         this.wxAppid = appid;
       }
     
    -  public String getSceneId() {
    -    return this.sceneId;
    -  }
    -
    -  public void setSceneId(String sceneId) {
    -    this.sceneId = sceneId;
    -  }
    -
    -  public String getRiskInfo() {
    -    return this.riskInfo;
    -  }
    -
    -  public void setRiskInfo(String riskInfo) {
    -    this.riskInfo = riskInfo;
    -  }
    -
    -  public String getConsumeMchId() {
    -    return this.consumeMchId;
    -  }
    -
    -  public void setConsumeMchId(String consumeMchId) {
    -    this.consumeMchId = consumeMchId;
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("mch_billno", mchBillNo);
    +    map.put("send_name", sendName);
    +    map.put("re_openid", reOpenid);
    +    map.put("total_amount", totalAmount.toString());
    +    map.put("total_num", totalNum.toString());
    +    map.put("amt_type", amtType);
    +    map.put("wishing", wishing);
    +    map.put("client_ip", clientIp);
    +    map.put("act_name", actName);
    +    map.put("remark", remark);
       }
     
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.zip b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.zip
    new file mode 100644
    index 0000000000..b02696333a
    Binary files /dev/null and b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.zip differ
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java
    index 849d928375..8602740bb8 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java
    @@ -1,16 +1,25 @@
     package com.github.binarywang.wxpay.bean.request;
     
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.*;
    +
    +import java.util.Map;
     
     /**
      * 
      * 转换短链接请求对象类
      * Created by Binary Wang on 2017-3-27.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayShorturlRequest extends WxPayBaseRequest { +public class WxPayShorturlRequest extends BaseWxPayRequest { /** *
        * URL链接
    @@ -24,23 +33,13 @@ public class WxPayShorturlRequest extends WxPayBaseRequest {
       @XStreamAlias("long_url")
       private String longUrl;
     
    -  public String getLongUrl() {
    -    return this.longUrl;
    -  }
    -
    -  public void setLongUrl(String longUrl) {
    -    this.longUrl = longUrl;
    -  }
    -
    -  public WxPayShorturlRequest() {
    -  }
    -
    -  public WxPayShorturlRequest(String longUrl) {
    -    this.longUrl = longUrl;
    -  }
    -
       @Override
       protected void checkConstraints() {
         //do nothing
       }
    +
    +  @Override
    +  protected void storeMap(Map map) {
    +    map.put("long_url", longUrl);
    +  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java
    index 48b059d36a..3a80c82787 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java
    @@ -1,42 +1,60 @@
     package com.github.binarywang.wxpay.bean.request;
     
     import com.github.binarywang.wxpay.config.WxPayConfig;
    +import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType;
    +import com.github.binarywang.wxpay.exception.WxPayException;
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import com.thoughtworks.xstream.annotations.XStreamConverter;
    +import lombok.*;
    +import lombok.experimental.Accessors;
     import me.chanjar.weixin.common.annotation.Required;
    -import me.chanjar.weixin.common.exception.WxErrorException;
    -import org.apache.commons.lang3.ArrayUtils;
    +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
     import org.apache.commons.lang3.StringUtils;
     
    -import java.util.Arrays;
    +import java.util.Map;
     
     /**
      * 
    - * 统一下单请求参数对象
    + * 统一下单请求参数对象.
      * 参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
    - * 注释中各行每个字段描述对应如下:
    - * 
  44. 字段名 - *
  45. 变量名 - *
  46. 是否必填 - *
  47. 类型 - *
  48. 示例值 - *
  49. 描述 - *
  50. * Created by Binary Wang on 2016/9/25. + *
    * - * @author binarywang (https://github.com/binarywang) + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor @XStreamAlias("xml") -public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { - private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; +@Accessors(chain = true) +public class WxPayUnifiedOrderRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 4611350167813931828L; /** *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * String(32)
    -   * 013467007045764
    -   * 终端设备号(门店号或收银设备Id),注意:PC网页或公众号内支付请传"WEB"
    +   * 字段名:接口版本号.
    +   * 变量名:version
    +   * 是否必填:单品优惠必填
    +   * 类型:String(32)
    +   * 示例值:1.0
    +   * 描述:单品优惠新增字段,接口版本号,区分原接口,默认填写1.0。
    +   * 入参新增version后,则支付通知接口也将返回单品优惠信息字段promotion_detail,请确保支付通知的签名验证能通过。
    +   * 更多信息,详见文档:https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_102&index=2
    +   * 
    + */ + @XStreamAlias("version") + private String version; + + /** + *
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:013467007045764
    +   * 描述:终端设备号(门店号或收银设备Id),注意:PC网页或公众号内支付请传"WEB"
        * 
    */ @XStreamAlias("device_info") @@ -44,12 +62,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品描述
    -   * body
    -   * 是
    -   * String(128)
    -   * 腾讯充值中心-QQ会员充值
    -   * 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
    +   * 字段名:商品描述.
    +   * 变量名:body
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值: 腾讯充值中心-QQ会员充值
    +   * 描述:商品简单描述,该字段须严格按照规范传递,具体请见参数规定
        * 
    */ @Required @@ -58,12 +76,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品详情
    -   * detail
    -   * 否
    -   * String(6000)
    -   *  {  "goods_detail":[
    -   * {
    +   * 字段名:商品详情.
    +   * 变量名:detail
    +   * 是否必填:否
    +   * 类型:String(6000)
    +   * 示例值: {  "goods_detail":[
    +   *  {
        * "goods_id":"iphone6s_16G",
        * "wxpay_goods_id":"1001",
        * "goods_name":"iPhone6s 16G",
    @@ -83,7 +101,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest {
        * }
        * ]
        * }
    -   * 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。
    +   * 描述:商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。
        * goods_detail []:
        * └ goods_id String 必填 32 商品的编号
        * └ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号
    @@ -95,29 +113,31 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest {
        * 
    */ @XStreamAlias("detail") + @XStreamConverter(value = XStreamCDataConverter.class) private String detail; /** *
    -   * 附加数据
    -   * attach
    -   * 否
    -   * String(127)
    -   * 深圳分店
    -   *  附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    +   * 字段名:附加数据.
    +   * 变量名:attach
    +   * 是否必填:否
    +   * 类型:String(127)
    +   * 示例值: 深圳分店
    +   * 描述:  附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
        * 
    */ @XStreamAlias("attach") + @XStreamConverter(value = XStreamCDataConverter.class) private String attach; /** *
    -   * 商户订单号
    -   * out_trade_no
    -   * 是
    -   * String(32)
    -   * 20150806125346
    -   * 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
    +   * 字段名:商户订单号.
    +   * 变量名:out_trade_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:20150806125346
    +   * 描述:商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
        * 
    */ @Required @@ -126,12 +146,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 货币类型
    -   * fee_type
    -   * 否
    -   * String(16)
    -   * CNY
    -   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 字段名:货币类型.
    +   * 变量名:fee_type
    +   * 是否必填:否
    +   * 类型:String(16)
    +   * 示例值:CNY
    +   * 描述: 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
        * 
    */ @XStreamAlias("fee_type") @@ -139,12 +159,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 总金额
    -   * total_fee
    -   * 是
    -   * Int
    -   * 888
    -   * 订单总金额,单位为分,详见支付金额
    +   * 字段名:总金额.
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值: 888
    +   * 描述:订单总金额,单位为分,详见支付金额
        * 
    */ @Required @@ -153,12 +173,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 终端IP
    -   * spbill_create_ip
    -   * 是
    -   * String(16)
    -   * 123.12.12.123
    -   * APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
    +   * 字段名:终端IP.
    +   * 变量名:spbill_create_ip
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:123.12.12.123
    +   * 描述:APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
        * 
    */ @Required @@ -167,12 +187,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 交易起始时间
    -   * time_start
    -   * 否
    -   * String(14)
    -   * 20091225091010
    -   * 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    +   * 字段名:交易起始时间.
    +   * 变量名:time_start
    +   * 是否必填:否
    +   * 类型:String(14)
    +   * 示例值:20091225091010
    +   * 描述:订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
        * 
    */ @XStreamAlias("time_start") @@ -180,12 +200,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 交易结束时间
    -   * time_expire
    -   * 否
    -   * String(14)
    -   * 20091227091010
    -   * 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
    +   * 字段名:交易结束时间.
    +   * 变量名:time_expire
    +   * 是否必填:否
    +   * 类型:String(14)
    +   * 示例值:20091227091010
    +   * 描述:订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
        *   注意:最短失效时间间隔必须大于5分钟
        * 
    */ @@ -194,12 +214,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品标记
    -   * goods_tag
    -   * 否
    -   * String(32)
    -   * WXG
    -   * 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    +   * 字段名:商品标记.
    +   * 变量名:goods_tag
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:WXG
    +   * 描述:商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
        * 
    */ @XStreamAlias("goods_tag") @@ -207,26 +227,26 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 通知地址
    -   * notify_url
    -   * 是
    -   * String(256)
    -   * http://www.weixin.qq.com/wxpay/pay.php
    -   * 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
    +   * 字段名:通知地址.
    +   * 变量名:notify_url
    +   * 是否必填:是
    +   * 类型:String(256)
    +   * 示例值:http://www.weixin.qq.com/wxpay/pay.php
    +   * 描述:接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
        * 
    */ @Required @XStreamAlias("notify_url") - private String notifyURL; + private String notifyUrl; /** *
    -   * 交易类型
    -   * trade_type
    -   * 是
    -   * String(16)
    -   * JSAPI
    -   * 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定:
    +   * 字段名:交易类型.
    +   * 变量名:trade_type
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值: JSAPI
    +   * 描述: 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定:
        * JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
        * 
    */ @@ -236,12 +256,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品Id
    -   * product_id
    -   * 否
    -   * String(32)
    -   * 12235413214070356458058
    -   * trade_type=NATIVE,此参数必传。此id为二维码中包含的商品Id,商户自行定义。
    +   * 字段名:商品Id.
    +   * 变量名:product_id
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:12235413214070356458058
    +   * 描述:trade_type=NATIVE,此参数必传。此id为二维码中包含的商品Id,商户自行定义。
        * 
    */ @XStreamAlias("product_id") @@ -249,11 +269,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 指定支付方式
    -   * limit_pay
    -   * 否
    -   * String(32)
    -   * no_credit no_credit--指定不能使用信用卡支付
    +   * 字段名:指定支付方式.
    +   * 变量名:limit_pay
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:no_credit
    +   * 描述:no_credit--指定不能使用信用卡支付
        * 
    */ @XStreamAlias("limit_pay") @@ -261,12 +282,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 用户标识
    -   * openid
    -   * 否
    -   * String(128)
    -   * oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    -   * trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
    +   * 字段名:用户标识.
    +   * 变量名:openid
    +   * 是否必填:否
    +   * 类型:String(128)
    +   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    +   * 描述:trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
        * openid如何获取,可参考【获取openid】。
        * 企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换
        * 
    @@ -274,117 +295,86 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { @XStreamAlias("openid") private String openid; - public static WxUnifiedOrderRequestBuilder builder() { - return new WxUnifiedOrderRequestBuilder(); - } - - public String getDeviceInfo() { - return this.deviceInfo; - } - - public void setDeviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - } - - public String getBody() { - return this.body; - } - - public void setBody(String body) { - this.body = body; - } - - public String getDetail() { - return this.detail; - } - - public void setDetail(String detail) { - this.detail = detail; - } - - public String getAttach() { - return this.attach; - } - - public void setAttach(String attach) { - this.attach = attach; - } - - public String getOutTradeNo() { - return this.outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public String getFeeType() { - return this.feeType; - } - - public void setFeeType(String feeType) { - this.feeType = feeType; - } - - public Integer getTotalFee() { - return this.totalFee; - } - - public void setTotalFee(Integer totalFee) { - this.totalFee = totalFee; - } - - public String getSpbillCreateIp() { - return this.spbillCreateIp; - } - - public void setSpbillCreateIp(String spbillCreateIp) { - this.spbillCreateIp = spbillCreateIp; - } - - public String getTimeStart() { - return this.timeStart; - } - - public void setTimeStart(String timeStart) { - this.timeStart = timeStart; - } - - public String getTimeExpire() { - return this.timeExpire; - } - - public void setTimeExpire(String timeExpire) { - this.timeExpire = timeExpire; - } - - public String getGoodsTag() { - return this.goodsTag; - } + /** + *
    +   * 字段名:用户子标识.
    +   * 变量名:sub_openid
    +   * 是否必填:否
    +   * 类型:String(128)
    +   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    +   * 描述:trade_type=JSAPI,此参数必传,用户在子商户appid下的唯一标识。
    +   * openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
    +   * 下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid。
    +   * 
    + */ + @XStreamAlias("sub_openid") + private String subOpenid; - public void setGoodsTag(String goodsTag) { - this.goodsTag = goodsTag; - } + /** + *
    +   * 字段名:电子发票入口开放标识.
    +   * 变量名:	receipt
    +   * 是否必填:否
    +   * 类型:String(8)
    +   * 示例值:Y
    +   * 描述:Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效
    +   * 
    + */ + @XStreamAlias("receipt") + private String receipt; - public String getNotifyURL() { - return this.notifyURL; - } + /** + *
    +   * 字段名:场景信息.
    +   * 变量名:scene_info
    +   * 是否必填:否,对H5支付来说是必填
    +   * 类型:String(256)
    +   * 示例值:{
    +   * "store_id": "SZT10000",
    +   * "store_name":"腾讯大厦腾大餐厅"
    +   * }
    +   * 描述:该字段用于统一下单时上报场景信息,目前支持上报实际门店信息。
    +   * {
    +   * "store_id": "", //门店唯一标识,选填,String(32)
    +   * "store_name":"”//门店名称,选填,String(64)
    +   * }
    +   * 
    + */ + @XStreamAlias("scene_info") + private String sceneInfo; + /** + *
    +   * 字段名:浏览器指纹.
    +   * 变量名:fingerprint
    +   * 是否必填:否
    +   * 详细参考 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_7&index=6
    +   * 
    + */ + @XStreamAlias("fingerprint") + private String fingerprint; + /** + *
    +   * 字段名:是否指定服务商分账.
    +   * 变量名:profit_sharing
    +   * 是否必填:否
    +   * 详情:Y-是,需要分账  N-否,不分账,字母要求大写,不传默认不分账
    +   * 详细参考 https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=24_3&index=3
    +   * 
    + */ + @XStreamAlias("profit_sharing") + private String profitSharing; /** - * 如果配置中已经设置,可以不设置值 + * 如果配置中已经设置,可以不设置值. * - * @param notifyURL + * @param notifyUrl 支付回调通知地址 */ - public void setNotifyURL(String notifyURL) { - this.notifyURL = notifyURL; - } - - public String getTradeType() { - return this.tradeType; + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; } /** - * 如果配置中已经设置,可以不设置值 + * 如果配置中已经设置,可以不设置值. * * @param tradeType 交易类型 */ @@ -392,50 +382,17 @@ public void setTradeType(String tradeType) { this.tradeType = tradeType; } - public String getProductId() { - return this.productId; - } - - public void setProductId(String productId) { - this.productId = productId; - } - - public String getLimitPay() { - return this.limitPay; - } - - public void setLimitPay(String limitPay) { - this.limitPay = limitPay; - } - - public String getOpenid() { - return this.openid; - } - - public void setOpenid(String openid) { - this.openid = openid; - } - @Override - protected void checkConstraints() { - if (!ArrayUtils.contains(TRADE_TYPES, this.getTradeType())) { - throw new IllegalArgumentException(String.format("trade_type目前必须为%s其中之一,实际值:%s", - Arrays.toString(TRADE_TYPES), this.getTradeType())); - } - - if ("JSAPI".equals(this.getTradeType()) && this.getOpenid() == null) { - throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid"); - } - - if ("NATIVE".equals(this.getTradeType()) && this.getProductId() == null) { - throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id"); + protected void checkConstraints() throws WxPayException { + if (TradeType.NATIVE.equals(this.getTradeType()) && StringUtils.isBlank(this.getProductId())) { + throw new WxPayException("当trade_type是'NATIVE'时,需指定非空的product_id值"); } } @Override - public void checkAndSign(WxPayConfig config) throws WxErrorException { - if (StringUtils.isBlank(this.getNotifyURL())) { - this.setNotifyURL(config.getNotifyUrl()); + public void checkAndSign(WxPayConfig config) throws WxPayException { + if (StringUtils.isBlank(this.getNotifyUrl())) { + this.setNotifyUrl(config.getNotifyUrl()); } if (StringUtils.isBlank(this.getTradeType())) { @@ -445,176 +402,30 @@ public void checkAndSign(WxPayConfig config) throws WxErrorException { super.checkAndSign(config); } - public static class WxUnifiedOrderRequestBuilder { - private String appid; - private String mchId; - private String deviceInfo; - private String nonceStr; - private String sign; - private String body; - private String detail; - private String attach; - private String outTradeNo; - private String feeType; - private Integer totalFee; - private String spbillCreateIp; - private String timeStart; - private String timeExpire; - private String goodsTag; - private String notifyURL; - private String tradeType; - private String productId; - private String limitPay; - private String openid; - - public WxUnifiedOrderRequestBuilder appid(String appid) { - this.appid = appid; - return this; - } - - public WxUnifiedOrderRequestBuilder mchId(String mchId) { - this.mchId = mchId; - return this; - } - - public WxUnifiedOrderRequestBuilder deviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - return this; - } - - public WxUnifiedOrderRequestBuilder nonceStr(String nonceStr) { - this.nonceStr = nonceStr; - return this; - } - - public WxUnifiedOrderRequestBuilder sign(String sign) { - this.sign = sign; - return this; - } - - public WxUnifiedOrderRequestBuilder body(String body) { - this.body = body; - return this; - } - - public WxUnifiedOrderRequestBuilder detail(String detail) { - this.detail = detail; - return this; - } - - public WxUnifiedOrderRequestBuilder attach(String attach) { - this.attach = attach; - return this; - } - - public WxUnifiedOrderRequestBuilder outTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - return this; - } - - public WxUnifiedOrderRequestBuilder feeType(String feeType) { - this.feeType = feeType; - return this; - } - - public WxUnifiedOrderRequestBuilder totalFee(Integer totalFee) { - this.totalFee = totalFee; - return this; - } - - public WxUnifiedOrderRequestBuilder spbillCreateIp(String spbillCreateIp) { - this.spbillCreateIp = spbillCreateIp; - return this; - } - - public WxUnifiedOrderRequestBuilder timeStart(String timeStart) { - this.timeStart = timeStart; - return this; - } - - public WxUnifiedOrderRequestBuilder timeExpire(String timeExpire) { - this.timeExpire = timeExpire; - return this; - } - - public WxUnifiedOrderRequestBuilder goodsTag(String goodsTag) { - this.goodsTag = goodsTag; - return this; - } - - public WxUnifiedOrderRequestBuilder notifyURL(String notifyURL) { - this.notifyURL = notifyURL; - return this; - } - - public WxUnifiedOrderRequestBuilder tradeType(String tradeType) { - this.tradeType = tradeType; - return this; - } - - public WxUnifiedOrderRequestBuilder productId(String productId) { - this.productId = productId; - return this; - } - - public WxUnifiedOrderRequestBuilder limitPay(String limitPay) { - this.limitPay = limitPay; - return this; - } - - public WxUnifiedOrderRequestBuilder openid(String openid) { - this.openid = openid; - return this; - } - - public WxUnifiedOrderRequestBuilder from(WxPayUnifiedOrderRequest origin) { - this.appid(origin.appid); - this.mchId(origin.mchId); - this.deviceInfo(origin.deviceInfo); - this.nonceStr(origin.nonceStr); - this.sign(origin.sign); - this.body(origin.body); - this.detail(origin.detail); - this.attach(origin.attach); - this.outTradeNo(origin.outTradeNo); - this.feeType(origin.feeType); - this.totalFee(origin.totalFee); - this.spbillCreateIp(origin.spbillCreateIp); - this.timeStart(origin.timeStart); - this.timeExpire(origin.timeExpire); - this.goodsTag(origin.goodsTag); - this.notifyURL(origin.notifyURL); - this.tradeType(origin.tradeType); - this.productId(origin.productId); - this.limitPay(origin.limitPay); - this.openid(origin.openid); - return this; - } - - public WxPayUnifiedOrderRequest build() { - WxPayUnifiedOrderRequest m = new WxPayUnifiedOrderRequest(); - m.appid = this.appid; - m.mchId = this.mchId; - m.deviceInfo = this.deviceInfo; - m.nonceStr = this.nonceStr; - m.sign = this.sign; - m.body = this.body; - m.detail = this.detail; - m.attach = this.attach; - m.outTradeNo = this.outTradeNo; - m.feeType = this.feeType; - m.totalFee = this.totalFee; - m.spbillCreateIp = this.spbillCreateIp; - m.timeStart = this.timeStart; - m.timeExpire = this.timeExpire; - m.goodsTag = this.goodsTag; - m.notifyURL = this.notifyURL; - m.tradeType = this.tradeType; - m.productId = this.productId; - m.limitPay = this.limitPay; - m.openid = this.openid; - return m; - } + @Override + protected void storeMap(Map map) { + map.put("version", version); + map.put("device_info", deviceInfo); + map.put("body", body); + map.put("detail", detail); + map.put("attach", attach); + map.put("out_trade_no", outTradeNo); + map.put("fee_type", feeType); + map.put("total_fee", totalFee.toString()); + map.put("spbill_create_ip", spbillCreateIp); + map.put("time_start", timeStart); + map.put("time_expire", timeExpire); + map.put("goods_tag", goodsTag); + map.put("notify_url", notifyUrl); + map.put("trade_type", tradeType); + map.put("product_id", productId); + map.put("limit_pay", limitPay); + map.put("openid", openid); + map.put("sub_openid", subOpenid); + map.put("receipt", receipt); + map.put("scene_info", sceneInfo); + map.put("fingerprint", fingerprint); + map.put("profit_sharing", profitSharing); } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java new file mode 100644 index 0000000000..51865664f4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java @@ -0,0 +1,367 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.util.SignUtils; +import com.github.binarywang.wxpay.util.XmlConfig; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.ByteArrayInputStream; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +/** + *
    + * 微信支付结果共用属性类.
    + * Created by Binary Wang on 2016-10-24.
    + * 
    + * + * @author Binary Wang + */ +@Data +public abstract class BaseWxPayResult { + /** + * 返回状态码. + */ + @XStreamAlias("return_code") + protected String returnCode; + /** + * 返回信息. + */ + @XStreamAlias("return_msg") + protected String returnMsg; + + //当return_code为SUCCESS的时候,还会包括以下字段: + + /** + * 业务结果. + */ + @XStreamAlias("result_code") + private String resultCode; + /** + * 错误代码. + */ + @XStreamAlias("err_code") + private String errCode; + /** + * 错误代码描述. + */ + @XStreamAlias("err_code_des") + private String errCodeDes; + /** + * 公众账号ID. + */ + @XStreamAlias("appid") + private String appid; + /** + * 商户号. + */ + @XStreamAlias("mch_id") + private String mchId; + /** + * 服务商模式下的子公众账号ID. + */ + @XStreamAlias("sub_appid") + private String subAppId; + /** + * 服务商模式下的子商户号. + */ + @XStreamAlias("sub_mch_id") + private String subMchId; + /** + * 随机字符串. + */ + @XStreamAlias("nonce_str") + private String nonceStr; + /** + * 签名. + */ + @XStreamAlias("sign") + private String sign; + + //以下为辅助属性 + /** + * xml字符串. + */ + private String xmlString; + + /** + * xml的Document对象,用于解析xml文本. + * make xmlDoc transient to ensure toString() can work. + */ + private transient Document xmlDoc; + + /** + * 将单位分转换成单位圆. + * + * @param fen 将要被转换为元的分的数值 + * @return the string + */ + public static String fenToYuan(Integer fen) { + return BigDecimal.valueOf(Double.valueOf(fen) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + + /** + * 从xml字符串创建bean对象. + * + * @param the type parameter + * @param xmlString the xml string + * @param clz the clz + * @return the t + */ + public static T fromXML(String xmlString, Class clz) { + if (XmlConfig.fastMode) { + try { + BaseWxPayResult t = clz.newInstance(); + t.setXmlString(xmlString); + Document doc = t.getXmlDoc(); + t.loadBasicXML(doc); + t.loadXml(doc); + return (T) t; + } catch (Exception e) { + throw new RuntimeException("parse xml error", e); + } + } + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(clz); + T result = (T) xstream.fromXML(xmlString); + result.setXmlString(xmlString); + return result; + } + + /** + * 从XML文档中加载属性,供子类覆盖加载额外的属性 + * + * @param d Document + */ + protected abstract void loadXml(Document d); + + /** + * 从XML文档中加载基础属性 + * + * @param d Document + */ + private void loadBasicXML(Document d) { + returnCode = readXmlString(d, "return_code"); + returnMsg = readXmlString(d, "return_msg"); + resultCode = readXmlString(d, "result_code"); + errCode = readXmlString(d, "err_code"); + errCodeDes = readXmlString(d, "err_code_des"); + appid = readXmlString(d, "appid"); + mchId = readXmlString(d, "mch_id"); + subAppId = readXmlString(d, "sub_appid"); + subMchId = readXmlString(d, "sub_mch_id"); + nonceStr = readXmlString(d, "nonce_str"); + sign = readXmlString(d, "sign"); + } + + protected static Integer readXmlInteger(Node d, String tagName) { + String content = readXmlString(d, tagName); + if (content == null || content.trim().length() == 0) { + return null; + } + return Integer.parseInt(content); + } + + protected static String readXmlString(Node d, String tagName) { + if (!d.hasChildNodes()) { + return null; + } + NodeList childNodes = d.getChildNodes(); + for (int i = 0, j = childNodes.getLength(); i < j; i++) { + Node node = childNodes.item(i); + if (tagName.equals(node.getNodeName())) { + if (!node.hasChildNodes()) { + return null; + } + return node.getFirstChild().getNodeValue(); + } + } + return null; + } + + public static String readXmlString(Document d, String tagName) { + NodeList elements = d.getElementsByTagName(tagName); + if (elements == null || elements.getLength() == 0) { + return null; + } + + Node node = elements.item(0).getFirstChild(); + if (node == null) { + return null; + } + return node.getNodeValue(); + } + + protected static Integer readXmlInteger(Document d, String tagName) { + String content = readXmlString(d, tagName); + if (content == null || content.trim().length() == 0) { + return null; + } + + return Integer.parseInt(content); + } + + /** + * Gets logger. + * + * @return the logger + */ + protected Logger getLogger() { + return LoggerFactory.getLogger(this.getClass()); + } + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + * 将bean通过保存的xml字符串转换成map. + * + * @return the map + */ + public Map toMap() { + if (StringUtils.isBlank(this.xmlString)) { + throw new RuntimeException("xml数据有问题,请核实!"); + } + + Map result = Maps.newHashMap(); + Document doc = this.getXmlDoc(); + + try { + NodeList list = (NodeList) XPathFactory.newInstance().newXPath() + .compile("/xml/*") + .evaluate(doc, XPathConstants.NODESET); + int len = list.getLength(); + for (int i = 0; i < len; i++) { + result.put(list.item(i).getNodeName(), list.item(i).getTextContent()); + } + } catch (XPathExpressionException e) { + throw new RuntimeException("非法的xml文本内容:" + xmlString); + } + + return result; + } + + /** + * 将xml字符串转换成Document对象,以便读取其元素值. + */ + private Document getXmlDoc() { + if (this.xmlDoc != null) { + return this.xmlDoc; + } + xmlDoc = openXML(xmlString); + return xmlDoc; + } + + protected Document openXML(String content) { + try { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setExpandEntityReferences(false); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + return factory.newDocumentBuilder().parse(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); + } catch (Exception e) { + throw new RuntimeException("非法的xml文本内容:\n" + this.xmlString, e); + } + } + + /** + * 获取xml中元素的值. + * + * @param path the path + * @return the xml value + */ + protected String getXmlValue(String... path) { + Document doc = this.getXmlDoc(); + String expression = String.format("/%s//text()", Joiner.on("/").join(path)); + try { + return (String) XPathFactory + .newInstance() + .newXPath() + .compile(expression) + .evaluate(doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new RuntimeException("未找到相应路径的文本:" + expression); + } + } + + /** + * 获取xml中元素的值,作为int值返回. + * + * @param path the path + * @return the xml value as int + */ + protected Integer getXmlValueAsInt(String... path) { + String result = this.getXmlValue(path); + if (StringUtils.isBlank(result)) { + return null; + } + + return Integer.valueOf(result); + } + + /** + * 校验返回结果签名. + * + * @param wxPayService the wx pay service + * @param signType 签名类型 + * @param checkSuccess 是否同时检查结果是否成功 + * @throws WxPayException the wx pay exception + */ + public void checkResult(WxPayService wxPayService, String signType, boolean checkSuccess) throws WxPayException { + //校验返回结果签名 + Map map = toMap(); + if (getSign() != null && !SignUtils.checkSign(map, signType, wxPayService.getConfig().getMchKey())) { + this.getLogger().debug("校验结果签名失败,参数:{}", map); + throw new WxPayException("参数格式校验错误!"); + } + + //校验结果是否成功 + if (checkSuccess) { + List successStrings = Lists.newArrayList(WxPayConstants.ResultCode.SUCCESS, ""); + if (!successStrings.contains(StringUtils.trimToEmpty(getReturnCode()).toUpperCase()) + || !successStrings.contains(StringUtils.trimToEmpty(getResultCode()).toUpperCase())) { + StringBuilder errorMsg = new StringBuilder(); + if (getReturnCode() != null) { + errorMsg.append("返回代码:").append(getReturnCode()); + } + if (getReturnMsg() != null) { + errorMsg.append(",返回信息:").append(getReturnMsg()); + } + if (getResultCode() != null) { + errorMsg.append(",结果代码:").append(getResultCode()); + } + if (getErrCode() != null) { + errorMsg.append(",错误代码:").append(getErrCode()); + } + if (getErrCodeDes() != null) { + errorMsg.append(",错误详情:").append(getErrCodeDes()); + } + + this.getLogger().error("\n结果业务代码异常,返回结果:{},\n{}", map, errorMsg.toString()); + throw WxPayException.from(this); + } + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java deleted file mode 100644 index dac69e17c4..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.github.binarywang.wxpay.bean.result; - -import com.thoughtworks.xstream.annotations.XStreamAlias; - -/** - * 企业付款查询返回结果 - * Created by Binary Wang on 2016/10/19. - * - * @author binarywang (https://github.com/binarywang) - */ -@XStreamAlias("xml") -public class WxEntPayQueryResult extends WxPayBaseResult { - - /** - * 商户订单号 - */ - @XStreamAlias("partner_trade_no") - private String partnerTradeNo; - - /** - * 付款单号 - */ - @XStreamAlias("detail_id") - private String detailId; - - /** - * 转账状态 - */ - @XStreamAlias("status") - private String status; - - /** - * 失败原因 - */ - @XStreamAlias("reason") - private String reason; - - /** - * 收款用户openid - */ - @XStreamAlias("openid") - private String openid; - - /** - * 收款用户姓名 - */ - @XStreamAlias("transfer_name") - private String transferName; - - /** - * 付款金额 - */ - @XStreamAlias("payment_amount") - private Integer paymentAmount; - - /** - * 转账时间 - */ - @XStreamAlias("transfer_time") - private String transferTime; - - /** - * 付款描述 - */ - @XStreamAlias("desc") - private String desc; - - public String getPartnerTradeNo() { - return this.partnerTradeNo; - } - - public void setPartnerTradeNo(String partnerTradeNo) { - this.partnerTradeNo = partnerTradeNo; - } - - public String getDetailId() { - return this.detailId; - } - - public void setDetailId(String detailId) { - this.detailId = detailId; - } - - public String getStatus() { - return this.status; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getReason() { - return this.reason; - } - - public void setReason(String reason) { - this.reason = reason; - } - - public String getOpenid() { - return this.openid; - } - - public void setOpenid(String openid) { - this.openid = openid; - } - - public String getTransferName() { - return this.transferName; - } - - public void setTransferName(String transferName) { - this.transferName = transferName; - } - - public Integer getPaymentAmount() { - return this.paymentAmount; - } - - public void setPaymentAmount(Integer paymentAmount) { - this.paymentAmount = paymentAmount; - } - - public String getTransferTime() { - return this.transferTime; - } - - public void setTransferTime(String transferTime) { - this.transferTime = transferTime; - } - - public String getDesc() { - return this.desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java deleted file mode 100644 index fa0a4e5fdc..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.github.binarywang.wxpay.bean.result; - -import com.thoughtworks.xstream.annotations.XStreamAlias; - -/** - * 企业付款返回结果 - * Created by Binary Wang on 2016/10/02. - * - * @author binarywang (https://github.com/binarywang) - */ -@XStreamAlias("xml") -public class WxEntPayResult extends WxPayBaseResult { - - /** - * 商户appid - */ - @XStreamAlias("mch_appid") - private String mchAppid; - - /** - * 设备号 - */ - @XStreamAlias("device_info") - private String deviceInfo; - - //############以下字段在return_code 和result_code都为SUCCESS的时候有返回############## - /** - * 商户订单号 - */ - @XStreamAlias("partner_trade_no") - private String partnerTradeNo; - - /** - * 微信订单号 - */ - @XStreamAlias("payment_no") - private String paymentNo; - - /** - * 微信支付成功时间 - */ - @XStreamAlias("payment_time") - private String paymentTime; - - public String getMchAppid() { - return this.mchAppid; - } - - public void setMchAppid(String mchAppid) { - this.mchAppid = mchAppid; - } - - public String getDeviceInfo() { - return this.deviceInfo; - } - - public void setDeviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - } - - public String getPartnerTradeNo() { - return this.partnerTradeNo; - } - - public void setPartnerTradeNo(String partnerTradeNo) { - this.partnerTradeNo = partnerTradeNo; - } - - public String getPaymentNo() { - return this.paymentNo; - } - - public void setPaymentNo(String paymentNo) { - this.paymentNo = paymentNo; - } - - public String getPaymentTime() { - return this.paymentTime; - } - - public void setPaymentTime(String paymentTime) { - this.paymentTime = paymentTime; - } -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java index f56c61a77f..eea7d8af46 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java @@ -1,19 +1,30 @@ package com.github.binarywang.wxpay.bean.result; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; /** *
      *  授权码查询openid接口请求结果类
      * Created by Binary Wang on 2017-3-27.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayAuthcode2OpenidResult extends WxPayBaseResult { +public class WxPayAuthcode2OpenidResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -2409408725777108398L; /** *
    -   *   用户标识
    +   *   用户标识.
        *   openid
        *   是
        *   String(128)
    @@ -23,18 +34,14 @@ public class WxPayAuthcode2OpenidResult extends WxPayBaseResult {
       @XStreamAlias("openid")
       private String openid;
     
    -  public WxPayAuthcode2OpenidResult() {
    -  }
    -
    -  public WxPayAuthcode2OpenidResult(String openid) {
    -    this.openid = openid;
    -  }
    -
    -  public String getOpenid() {
    -    return this.openid;
    +  /**
    +   * 从XML结构中加载额外的熟悉
    +   *
    +   * @param d Document
    +   */
    +  @Override
    +  protected void loadXml(Document d) {
    +    openid = readXmlString(d, "openid");
       }
     
    -  public void setOpenid(String openid) {
    -    this.openid = openid;
    -  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
    deleted file mode 100644
    index 899703c84d..0000000000
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
    +++ /dev/null
    @@ -1,347 +0,0 @@
    -package com.github.binarywang.wxpay.bean.result;
    -
    -import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
    -import com.github.binarywang.wxpay.util.SignUtils;
    -import com.google.common.base.Joiner;
    -import com.google.common.collect.Maps;
    -import com.thoughtworks.xstream.XStream;
    -import com.thoughtworks.xstream.annotations.XStreamAlias;
    -import me.chanjar.weixin.common.bean.result.WxError;
    -import me.chanjar.weixin.common.exception.WxErrorException;
    -import me.chanjar.weixin.common.util.ToStringUtils;
    -import me.chanjar.weixin.common.util.xml.XStreamInitializer;
    -import org.apache.commons.lang3.StringUtils;
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -import org.w3c.dom.Document;
    -import org.w3c.dom.NodeList;
    -import org.xml.sax.SAXException;
    -
    -import javax.xml.parsers.DocumentBuilderFactory;
    -import javax.xml.parsers.ParserConfigurationException;
    -import javax.xml.xpath.XPathConstants;
    -import javax.xml.xpath.XPathExpressionException;
    -import javax.xml.xpath.XPathFactory;
    -import java.io.ByteArrayInputStream;
    -import java.io.IOException;
    -import java.math.BigDecimal;
    -import java.util.Map;
    -
    -/**
    - * 
    - * 微信支付结果共用属性类
    - * Created by Binary Wang on 2016-10-24.
    - * @author binarywang(Binary Wang)
    - * 
    - */ -public abstract class WxPayBaseResult { - /** - * 返回状态码 - */ - @XStreamAlias("return_code") - protected String returnCode; - /** - * 返回信息 - */ - @XStreamAlias("return_msg") - protected String returnMsg; - - //当return_code为SUCCESS的时候,还会包括以下字段: - - /** - * 业务结果 - */ - @XStreamAlias("result_code") - private String resultCode; - /** - * 错误代码 - */ - @XStreamAlias("err_code") - private String errCode; - /** - * 错误代码描述 - */ - @XStreamAlias("err_code_des") - private String errCodeDes; - /** - * 公众账号ID - */ - @XStreamAlias("appid") - private String appid; - /** - * 商户号 - */ - @XStreamAlias("mch_id") - private String mchId; - /** - * 服务商模式下的子公众账号ID - */ - @XStreamAlias("sub_appid") - private String subAppId; - /** - * 服务商模式下的子商户号 - */ - @XStreamAlias("sub_mch_id") - private String subMchId; - /** - * 随机字符串 - */ - @XStreamAlias("nonce_str") - private String nonceStr; - /** - * 签名 - */ - @XStreamAlias("sign") - private String sign; - - //以下为辅助属性 - /** - * xml字符串 - */ - private String xmlString; - - /** - * xml的Document对象,用于解析xml文本 - */ - private Document xmlDoc; - - /** - * 将单位分转换成单位圆 - * - * @param fee 将要被转换为元的分的数值 - */ - public static String feeToYuan(Integer fee) { - return new BigDecimal(Double.valueOf(fee) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); - } - - /** - * 从xml字符串创建bean对象 - */ - public static T fromXML(String xmlString, Class clz) { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(clz); - T result = (T) xstream.fromXML(xmlString); - result.setXmlString(xmlString); - return result; - } - - public String getXmlString() { - return this.xmlString; - } - - public void setXmlString(String xmlString) { - this.xmlString = xmlString; - } - - protected Logger getLogger() { - return LoggerFactory.getLogger(this.getClass()); - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - - public String getReturnCode() { - return this.returnCode; - } - - public void setReturnCode(String returnCode) { - this.returnCode = returnCode; - } - - public String getReturnMsg() { - return this.returnMsg; - } - - public void setReturnMsg(String returnMsg) { - this.returnMsg = returnMsg; - } - - public String getResultCode() { - return this.resultCode; - } - - public void setResultCode(String resultCode) { - this.resultCode = resultCode; - } - - public String getErrCode() { - return this.errCode; - } - - public void setErrCode(String errCode) { - this.errCode = errCode; - } - - public String getErrCodeDes() { - return this.errCodeDes; - } - - public void setErrCodeDes(String errCodeDes) { - this.errCodeDes = errCodeDes; - } - - public String getAppid() { - return this.appid; - } - - public void setAppid(String appid) { - this.appid = appid; - } - - public String getMchId() { - return this.mchId; - } - - public void setMchId(String mchId) { - this.mchId = mchId; - } - - public String getNonceStr() { - return this.nonceStr; - } - - public void setNonceStr(String nonceStr) { - this.nonceStr = nonceStr; - } - - public String getSign() { - return this.sign; - } - - public void setSign(String sign) { - this.sign = sign; - } - - public String getSubAppId() { - return subAppId; - } - - public void setSubAppId(String subAppId) { - this.subAppId = subAppId; - } - - public String getSubMchId() { - return subMchId; - } - - public void setSubMchId(String subMchId) { - this.subMchId = subMchId; - } - - /** - * 将bean通过保存的xml字符串转换成map - */ - public Map toMap() { - if (StringUtils.isBlank(this.xmlString)) { - throw new RuntimeException("xml数据有问题,请核实!"); - } - - Map result = Maps.newHashMap(); - Document doc = this.getXmlDoc(); - - try { - NodeList list = (NodeList) XPathFactory.newInstance().newXPath() - .compile("/xml/*") - .evaluate(doc, XPathConstants.NODESET); - int len = list.getLength(); - for (int i = 0; i < len; i++) { - result.put(list.item(i).getNodeName(), list.item(i).getTextContent()); - } - } catch (XPathExpressionException e) { - throw new RuntimeException("非法的xml文本内容:" + xmlString); - } - - return result; - } - - /** - * 将xml字符串转换成Document对象,以便读取其元素值 - */ - protected Document getXmlDoc() { - if (this.xmlDoc != null) { - return this.xmlDoc; - } - - try { - this.xmlDoc = DocumentBuilderFactory - .newInstance() - .newDocumentBuilder() - .parse(new ByteArrayInputStream(this.xmlString.getBytes("UTF-8"))); - return xmlDoc; - } catch (SAXException | IOException | ParserConfigurationException e) { - throw new RuntimeException("非法的xml文本内容:" + this.xmlString); - } - - } - - /** - * 获取xml中元素的值 - */ - protected String getXmlValue(String... path) { - Document doc = this.getXmlDoc(); - String expression = String.format("/%s//text()", Joiner.on("/").join(path)); - try { - return (String) XPathFactory - .newInstance() - .newXPath() - .compile(expression) - .evaluate(doc, XPathConstants.STRING); - } catch (XPathExpressionException e) { - throw new RuntimeException("未找到相应路径的文本:" + expression); - } - } - - /** - * 获取xml中元素的值,作为int值返回 - */ - protected Integer getXmlValueAsInt(String... path) { - String result = this.getXmlValue(path); - if (StringUtils.isBlank(result)) { - return null; - } - - return Integer.valueOf(result); - } - - /** - * 校验返回结果签名 - */ - public void checkResult(WxPayServiceImpl wxPayService) throws WxErrorException { - //校验返回结果签名 - Map map = toMap(); - if (getSign() != null && !SignUtils.checkSign(map, wxPayService.getConfig().getMchKey())) { - this.getLogger().debug("校验结果签名失败,参数:{}", map); - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); - } - - //校验结果是否成功 - if (!"SUCCESS".equalsIgnoreCase(getReturnCode()) - || !"SUCCESS".equalsIgnoreCase(getResultCode())) { - StringBuilder errorMsg = new StringBuilder(); - if (getReturnCode() != null) { - errorMsg.append("返回代码:").append(getReturnCode()); - } - if (getReturnMsg() != null) { - errorMsg.append(",返回信息:").append(getReturnMsg()); - } - if (getResultCode() != null) { - errorMsg.append(",结果代码:").append(getResultCode()); - } - if (getErrCode() != null) { - errorMsg.append(",错误代码:").append(getErrCode()); - } - if (getErrCodeDes() != null) { - errorMsg.append(",错误详情:").append(getErrCodeDes()); - } - - WxError error = WxError.newBuilder() - .setErrorCode(-1) - .setErrorMsg(errorMsg.toString()) - .build(); - this.getLogger().error("\n结果业务代码异常,返回結果:{},\n{}", map, error); - throw new WxErrorException(error); - } - } -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillInfo.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillInfo.java new file mode 100644 index 0000000000..49b5b7bf31 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillInfo.java @@ -0,0 +1,142 @@ +package com.github.binarywang.wxpay.bean.result; + +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * 交易时间:2017-04-06 01:00:02 公众账号ID: 商户号: 子商户号:0 设备号:WEB 微信订单号: 商户订单号:2017040519091071873216 用户标识: 交易类型:NATIVE + * 交易状态:REFUND 付款银行:CFT 货币种类:CNY 总金额:0.00 企业红包金额:0.00 微信退款单号: 商户退款单号:20170406010000933 退款金额:0.01 企业红包退款金额:0.00 + * 退款类型:ORIGINAL 退款状态:SUCCESS 商品名称: 商户数据包: 手续费:0.00000 费率 :0.60% + * + * @author BinaryWang + */ +@Data +@NoArgsConstructor +public class WxPayBillInfo implements Serializable { + private static final long serialVersionUID = 2226245109137435453L; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + * 交易时间. + */ + private String tradeTime; + /** + * 公众账号ID. + */ + private String appId; + /** + * 商户号. + */ + private String mchId; + /** + * 特约商户号. + */ + private String subMchId; + /** + * 设备号. + */ + private String deviceInfo; + /** + * 微信订单号. + */ + private String transactionId; + /** + * 商户订单号. + */ + private String outTradeNo; + /** + * 用户标识. + */ + private String openId; + /** + * 交易类型. + */ + private String tradeType; + /** + * 交易状态. + */ + private String tradeState; + /** + * 付款银行. + */ + private String bankType; + /** + * 货币种类. + */ + private String feeType; + /** + * 应结订单金额. + */ + private String totalFee; + /** + * 代金券金额. + */ + private String couponFee; + /** + * 微信退款单号. + */ + private String refundId; + /** + * 商户退款单号. + */ + private String outRefundNo; + /** + * 退款金额. + */ + private String settlementRefundFee; + /** + * 充值券退款金额. + */ + private String couponRefundFee; + /** + * 退款类型. + */ + private String refundChannel; + /** + * 退款状态. + */ + private String refundState; + /** + * 商品名称. + */ + private String body; + /** + * 商户数据包. + */ + private String attach; + /** + * 手续费. + */ + private String poundage; + /** + * 费率. + */ + private String poundageRate; + /** + * 订单金额. + */ + private String totalAmount; + /** + * 申请退款金额. + */ + private String appliedRefundAmount; + /** + * 费率备注. + */ + private String feeRemark; + /** + * 退款申请时间 + */ + private String refundTime; + /** + * 退款成功时间 + */ + private String refundSuccessTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java new file mode 100644 index 0000000000..84a382db3c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java @@ -0,0 +1,399 @@ +package com.github.binarywang.wxpay.bean.result; + +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 微信对账单结果类. + * + * @author DDLeEHi + */ +@Data +@NoArgsConstructor +public class WxPayBillResult implements Serializable { + private static final String TOTAL_DEAL_COUNT = "总交易单数"; + private static final long serialVersionUID = -7687458652694204070L; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + * 对账明细列表. + */ + private List billInfoList; + /** + * 总交易单数. + */ + private String totalRecord; + /** + * 应结订单总金额. + */ + private String totalFee; + /** + * 退款总金额. + */ + private String totalRefundFee; + /** + * 充值券退款总金额. + */ + private String totalCouponFee; + /** + * 手续费总金额. + */ + private String totalPoundageFee; + /** + * 订单总金额. + */ + private String totalAmount; + /** + * 申请退款总金额. + */ + private String totalAppliedRefundFee; + + /** + * 根据账单类型,从原始对账单字符串里构造出WxPayBillResult对象 + * + * @param responseContent 原始对账单字符串 + * @param billType 账单类型 + * @return WxPayBillResult对象 + */ + public static WxPayBillResult fromRawBillResultString(String responseContent, String billType) { + switch (billType) { + case "ALL": { + return fromRawBillResultString(responseContent); + } + case "SUCCESS": { + return fromRawBillResultStringToSuccess(responseContent); + } + case "REFUND": { + return fromRawBillResultStringToRefund(responseContent); + } + case "RECHARGE_REFUND": { + return fromRawBillResultStringToRechargeRefund(responseContent); + } + default: { + return null; + } + } + } + + /** + * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日所有订单信息 + */ + private static WxPayBillResult fromRawBillResultString(String responseContent) { + String listStr = ""; + String objStr = ""; + if (responseContent.contains(TOTAL_DEAL_COUNT)) { + listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT)); + objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT)); + } + + List results = new ArrayList<>(); + // 去空格 + String newStr = listStr.replaceAll(",", " "); + // 数据分组 + String[] tempStr = newStr.split("`"); + // 分组标题 + String[] t = tempStr[0].split(" "); + // 计算循环次数 + int j = tempStr.length / t.length; + // 纪录数组下标 + int k = 1; + // 交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类, + // 应结订单金额,代金券金额,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率, + // 订单金额,申请退款金额,费率备注 (开通免充值券后的结算对账单专有的三个字段) + for (int i = 0; i < j; i++) { + WxPayBillInfo result = new WxPayBillInfo(); + result.setTradeTime(tempStr[k].trim()); + result.setAppId(tempStr[k + 1].trim()); + result.setMchId(tempStr[k + 2].trim()); + result.setSubMchId(tempStr[k + 3].trim()); + result.setDeviceInfo(tempStr[k + 4].trim()); + result.setTransactionId(tempStr[k + 5].trim()); + result.setOutTradeNo(tempStr[k + 6].trim()); + result.setOpenId(tempStr[k + 7].trim()); + result.setTradeType(tempStr[k + 8].trim()); + result.setTradeState(tempStr[k + 9].trim()); + result.setBankType(tempStr[k + 10].trim()); + result.setFeeType(tempStr[k + 11].trim()); + result.setTotalFee(tempStr[k + 12].trim()); + result.setCouponFee(tempStr[k + 13].trim()); + result.setRefundId(tempStr[k + 14].trim()); + result.setOutRefundNo(tempStr[k + 15].trim()); + result.setSettlementRefundFee(tempStr[k + 16].trim()); + result.setCouponRefundFee(tempStr[k + 17].trim()); + result.setRefundChannel(tempStr[k + 18].trim()); + result.setRefundState(tempStr[k + 19].trim()); + result.setBody(tempStr[k + 20].trim()); + result.setAttach(tempStr[k + 21].trim()); + result.setPoundage(tempStr[k + 22].trim()); + result.setPoundageRate(tempStr[k + 23].trim()); + + if (t.length > 24) { + // 开通免充值券后的结算对账单 + result.setTotalAmount(tempStr[k + 24].trim()); + result.setAppliedRefundAmount(tempStr[k + 25].trim()); + result.setFeeRemark(tempStr[k + 26].trim()); + } + + results.add(result); + k += t.length; + } + + WxPayBillResult billResult = new WxPayBillResult(); + billResult.setBillInfoList(results); + + /* + * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `48,`5.76,`1.42,`0.00,`0.01000,`5.76,`1.42 + * 参考以上格式进行取值 + */ + String[] totalTempStr = objStr.replaceAll(",", " ").split("`"); + billResult.setTotalRecord(totalTempStr[1].trim()); + billResult.setTotalFee(totalTempStr[2].trim()); + billResult.setTotalRefundFee(totalTempStr[3].trim()); + billResult.setTotalCouponFee(totalTempStr[4].trim()); + billResult.setTotalPoundageFee(totalTempStr[5].trim()); + billResult.setTotalAmount(get(totalTempStr, 6)); + billResult.setTotalAppliedRefundFee(get(totalTempStr, 7)); + + return billResult; + } + + /** + * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日成功支付的订单 + */ + private static WxPayBillResult fromRawBillResultStringToSuccess(String responseContent) { + String listStr = ""; + String objStr = ""; + if (responseContent.contains(TOTAL_DEAL_COUNT)) { + listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT)); + objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT)); + } + + List results = new ArrayList<>(); + // 去空格 + String newStr = listStr.replaceAll(",", " "); + // 数据分组 + String[] tempStr = newStr.split("`"); + // 分组标题 + String[] t = tempStr[0].split(" "); + // 计算循环次数 + int j = tempStr.length / t.length; + // 纪录数组下标 + int k = 1; + // 交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类, + // 应结订单金额,代金券金额,商品名称,商户数据包,手续费,费率,订单金额,费率备注 + for (int i = 0; i < j; i++) { + WxPayBillInfo result = new WxPayBillInfo(); + result.setTradeTime(tempStr[k].trim()); + result.setAppId(tempStr[k + 1].trim()); + result.setMchId(tempStr[k + 2].trim()); + result.setSubMchId(tempStr[k + 3].trim()); + result.setDeviceInfo(tempStr[k + 4].trim()); + result.setTransactionId(tempStr[k + 5].trim()); + result.setOutTradeNo(tempStr[k + 6].trim()); + result.setOpenId(tempStr[k + 7].trim()); + result.setTradeType(tempStr[k + 8].trim()); + result.setTradeState(tempStr[k + 9].trim()); + result.setBankType(tempStr[k + 10].trim()); + result.setFeeType(tempStr[k + 11].trim()); + result.setTotalFee(tempStr[k + 12].trim()); + result.setCouponFee(tempStr[k + 13].trim()); + result.setBody(tempStr[k + 14].trim()); + result.setAttach(tempStr[k + 15].trim()); + result.setPoundage(tempStr[k + 16].trim()); + result.setPoundageRate(tempStr[k + 17].trim()); + result.setTotalAmount(tempStr[k + 18].trim()); + result.setFeeRemark(tempStr[k + 19].trim()); + results.add(result); + k += t.length; + } + + WxPayBillResult billResult = new WxPayBillResult(); + billResult.setBillInfoList(results); + + /* + * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `31,`3.05,`0.00,`0.00,`0.02000,`3.05,`0.00 + * 参考以上格式进行取值 + */ + String[] totalTempStr = objStr.replaceAll(",", " ").split("`"); + billResult.setTotalRecord(totalTempStr[1].trim()); + billResult.setTotalFee(totalTempStr[2].trim()); + billResult.setTotalRefundFee(totalTempStr[3].trim()); + billResult.setTotalCouponFee(totalTempStr[4].trim()); + billResult.setTotalPoundageFee(totalTempStr[5].trim()); + billResult.setTotalAmount(get(totalTempStr, 6)); + billResult.setTotalAppliedRefundFee(get(totalTempStr, 7)); + return billResult; + } + + /** + * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日退款的订单 + */ + private static WxPayBillResult fromRawBillResultStringToRefund(String responseContent) { + String listStr = ""; + String objStr = ""; + if (responseContent.contains(TOTAL_DEAL_COUNT)) { + listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT)); + objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT)); + } + + List results = new ArrayList<>(); + // 去空格 + String newStr = listStr.replaceAll(",", " "); + // 数据分组 + String[] tempStr = newStr.split("`"); + // 分组标题 + String[] t = tempStr[0].split(" "); + // 计算循环次数 + int j = tempStr.length / t.length; + // 纪录数组下标 + int k = 1; + // 交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,总金额,代金券或立减优惠金额, + // 退款申请时间,退款成功时间,微信退款单号,商户退款单号,退款金额,代金券或立减优惠退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率 + for (int i = 0; i < j; i++) { + WxPayBillInfo result = new WxPayBillInfo(); + result.setTradeTime(tempStr[k].trim()); + result.setAppId(tempStr[k + 1].trim()); + result.setMchId(tempStr[k + 2].trim()); + result.setSubMchId(tempStr[k + 3].trim()); + result.setDeviceInfo(tempStr[k + 4].trim()); + result.setTransactionId(tempStr[k + 5].trim()); + result.setOutTradeNo(tempStr[k + 6].trim()); + result.setOpenId(tempStr[k + 7].trim()); + result.setTradeType(tempStr[k + 8].trim()); + result.setTradeState(tempStr[k + 9].trim()); + result.setBankType(tempStr[k + 10].trim()); + result.setFeeType(tempStr[k + 11].trim()); + result.setTotalFee(tempStr[k + 12].trim()); + result.setCouponFee(tempStr[k + 13].trim()); + result.setRefundTime(tempStr[k + 14].trim()); + result.setRefundSuccessTime(tempStr[k + 15].trim()); + result.setRefundId(tempStr[k + 16].trim()); + result.setOutRefundNo(tempStr[k + 17].trim()); + result.setSettlementRefundFee(tempStr[k + 18].trim()); + result.setCouponRefundFee(tempStr[k + 19].trim()); + result.setRefundChannel(tempStr[k + 20].trim()); + result.setRefundState(tempStr[k + 21].trim()); + result.setBody(tempStr[k + 22].trim()); + result.setAttach(tempStr[k + 23].trim()); + result.setPoundage(tempStr[k + 24].trim()); + result.setPoundageRate(tempStr[k + 25].trim()); + results.add(result); + k += t.length; + } + + WxPayBillResult billResult = new WxPayBillResult(); + billResult.setBillInfoList(results); + + /* + * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `2,`0.02,`0.0,`0.0,`0 + * 参考以上格式进行取值 + */ + String[] totalTempStr = objStr.replaceAll(",", " ").split("`"); + billResult.setTotalRecord(totalTempStr[1].trim()); + billResult.setTotalFee(totalTempStr[2].trim()); + billResult.setTotalRefundFee(totalTempStr[3].trim()); + billResult.setTotalCouponFee(totalTempStr[4].trim()); + billResult.setTotalPoundageFee(totalTempStr[5].trim()); + billResult.setTotalAmount(get(totalTempStr, 6)); + billResult.setTotalAppliedRefundFee(get(totalTempStr, 7)); + + return billResult; + } + + /** + * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日充值退款订单 + */ + private static WxPayBillResult fromRawBillResultStringToRechargeRefund(String responseContent) { + String listStr = ""; + String objStr = ""; + if (responseContent.contains(TOTAL_DEAL_COUNT)) { + listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT)); + objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT)); + } + + List results = new ArrayList<>(); + // 去空格 + String newStr = listStr.replaceAll(",", " "); + // 数据分组 + String[] tempStr = newStr.split("`"); + // 分组标题 + String[] t = tempStr[0].split(" "); + // 计算循环次数 + int j = tempStr.length / t.length; + // 纪录数组下标 + int k = 1; + // 交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额, + // 退款申请时间,退款成功时间,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,返还手续费,费率,订单金额,申请退款金额 + for (int i = 0; i < j; i++) { + WxPayBillInfo result = new WxPayBillInfo(); + result.setTradeTime(tempStr[k].trim()); + result.setAppId(tempStr[k + 1].trim()); + result.setMchId(tempStr[k + 2].trim()); + result.setSubMchId(tempStr[k + 3].trim()); + result.setDeviceInfo(tempStr[k + 4].trim()); + result.setTransactionId(tempStr[k + 5].trim()); + result.setOutTradeNo(tempStr[k + 6].trim()); + result.setOpenId(tempStr[k + 7].trim()); + result.setTradeType(tempStr[k + 8].trim()); + result.setTradeState(tempStr[k + 9].trim()); + result.setBankType(tempStr[k + 10].trim()); + result.setFeeType(tempStr[k + 11].trim()); + result.setTotalFee(tempStr[k + 12].trim()); + result.setCouponFee(tempStr[k + 13].trim()); + result.setRefundTime(tempStr[k + 14].trim()); + result.setRefundSuccessTime(tempStr[k + 15].trim()); + result.setRefundId(tempStr[k + 16].trim()); + result.setOutRefundNo(tempStr[k + 17].trim()); + result.setSettlementRefundFee(tempStr[k + 18].trim()); + result.setCouponRefundFee(tempStr[k + 19].trim()); + result.setRefundChannel(tempStr[k + 20].trim()); + result.setRefundState(tempStr[k + 21].trim()); + result.setBody(tempStr[k + 22].trim()); + result.setAttach(tempStr[k + 23].trim()); + result.setPoundage(tempStr[k + 24].trim()); + result.setPoundageRate(tempStr[k + 25].trim()); + result.setTotalAmount(get(tempStr, k + 26, t.length)); + result.setAppliedRefundAmount(get(tempStr, k + 27, t.length)); + results.add(result); + k += t.length; + } + + WxPayBillResult billResult = new WxPayBillResult(); + billResult.setBillInfoList(results); + + /* + * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `2,`0.02,`0.0,`0.0,`0 + * 参考以上格式进行取值 + */ + String[] totalTempStr = objStr.replaceAll(",", " ").split("`"); + billResult.setTotalRecord(totalTempStr[1].trim()); + billResult.setTotalFee(totalTempStr[2].trim()); + billResult.setTotalRefundFee(totalTempStr[3].trim()); + billResult.setTotalCouponFee(totalTempStr[4].trim()); + billResult.setTotalPoundageFee(totalTempStr[5].trim()); + billResult.setTotalAmount(get(totalTempStr, 6)); + billResult.setTotalAppliedRefundFee(get(totalTempStr, 7)); + + return billResult; + } + + private static String get(String[] array, int idx) { + return get(array, idx, array.length); + } + + private static String get(String[] array, int idx, int length) { + if (length > idx) { + return array[idx].trim(); + } + return null; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java index 1fc4d30940..35e29a9e01 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java @@ -1,15 +1,23 @@ package com.github.binarywang.wxpay.bean.result; import com.thoughtworks.xstream.annotations.XStreamAlias; +import org.w3c.dom.Document; + +import java.io.Serializable; /** *
      * 微信支付结果仅包含有return 和result等相关信息的的属性类
      * Created by Binary Wang on 2017-01-09.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ - @XStreamAlias("xml") -public class WxPayCommonResult extends WxPayBaseResult { +public class WxPayCommonResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -8051324891539367420L; + + @Override + protected void loadXml(Document d) { + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFaceAuthInfoResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFaceAuthInfoResult.java new file mode 100644 index 0000000000..7cdd210d39 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFaceAuthInfoResult.java @@ -0,0 +1,49 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; + +/** + *
    + * 获取微信刷脸调用凭证返回结果.
    + * 详见文档:https://pay.weixin.qq.com/wiki/doc/wxfacepay/develop/sdk-android.html#获取数据-getwxpayfacerawdata
    + * 
    + * + * @author Jmdhappy + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxPayFaceAuthInfoResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -65138145275211272L; + + /** + * SDK调用凭证. + */ + @XStreamAlias("authinfo") + private String authinfo; + + /** + * authinfo的有效时间, 单位秒. + */ + @XStreamAlias("expires_in") + private String expiresIn; + + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + authinfo = readXmlString(d, "authinfo"); + expiresIn = readXmlString(d, "expires_in"); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFacepayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFacepayResult.java new file mode 100644 index 0000000000..5fd6cfe8eb --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFacepayResult.java @@ -0,0 +1,271 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +/** + *
    + * 提交书刷脸支付接口响应结果对象类
    + * Created by Jmdhappy on 2019-09-05.
    + * 
    + * + * @author XxPay + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxPayFacepayResult extends BaseWxPayResult { + private static final long serialVersionUID = -4116580976046716911L; + + /** + *
    +   * 设备号.
    +   * device_info
    +   * 否
    +   * String(32)
    +   * 013467007045764
    +   * 调用接口提交的终端设备号
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 用户标识.
    +   * openid
    +   * 是
    +   * String(128)
    +   * Y
    +   * 用户在商户appid 下的唯一标识
    +   * 
    + **/ + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 是否关注公众账号.
    +   * is_subscribe
    +   * 是
    +   * String(1)
    +   * Y
    +   * 用户是否关注公众账号,仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注
    +   * 
    + **/ + @XStreamAlias("is_subscribe") + private String isSubscribe; + + /** + *
    +   * 用户子标识.
    +   * sub_openid
    +   * 否
    +   * String(128)
    +   * Y
    +   * 子商户appid下用户唯一标识,如需返回则请求时需要传sub_appid
    +   * 
    + **/ + @XStreamAlias("sub_openid") + private String subOpenid; + + /** + *
    +   * 是否关注子公众账号.
    +   * sub_is_subscribe
    +   * 是
    +   * String(1)
    +   * Y
    +   * 用户是否关注子公众账号,仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注
    +   * 
    + **/ + @XStreamAlias("sub_is_subscribe") + private String subsSubscribe; + + /** + *
    +   * 交易类型.
    +   * trade_type
    +   * 是
    +   * String(16)
    +   * FACEPAY
    +   * 支付类型为 FACEPAY(即刷脸支付)
    +   * 
    + **/ + @XStreamAlias("trade_type") + private String tradeType; + + /** + *
    +   * 付款银行.
    +   * bank_type
    +   * 是
    +   * String(32)
    +   * CMC
    +   * 银行类型,采用字符串类型的银行标识,值列表详见银行类型
    +   * 
    + **/ + @XStreamAlias("bank_type") + private String bankType; + + /** + *
    +   * 货币类型.
    +   * fee_type
    +   * 否
    +   * String(16)
    +   * CNY
    +   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + **/ + @XStreamAlias("fee_type") + private String feeType; + + /** + *
    +   * 订单金额.
    +   * total_fee
    +   * 是
    +   * Int
    +   * 888
    +   * 订单总金额,单位为分,只能为整数,详见支付金额
    +   * 
    + **/ + @XStreamAlias("total_fee") + private Integer totalFee; + + /** + *
    +   * 现金支付货币类型.
    +   * cash_fee_type
    +   * 否
    +   * String(16)
    +   * CNY
    +   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + **/ + @XStreamAlias("cash_fee_type") + private String cashFeeType; + + /** + *
    +   * 现金支付金额.
    +   * cash_fee
    +   * 是
    +   * Int
    +   * 100
    +   * 订单现金支付金额,详见支付金额
    +   * 
    + **/ + @XStreamAlias("cash_fee") + private Integer cashFee; + + /** + *
    +   * 微信支付订单号.
    +   * transaction_id
    +   * 是
    +   * String(32)
    +   * 1217752501201407033233368018
    +   * 微信支付订单号
    +   * 
    + **/ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
    +   * 商户订单号.
    +   * out_trade_no
    +   * 是
    +   * String(32)
    +   * 1217752501201407033233368018
    +   * 商户系统的订单号,与请求一致。
    +   * 
    + **/ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
    +   * 商品详情.
    +   * detail
    +   * 否
    +   * String(8192)
    +   * 与提交数据一致
    +   * 实际提交的返回
    +   * 
    + **/ + @XStreamAlias("detail") + private String detail; + + /** + *
    +   * 商家数据包.
    +   * attach
    +   * 否
    +   * String(128)
    +   * 123456
    +   * 商家数据包,原样返回
    +   * 
    + **/ + @XStreamAlias("attach") + private String attach; + + /** + *
    +   * 营销详情.
    +   * promotion_detail
    +   * 否
    +   * String(6000)
    +   * 示例见下文
    +   * 新增返回,单品优惠功能字段,需要接入请见详细说明
    +   * 
    + **/ + @XStreamAlias("promotion_detail") + private String promotionDetail; + + /** + *
    +   * 支付完成时间.
    +   * time_end
    +   * 是
    +   * String(14)
    +   * 20141030133525
    +   * 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。详见时间规则
    +   * 
    + **/ + @XStreamAlias("time_end") + private String timeEnd; + + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + deviceInfo = readXmlString(d, "device_info"); + openid = readXmlString(d, "openid"); + isSubscribe = readXmlString(d, "is_subscribe"); + subOpenid = readXmlString(d, "sub_openid"); + subsSubscribe = readXmlString(d, "sub_is_subscribe"); + tradeType = readXmlString(d, "trade_type"); + bankType = readXmlString(d, "bank_type"); + feeType = readXmlString(d, "fee_type"); + totalFee = readXmlInteger(d, "total_fee"); + cashFeeType = readXmlString(d, "cash_fee_type"); + cashFee = readXmlInteger(d, "cash_fee"); + transactionId = readXmlString(d, "transaction_id"); + outTradeNo = readXmlString(d, "out_trade_no"); + detail = readXmlString(d, "detail"); + attach = readXmlString(d, "attach"); + promotionDetail = readXmlString(d, "promotion_detail"); + timeEnd = readXmlString(d, "time_end"); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowBaseResult.java new file mode 100644 index 0000000000..ab9077615b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowBaseResult.java @@ -0,0 +1,70 @@ +package com.github.binarywang.wxpay.bean.result; + +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * 记账时间:2018-02-01 04:21:23 微信支付业务单号:50000305742018020103387128253 资金流水单号:1900009231201802015884652186 业务名称:退款 + * 业务类型:退款 收支类型:支出 收支金额(元):0.02 账户结余(元):0.17 资金变更提交申请人:system 备注:缺货 业务凭证号:REF4200000068201801293084726067 + * + * @author cwivan + */ +@Data +@NoArgsConstructor +public class WxPayFundFlowBaseResult implements Serializable { + private static final long serialVersionUID = 4474557532904682718L; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + * 记账时间. + */ + private String BillingTime; + /** + * 微信支付业务单号. + */ + private String bizTransactionId; + /** + * 资金流水单号. + */ + private String fundFlowId; + /** + * 业务名称 + */ + private String bizName; + /** + * 业务类型. + */ + private String bizType; + /** + * 收支类型. + */ + private String financialType; + /** + * 收支金额(元). + */ + private String financialFee; + /** + * 账户结余(元). + */ + private String AccountBalance; + /** + * 资金变更提交申请人. + */ + private String fundApplicant; + /** + * 备注. + */ + private String memo; + /** + * 业务凭证号. + */ + private String bizVoucherId; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowResult.java new file mode 100644 index 0000000000..3f26e0654e --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFundFlowResult.java @@ -0,0 +1,58 @@ +package com.github.binarywang.wxpay.bean.result; + +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + *
    + * 下载资金账单接口响应结果对象类
    + * Created by cwivan on 2018-08-02.
    + * 
    + * + * @author cwivan + */ +@Data +@NoArgsConstructor +public class WxPayFundFlowResult implements Serializable { + private static final long serialVersionUID = 8371500036495349207L; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + * 资金流水返回对象. + */ + private List wxPayFundFlowBaseResultList; + + /** + * 资金流水总笔数 + */ + private String totalRecord; + + /** + * 收入笔数 + */ + private String incomeRecord; + + /** + * 收入金额 + */ + private String incomeAmount; + + /** + * 支出笔数 + */ + private String expenditureRecord; + + /** + * 支出金额 + */ + private String expenditureAmount; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java index 2f8842af82..1eb1a7a604 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java @@ -1,19 +1,31 @@ package com.github.binarywang.wxpay.bean.result; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; /** *
      * 提交刷卡支付接口响应结果对象类
      * Created by Binary Wang on 2017-3-23.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayMicropayResult extends WxPayBaseResult { +public class WxPayMicropayResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 529670965722059189L; + /** *
    -   * 用户标识
    +   * 用户标识.
        * openid
        * 是
        * String(128)
    @@ -26,7 +38,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 是否关注公众账号
    +   * 是否关注公众账号.
        * is_subscribe
        * 是
        * String(1)
    @@ -39,7 +51,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 交易类型
    +   * 交易类型.
        * trade_type
        * 是
        * String(16)
    @@ -52,7 +64,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 付款银行
    +   * 付款银行.
        * bank_type
        * 是
        * String(32)
    @@ -65,7 +77,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 货币类型
    +   * 货币类型.
        * fee_type
        * 否
        * String(16)
    @@ -78,7 +90,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 订单金额
    +   * 订单金额.
        * total_fee
        * 是
        * Int
    @@ -87,11 +99,11 @@ public class WxPayMicropayResult extends WxPayBaseResult {
        * 
    **/ @XStreamAlias("total_fee") - private String totalFee; + private Integer totalFee; /** *
    -   * 应结订单金额
    +   * 应结订单金额.
        * settlement_total_fee
        * 否
        * Int
    @@ -104,7 +116,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 代金券金额
    +   * 代金券金额.
        * coupon_fee
        * 否
        * Int
    @@ -117,7 +129,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 现金支付货币类型
    +   * 现金支付货币类型.
        * cash_fee_type
        * 否
        * String(16)
    @@ -130,7 +142,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 现金支付金额
    +   * 现金支付金额.
        * cash_fee
        * 是
        * Int
    @@ -143,7 +155,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 微信支付订单号
    +   * 微信支付订单号.
        * transaction_id
        * 是
        * String(32)
    @@ -156,7 +168,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 商户订单号
    +   * 商户订单号.
        * out_trade_no
        * 是
        * String(32)
    @@ -169,7 +181,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 商家数据包
    +   * 商家数据包.
        * attach
        * 否
        * String(128)
    @@ -182,7 +194,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 支付完成时间
    +   * 支付完成时间.
        * time_end
        * 是
        * String(14)
    @@ -195,7 +207,7 @@ public class WxPayMicropayResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 营销详情
    +   * 营销详情.
        * promotion_detail
        * 否
        * String(6000)
    @@ -206,123 +218,28 @@ public class WxPayMicropayResult extends WxPayBaseResult {
       @XStreamAlias("promotion_detail")
       private String promotionDetail;
     
    -  public String getOpenid() {
    -    return this.openid;
    -  }
    -
    -  public void setOpenid(String openid) {
    -    this.openid = openid;
    -  }
    -
    -  public String getIsSubscribe() {
    -    return this.isSubscribe;
    -  }
    -
    -  public void setIsSubscribe(String isSubscribe) {
    -    this.isSubscribe = isSubscribe;
    -  }
    -
    -  public String getTradeType() {
    -    return this.tradeType;
    -  }
    -
    -  public void setTradeType(String tradeType) {
    -    this.tradeType = tradeType;
    -  }
    -
    -  public String getBankType() {
    -    return this.bankType;
    -  }
    -
    -  public void setBankType(String bankType) {
    -    this.bankType = bankType;
    -  }
    -
    -  public String getFeeType() {
    -    return this.feeType;
    -  }
    -
    -  public void setFeeType(String feeType) {
    -    this.feeType = feeType;
    -  }
    -
    -  public String getTotalFee() {
    -    return this.totalFee;
    -  }
    -
    -  public void setTotalFee(String totalFee) {
    -    this.totalFee = totalFee;
    -  }
    -
    -  public Integer getSettlementTotalFee() {
    -    return this.settlementTotalFee;
    -  }
    -
    -  public void setSettlementTotalFee(Integer settlementTotalFee) {
    -    this.settlementTotalFee = settlementTotalFee;
    -  }
    -
    -  public Integer getCouponFee() {
    -    return this.couponFee;
    -  }
    -
    -  public void setCouponFee(Integer couponFee) {
    -    this.couponFee = couponFee;
    -  }
    -
    -  public String getCashFeeType() {
    -    return this.cashFeeType;
    -  }
    -
    -  public void setCashFeeType(String cashFeeType) {
    -    this.cashFeeType = cashFeeType;
    -  }
    -
    -  public Integer getCashFee() {
    -    return this.cashFee;
    -  }
    -
    -  public void setCashFee(Integer cashFee) {
    -    this.cashFee = cashFee;
    -  }
    -
    -  public String getTransactionId() {
    -    return this.transactionId;
    -  }
    -
    -  public void setTransactionId(String transactionId) {
    -    this.transactionId = transactionId;
    -  }
    -
    -  public String getOutTradeNo() {
    -    return this.outTradeNo;
    -  }
    -
    -  public void setOutTradeNo(String outTradeNo) {
    -    this.outTradeNo = outTradeNo;
    -  }
    -
    -  public String getAttach() {
    -    return this.attach;
    -  }
    -
    -  public void setAttach(String attach) {
    -    this.attach = attach;
    -  }
    -
    -  public String getTimeEnd() {
    -    return this.timeEnd;
    -  }
    -
    -  public void setTimeEnd(String timeEnd) {
    -    this.timeEnd = timeEnd;
    -  }
    -
    -  public String getPromotionDetail() {
    -    return this.promotionDetail;
    +  /**
    +   * 从XML结构中加载额外的熟悉
    +   *
    +   * @param d Document
    +   */
    +  @Override
    +  protected void loadXml(Document d) {
    +    openid = readXmlString(d, "openid");
    +    isSubscribe = readXmlString(d, "is_subscribe");
    +    tradeType = readXmlString(d, "trade_type");
    +    bankType = readXmlString(d, "bank_type");
    +    feeType = readXmlString(d, "fee_type");
    +    totalFee = readXmlInteger(d, "total_fee");
    +    settlementTotalFee = readXmlInteger(d, "settlement_total_fee");
    +    couponFee = readXmlInteger(d, "coupon_fee");
    +    cashFeeType = readXmlString(d, "cash_fee_type");
    +    cashFee = readXmlInteger(d, "cash_fee");
    +    transactionId = readXmlString(d, "transaction_id");
    +    outTradeNo = readXmlString(d, "out_trade_no");
    +    attach = readXmlString(d, "attach");
    +    timeEnd = readXmlString(d, "time_end");
    +    promotionDetail = readXmlString(d, "promotion_detail");
       }
     
    -  public void setPromotionDetail(String promotionDetail) {
    -    this.promotionDetail = promotionDetail;
    -  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java
    index 7221251f10..8be7e858dd 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java
    @@ -1,28 +1,41 @@
     package com.github.binarywang.wxpay.bean.result;
     
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.Data;
    +import lombok.EqualsAndHashCode;
    +import lombok.NoArgsConstructor;
    +import org.w3c.dom.Document;
    +
    +import java.io.Serializable;
     
     /**
      * 
      * 关闭订单结果对象类
      * Created by Binary Wang on 2016-10-27.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayOrderCloseResult extends WxPayBaseResult { - +public class WxPayOrderCloseResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 800873502890274834L; /** * 业务结果描述 */ @XStreamAlias("result_msg") private String resultMsg; - public String getResultMsg() { - return this.resultMsg; + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + resultMsg = readXmlString(d, "result_msg"); } - public void setResultMsg(String resultMsg) { - this.resultMsg = resultMsg; - } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java deleted file mode 100644 index 723702a455..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java +++ /dev/null @@ -1,392 +0,0 @@ -package com.github.binarywang.wxpay.bean.result; - -import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon; -import com.github.binarywang.wxpay.converter.WxPayOrderNotifyResultConverter; -import com.thoughtworks.xstream.XStream; -import com.thoughtworks.xstream.annotations.XStreamAlias; -import me.chanjar.weixin.common.util.BeanUtils; -import me.chanjar.weixin.common.util.ToStringUtils; -import me.chanjar.weixin.common.util.xml.XStreamInitializer; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -/** - * 支付结果通用通知 ,文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 - * - * @author aimilin6688 - * @since 2.5.0 - */ -@XStreamAlias("xml") -public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable { - - private static final long serialVersionUID = 5389718115223345496L; - - /** - *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * String(32)
    -   * 013467007045764
    -   * 微信支付分配的终端设备号,
    -   * 
    - */ - @XStreamAlias("device_info") - private String deviceInfo; - - /** - *
    -   * 用户标识
    -   * openid
    -   * 是
    -   * String(128)
    -   * wxd930ea5d5a258f4f
    -   * 用户在商户appid下的唯一标识
    -   * 
    - */ - @XStreamAlias("openid") - private String openid; - - /** - *
    -   * 是否关注公众账号
    -   * is_subscribe
    -   * 否
    -   * String(1)
    -   * Y
    -   * 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    -   * 
    - */ - @XStreamAlias("is_subscribe") - private String isSubscribe; - - - /** - *
    -   * 交易类型
    -   * trade_type
    -   * 是
    -   * String(16)
    -   * JSAPI	JSAPI、NATIVE、APP
    -   * 
    - */ - @XStreamAlias("trade_type") - private String tradeType; - - - /** - *
    -   * 付款银行
    -   * bank_type
    -   * 是
    -   * String(16)
    -   * CMC
    -   * 银行类型,采用字符串类型的银行标识,银行类型见银行列表
    -   * 
    - */ - @XStreamAlias("bank_type") - private String bankType; - - - /** - *
    -   * 订单金额
    -   * total_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 订单总金额,单位为分
    -   * 
    - */ - @XStreamAlias("total_fee") - private Integer totalFee; - /** - *
    -   * 应结订单金额
    -   * settlement_total_fee
    -   * 否
    -   * Int
    -   * 100
    -   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    -   * 
    - */ - @XStreamAlias("settlement_total_fee") - private Integer settlementTotalFee; - /** - *
    -   * 货币种类
    -   * fee_type
    -   * 否
    -   * String(8)
    -   * CNY
    -   * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    -   * 
    - */ - @XStreamAlias("fee_type") - private String feeType; - /** - *
    -   * 现金支付金额
    -   * cash_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 现金支付金额订单现金支付金额,详见支付金额
    -   * 
    - */ - @XStreamAlias("cash_fee") - private Integer cashFee; - /** - *
    -   * 现金支付货币类型
    -   * cash_fee_type
    -   * 否
    -   * String(16)
    -   * CNY
    -   * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    -   * 
    - */ - @XStreamAlias("cash_fee_type") - private String cashFeeType; - /** - *
    -   * 总代金券金额
    -   * coupon_fee
    -   * 否
    -   * Int
    -   * 10
    -   * 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
    -   * 
    - */ - @XStreamAlias("coupon_fee") - private Integer couponFee; - - /** - *
    -   * 代金券使用数量
    -   * coupon_count
    -   * 否
    -   * Int
    -   * 1
    -   * 代金券使用数量
    -   * 
    - */ - @XStreamAlias("coupon_count") - private Integer couponCount; - - private List couponList; - - /** - *
    -   * 微信支付订单号
    -   * transaction_id
    -   * 是
    -   * String(32)
    -   * 1217752501201407033233368018
    -   * 微信支付订单号
    -   * 
    - */ - @XStreamAlias("transaction_id") - private String transactionId; - - /** - *
    -   * 商户订单号
    -   * out_trade_no
    -   * 是
    -   * String(32)
    -   * 1212321211201407033568112322
    -   * 商户系统的订单号,与请求一致。
    -   * 
    - */ - @XStreamAlias("out_trade_no") - private String outTradeNo; - /** - *
    -   * 商家数据包
    -   * attach
    -   * 否
    -   * String(128)
    -   * 123456
    -   * 商家数据包,原样返回
    -   * 
    - */ - @XStreamAlias("attach") - private String attach; - /** - *
    -   * 支付完成时间
    -   * time_end
    -   * 是
    -   * String(14)
    -   * 20141030133525
    -   * 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    -   * 
    - */ - @XStreamAlias("time_end") - private String timeEnd; - - public static WxPayOrderNotifyResult fromXML(String xmlString) { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxPayOrderNotifyResult.class); - xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); - WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString); - result.setXmlString(xmlString); - return result; - } - - public Integer getCouponCount() { - return couponCount; - } - - public void setCouponCount(Integer couponCount) { - this.couponCount = couponCount; - } - - public List getCouponList() { - return couponList; - } - - public void setCouponList(List couponList) { - this.couponList = couponList; - } - - public String getDeviceInfo() { - return deviceInfo; - } - - public void setDeviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - } - - public String getOpenid() { - return openid; - } - - public void setOpenid(String openid) { - this.openid = openid; - } - - public String getIsSubscribe() { - return isSubscribe; - } - - public void setIsSubscribe(String isSubscribe) { - this.isSubscribe = isSubscribe; - } - - public String getTradeType() { - return tradeType; - } - - public void setTradeType(String tradeType) { - this.tradeType = tradeType; - } - - public String getBankType() { - return bankType; - } - - public void setBankType(String bankType) { - this.bankType = bankType; - } - - public Integer getTotalFee() { - return totalFee; - } - - public void setTotalFee(Integer totalFee) { - this.totalFee = totalFee; - } - - public Integer getSettlementTotalFee() { - return settlementTotalFee; - } - - public void setSettlementTotalFee(Integer settlementTotalFee) { - this.settlementTotalFee = settlementTotalFee; - } - - public String getFeeType() { - return feeType; - } - - public void setFeeType(String feeType) { - this.feeType = feeType; - } - - public Integer getCashFee() { - return cashFee; - } - - public void setCashFee(Integer cashFee) { - this.cashFee = cashFee; - } - - public String getCashFeeType() { - return cashFeeType; - } - - public void setCashFeeType(String cashFeeType) { - this.cashFeeType = cashFeeType; - } - - public Integer getCouponFee() { - return couponFee; - } - - public void setCouponFee(Integer couponFee) { - this.couponFee = couponFee; - } - - public String getTransactionId() { - return transactionId; - } - - public void setTransactionId(String transactionId) { - this.transactionId = transactionId; - } - - public String getOutTradeNo() { - return outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public String getAttach() { - return attach; - } - - public void setAttach(String attach) { - this.attach = attach; - } - - public String getTimeEnd() { - return timeEnd; - } - - public void setTimeEnd(String timeEnd) { - this.timeEnd = timeEnd; - } - - @Override - public Map toMap() { - Map resultMap = BeanUtils.xmlBean2Map(this); - if (this.getCouponCount() != null && this.getCouponCount() > 0) { - for (int i = 0; i < this.getCouponCount(); i++) { - WxPayOrderNotifyCoupon coupon = couponList.get(i); - resultMap.putAll(coupon.toMap(i)); - } - } - return resultMap; - } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java index ff087bf010..aa9985dfa9 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java @@ -2,7 +2,10 @@ import com.google.common.collect.Lists; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import org.w3c.dom.Document; +import java.io.Serializable; import java.util.List; /** @@ -18,13 +21,31 @@ *
  51. 描述 *
  52. * - * @author binarywang(Binary Wang) + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayOrderQueryResult extends WxPayBaseResult { +public class WxPayOrderQueryResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 8241891654782412789L; /** - *
    设备号
    +   * 
    +   * 字段名:营销详情.
    +   * 变量名:promotion_detail
    +   * 是否必填:否,单品优惠才有
    +   * 类型:String(6000)
    +   * 示例值:[{"promotion_detail":[{"promotion_id":"109519","name":"单品惠-6","scope":"SINGLE","type":"DISCOUNT","amount":5,"activity_id":"931386","wxpay_contribute":0,"merchant_contribute":0,"other_contribute":5,"goods_detail":[{"goods_id":"a_goods1","goods_remark":"商品备注","quantity":7,"price":1,"discount_amount":4},{"goods_id":"a_goods2","goods_remark":"商品备注","quantity":1,"price":2,"discount_amount":1}]}]}
    +   * 描述:单品优惠专用参数,详见https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_201&index=3
    +   * 
    + */ + @XStreamAlias("promotion_detail") + private String promotionDetail; + + /** + *
    +   * 设备号.
        * device_info
        * 否
        * String(32)
    @@ -36,7 +57,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String deviceInfo;
     
       /**
    -   * 
    用户标识
    +   * 
    +   * 用户标识.
        * openid
        * 是
        * String(128)
    @@ -48,9 +70,10 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String openid;
     
       /**
    -   * 
    是否关注公众账号
    +   * 
    +   * 是否关注公众账号.
        * is_subscribe
    -   * 否
    +   * 是
        * String(1)
        * Y
        * 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    @@ -60,7 +83,34 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String isSubscribe;
     
       /**
    -   * 
    交易类型
    +   * 
    +   * 用户子标识	.
    +   * sub_openid
    +   * 否
    +   * String(128)
    +   * oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    +   * 用户在子商户appid下的唯一标识
    +   * 
    + */ + @XStreamAlias("sub_openid") + private String subOpenid; + + /** + *
    +   * 是否关注子公众账号.
    +   * sub_is_subscribe
    +   * 否
    +   * String(1)
    +   * Y
    +   * 用户是否关注子公众账号,Y-关注,N-未关注(机构商户不返回)
    +   * 
    + */ + @XStreamAlias("sub_is_subscribe") + private String isSubscribeSub; + + /** + *
    +   * 交易类型.
        * trade_type
        * 是
        * String(16)
    @@ -72,7 +122,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String tradeType;
     
       /**
    -   * 
    交易状态
    +   * 
    +   * 交易状态.
        * trade_state
        * 是
        * String(32)
    @@ -84,7 +135,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String tradeState;
     
       /**
    -   * 
    付款银行
    +   * 
    +   * 付款银行.
        * bank_type
        * 是
        * String(16)
    @@ -96,7 +148,27 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String bankType;
     
       /**
    -   * 
    订单金额
    +   * 
    +   * 商品详情.
    +   * detail
    +   * 否
    +   * String(8192)
    +   * 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。如果使用了单品优惠,会有单品优惠信息返回
    +   *
    +   * discount_detail []:
    +   * └ goods_id String 必填 32 商品的编号
    +   * └ goods_name String 必填 256 商品名称
    +   * └ coupon_batch_id String 必填 代金券批次ID
    +   * └ coupon_id String 必填 代金卷ID
    +   * └ coupon_fee Int 必填 代金券支付金额,单位为分
    +   * 
    + **/ + @XStreamAlias("detail") + private String detail; + + /** + *
    +   * 订单金额.
        * total_fee
        * 是
        * Int
    @@ -108,19 +180,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private Integer totalFee;
     
       /**
    -   * 
    应结订单金额
    -   * settlement_total_fee
    -   * 否
    -   * Int
    -   * 100
    -   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    -   * 
    - */ - @XStreamAlias("settlement_total_fee") - private Integer settlementTotalFee; - - /** - *
    货币种类
    +   * 
    +   * 货币种类.
        * fee_type
        * 否
        * String(8)
    @@ -132,7 +193,21 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String feeType;
     
       /**
    -   * 
    现金支付金额
    +   * 
    +   * 应结订单金额.
    +   * settlement_total_fee
    +   * 否
    +   * Int
    +   * 100
    +   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +   * 
    + */ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + + /** + *
    +   * 现金支付金额.
        * cash_fee
        * 是
        * Int
    @@ -144,7 +219,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private Integer cashFee;
     
       /**
    -   * 
    现金支付货币类型
    +   * 
    +   * 现金支付货币类型.
        * cash_fee_type
        * 否
        * String(16)
    @@ -156,7 +232,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private String cashFeeType;
     
       /**
    -   * 
    代金券金额
    +   * 
    +   * 代金券金额.
        * coupon_fee
        * 否
        * Int
    @@ -168,7 +245,7 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       private Integer couponFee;
     
       /**
    -   * 
    代金券使用数量
    +   * 
    代金券使用数量.
        * coupon_count
        * 否
        * Int
    @@ -181,7 +258,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
     
       private List coupons;
       /**
    -   * 
    微信支付订单号
    +   * 
    +   * 微信支付订单号.
        * transaction_id
        * 是
        * String(32)
    @@ -192,7 +270,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       @XStreamAlias("transaction_id")
       private String transactionId;
       /**
    -   * 
    商户订单号
    +   * 
    +   * 商户订单号.
        * out_trade_no
        * 是
        * String(32)
    @@ -202,8 +281,10 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
        */
       @XStreamAlias("out_trade_no")
       private String outTradeNo;
    +
       /**
    -   * 
    附加数据
    +   * 
    +   * 附加数据.
        * attach
        * 否
        * String(128)
    @@ -213,8 +294,10 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
        */
       @XStreamAlias("attach")
       private String attach;
    +
       /**
    -   * 
    支付完成时间
    +   * 
    +   * 支付完成时间.
        * time_end
        * 是
        * String(14)
    @@ -224,8 +307,10 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
        */
       @XStreamAlias("time_end")
       private String timeEnd;
    +
       /**
    -   * 
    交易状态描述
    +   * 
    +   * 交易状态描述.
        * trade_state_desc
        * 是
        * String(256)
    @@ -236,160 +321,8 @@ public class WxPayOrderQueryResult extends WxPayBaseResult {
       @XStreamAlias("trade_state_desc")
       private String tradeStateDesc;
     
    -  public String getDeviceInfo() {
    -    return this.deviceInfo;
    -  }
    -
    -  public void setDeviceInfo(String deviceInfo) {
    -    this.deviceInfo = deviceInfo;
    -  }
    -
    -  public String getOpenid() {
    -    return this.openid;
    -  }
    -
    -  public void setOpenid(String openid) {
    -    this.openid = openid;
    -  }
    -
    -  public String getIsSubscribe() {
    -    return this.isSubscribe;
    -  }
    -
    -  public void setIsSubscribe(String isSubscribe) {
    -    this.isSubscribe = isSubscribe;
    -  }
    -
    -  public String getTradeType() {
    -    return this.tradeType;
    -  }
    -
    -  public void setTradeType(String tradeType) {
    -    this.tradeType = tradeType;
    -  }
    -
    -  public String getTradeState() {
    -    return this.tradeState;
    -  }
    -
    -  public void setTradeState(String tradeState) {
    -    this.tradeState = tradeState;
    -  }
    -
    -  public String getBankType() {
    -    return this.bankType;
    -  }
    -
    -  public void setBankType(String bankType) {
    -    this.bankType = bankType;
    -  }
    -
    -  public Integer getTotalFee() {
    -    return this.totalFee;
    -  }
    -
    -  public void setTotalFee(Integer totalFee) {
    -    this.totalFee = totalFee;
    -  }
    -
    -  public Integer getSettlementTotalFee() {
    -    return this.settlementTotalFee;
    -  }
    -
    -  public void setSettlementTotalFee(Integer settlementTotalFee) {
    -    this.settlementTotalFee = settlementTotalFee;
    -  }
    -
    -  public String getFeeType() {
    -    return this.feeType;
    -  }
    -
    -  public void setFeeType(String feeType) {
    -    this.feeType = feeType;
    -  }
    -
    -  public Integer getCashFee() {
    -    return this.cashFee;
    -  }
    -
    -  public void setCashFee(Integer cashFee) {
    -    this.cashFee = cashFee;
    -  }
    -
    -  public String getCashFeeType() {
    -    return this.cashFeeType;
    -  }
    -
    -  public void setCashFeeType(String cashFeeType) {
    -    this.cashFeeType = cashFeeType;
    -  }
    -
    -  public Integer getCouponFee() {
    -    return this.couponFee;
    -  }
    -
    -  public void setCouponFee(Integer couponFee) {
    -    this.couponFee = couponFee;
    -  }
    -
    -  public Integer getCouponCount() {
    -    return this.couponCount;
    -  }
    -
    -  public void setCouponCount(Integer couponCount) {
    -    this.couponCount = couponCount;
    -  }
    -
    -  public List getCoupons() {
    -    return this.coupons;
    -  }
    -
    -  public void setCoupons(List coupons) {
    -    this.coupons = coupons;
    -  }
    -
    -  public String getTransactionId() {
    -    return this.transactionId;
    -  }
    -
    -  public void setTransactionId(String transactionId) {
    -    this.transactionId = transactionId;
    -  }
    -
    -  public String getOutTradeNo() {
    -    return this.outTradeNo;
    -  }
    -
    -  public void setOutTradeNo(String outTradeNo) {
    -    this.outTradeNo = outTradeNo;
    -  }
    -
    -  public String getAttach() {
    -    return this.attach;
    -  }
    -
    -  public void setAttach(String attach) {
    -    this.attach = attach;
    -  }
    -
    -  public String getTimeEnd() {
    -    return this.timeEnd;
    -  }
    -
    -  public void setTimeEnd(String timeEnd) {
    -    this.timeEnd = timeEnd;
    -  }
    -
    -  public String getTradeStateDesc() {
    -    return this.tradeStateDesc;
    -  }
    -
    -  public void setTradeStateDesc(String tradeStateDesc) {
    -    this.tradeStateDesc = tradeStateDesc;
    -  }
    -
       /**
    -   * 通过xml组装coupons属性内容
    +   * 通过xml组装coupons属性内容.
        */
       public void composeCoupons() {
         if (this.couponCount != null && this.couponCount > 0) {
    @@ -402,9 +335,46 @@ public void composeCoupons() {
         }
       }
     
    -  public static class Coupon {
    +  /**
    +   * 从XML结构中加载额外的熟悉
    +   *
    +   * @param d Document
    +   */
    +  @Override
    +  protected void loadXml(Document d) {
    +    promotionDetail = readXmlString(d, "promotion_detail");
    +    deviceInfo = readXmlString(d, "device_info");
    +    openid = readXmlString(d, "openid");
    +    isSubscribe = readXmlString(d, "is_subscribe");
    +    tradeType = readXmlString(d, "trade_type");
    +    tradeState = readXmlString(d, "trade_state");
    +    bankType = readXmlString(d, "bank_type");
    +    totalFee = readXmlInteger(d, "total_fee");
    +    settlementTotalFee = readXmlInteger(d, "settlement_total_fee");
    +    feeType = readXmlString(d, "fee_type");
    +    cashFee = readXmlInteger(d, "cash_fee");
    +    cashFeeType = readXmlString(d, "cash_fee_type");
    +    couponFee = readXmlInteger(d, "coupon_fee");
    +    couponCount = readXmlInteger(d, "coupon_count");
    +    this.transactionId = readXmlString(d, "transaction_id");
    +    this.outTradeNo = readXmlString(d, "out_trade_no");
    +    this.attach = readXmlString(d, "attach");
    +    this.timeEnd = readXmlString(d, "time_end");
    +    this.tradeStateDesc = readXmlString(d, "trade_state_desc");
    +  }
    +
    +  /**
    +   * The type Coupon.
    +   */
    +  @Data
    +  @Builder(builderMethodName = "newBuilder")
    +  @AllArgsConstructor
    +  public static class Coupon implements Serializable {
    +    private static final long serialVersionUID = -954000582332155081L;
    +
         /**
    -     * 
    代金券类型
    +     * 
    +     * 代金券类型.
          * coupon_type_$n
          * 否
          * String
    @@ -417,7 +387,8 @@ public static class Coupon {
         private String couponType;
     
         /**
    -     * 
    代金券ID
    +     * 
    +     * 代金券ID.
          * coupon_id_$n
          * 否
          * String(20)
    @@ -428,7 +399,8 @@ public static class Coupon {
         private String couponId;
     
         /**
    -     * 
    单个代金券支付金额
    +     * 
    +     * 单个代金券支付金额.
          * coupon_fee_$n
          * 否
          * Int
    @@ -438,35 +410,6 @@ public static class Coupon {
          */
         private Integer couponFee;
     
    -    public Coupon(String couponType, String couponId, Integer couponFee) {
    -      this.couponType = couponType;
    -      this.couponId = couponId;
    -      this.couponFee = couponFee;
    -    }
    -
    -    public String getCouponType() {
    -      return this.couponType;
    -    }
    -
    -    public void setCouponType(String couponType) {
    -      this.couponType = couponType;
    -    }
    -
    -    public String getCouponId() {
    -      return this.couponId;
    -    }
    -
    -    public void setCouponId(String couponId) {
    -      this.couponId = couponId;
    -    }
    -
    -    public Integer getCouponFee() {
    -      return this.couponFee;
    -    }
    -
    -    public void setCouponFee(Integer couponFee) {
    -      this.couponFee = couponFee;
    -    }
    -
       }
    +
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
    index c80b72d216..e725ac5689 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
    @@ -1,17 +1,27 @@
     package com.github.binarywang.wxpay.bean.result;
     
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.Data;
    +import lombok.EqualsAndHashCode;
    +import lombok.NoArgsConstructor;
    +import org.w3c.dom.Document;
    +
    +import java.io.Serializable;
     
     /**
      * 
      * 撤销订单响应结果类
      * Created by Binary Wang on 2017-3-23.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayOrderReverseResult extends WxPayBaseResult { - +public class WxPayOrderReverseResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 3615350465758009338L; /** *
        * 是否重调
    @@ -25,11 +35,14 @@ public class WxPayOrderReverseResult extends WxPayBaseResult {
       @XStreamAlias("recall")
       private String isRecall;
     
    -  public String getIsRecall() {
    -    return this.isRecall;
    +  /**
    +   * 从XML结构中加载额外的熟悉
    +   *
    +   * @param d Document
    +   */
    +  @Override
    +  protected void loadXml(Document d) {
    +    isRecall = readXmlString(d, "recall");
       }
     
    -  public void setIsRecall(String isRecall) {
    -    this.isRecall = isRecall;
    -  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayQueryExchangeRateResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayQueryExchangeRateResult.java
    new file mode 100644
    index 0000000000..c6f9409933
    --- /dev/null
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayQueryExchangeRateResult.java
    @@ -0,0 +1,60 @@
    +package com.github.binarywang.wxpay.bean.result;
    +
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.Data;
    +import lombok.EqualsAndHashCode;
    +import lombok.NoArgsConstructor;
    +import org.w3c.dom.Document;
    +
    +import java.io.Serializable;
    +
    +/**
    + * 汇率查询响应.
    + *
    + * @author Binary Wang
    + * @date 2020-05-23
    + */
    +@Data
    +@EqualsAndHashCode(callSuper = true)
    +@NoArgsConstructor
    +@XStreamAlias("xml")
    +public class WxPayQueryExchangeRateResult extends BaseWxPayResult implements Serializable {
    +  private static final long serialVersionUID = 2269734222658532364L;
    +
    +  /**
    +   * 币种
    +   * fee_type
    +   * 是
    +   * String(10)
    +   * SUCCESS	外币币种,详细请见参数规定
    +   */
    +  @XStreamAlias("fee_type")
    +  private String feeType;
    +
    +  /**
    +   * 汇率时间
    +   * rate_time
    +   * 是
    +   * String(14)
    +   * 20150807131545
    +   * 格式:yyyyMMddhhmmss
    +   */
    +  @XStreamAlias("rate_time")
    +  private String rateTime;
    +
    +  /**
    +   * 现汇卖出价
    +   * rate
    +   * 是
    +   * String(15)
    +   * 系统错误
    +   * 外币标准单位乘以100折算为人民币的金额,保留4位小数(如:100美元按当时汇率折算返回的先汇卖出价是628.2100)
    +   */
    +  @XStreamAlias("rate")
    +  private String rate;
    +
    +  @Override
    +  protected void loadXml(Document d) {
    +
    +  }
    +}
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java
    index e0b86732fd..88a1f3e4e5 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java
    @@ -1,31 +1,39 @@
     package com.github.binarywang.wxpay.bean.result;
     
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.Data;
    +import lombok.EqualsAndHashCode;
    +import lombok.NoArgsConstructor;
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Node;
    +import org.w3c.dom.NodeList;
    +
    +import java.io.Serializable;
    +import java.util.ArrayList;
    +import java.util.List;
     
     /**
      * 
    - *   注释中各行对应含义:
    - *   字段名
    - *   字段
    - *   必填
    - *   示例值
    - *   类型
    - *   说明
      * Created by Binary Wang on 2016-11-28.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayRedpackQueryResult extends WxPayBaseResult { +public class WxPayRedpackQueryResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -3849864122189552906L; /** *
    -   * 商户订单号
    -   * mch_billno
    -   * 是
    -   * 10000098201411111234567890
    -   * String(28)
    -   * 商户使用查询API填写的商户单号的原路返回
    +   * 字段含义:商户订单号.
    +   * 字段名:mch_billno
    +   * 是否必填:是
    +   * 示例值:10000098201411111234567890
    +   * 类型:String(28)
    +   * 字段说明:商户使用查询API填写的商户单号的原路返回
        * 
    */ @XStreamAlias("mch_billno") @@ -33,12 +41,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包单号
    -   * detail_id
    -   * 是
    -   * 1000000000201503283103439304
    -   * String(32)
    -   * 使用API发放现金红包时返回的红包单号
    +   * 字段含义:红包单号.
    +   * 字段名:detail_id
    +   * 是否必填:是
    +   * 示例值:1000000000201503283103439304
    +   * 类型:String(32)
    +   * 字段说明:使用API发放现金红包时返回的红包单号
        * 
    */ @XStreamAlias("detail_id") @@ -46,12 +54,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包状态
    -   * status
    -   * 是
    -   * RECEIVED
    -   * string(16)
    -   * SENDING:发放中,
    +   * 字段含义:红包状态.
    +   * 字段名:status
    +   * 是否必填:是
    +   * 示例值:RECEIVED
    +   * 类型:string(16)
    +   * 字段说明:SENDING:发放中,
        * SENT:已发放待领取,
        * FAILED:发放失败,
        * RECEIVED:已领取,
    @@ -64,12 +72,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult {
     
       /**
        * 
    -   * 发放类型
    -   * send_type
    -   * 是
    -   * API
    -   * String(32)
    -   *  API:通过API接口发放,
    +   * 字段含义:发放类型.
    +   * 字段名:send_type
    +   * 是否必填:是
    +   * 示例值:API
    +   * 类型:String(32)
    +   * 字段说明:API:通过API接口发放,
        *  UPLOAD:通过上传文件方式发放,
        *  ACTIVITY:通过活动方式发放
        * 
    @@ -79,12 +87,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包类型
    -   * hb_type
    -   * 是
    -   * GROUP
    -   * String(32)
    -   *  GROUP:裂变红包,
    +   * 字段含义:红包类型.
    +   * 字段名:hb_type
    +   * 是否必填:是
    +   * 示例值:GROUP
    +   * 类型:String(32)
    +   * 字段说明:GROUP:裂变红包,
        *  NORMAL:普通红包
        * 
    */ @@ -93,12 +101,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包个数
    -   * total_num
    -   * 是
    -   * 1
    -   * int
    -   * 红包个数
    +   * 字段含义:红包个数.
    +   * 字段名:total_num
    +   * 是否必填:是
    +   * 示例值:1
    +   * 类型:int
    +   * 字段说明:红包个数
        * 
    */ @XStreamAlias("total_num") @@ -106,12 +114,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包金额
    -   * total_amount
    -   * 是
    -   * 5000
    -   * int
    -   * 红包总金额(单位分)
    +   * 字段含义:红包金额.
    +   * 字段名:total_amount
    +   * 是否必填:是
    +   * 示例值:5000
    +   * 类型:int
    +   * 字段说明:红包总金额(单位分)
        * 
    */ @XStreamAlias("total_amount") @@ -119,12 +127,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 失败原因
    -   * reason
    -   * 否
    -   * 余额不足
    -   * String(32)
    -   * 发送失败原因
    +   * 字段含义:失败原因.
    +   * 字段名:reason
    +   * 是否必填:否
    +   * 示例值:余额不足
    +   * 类型:String(32)
    +   * 字段说明:发送失败原因
        * 
    */ @XStreamAlias("reason") @@ -132,12 +140,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包发送时间
    -   * send_time
    -   * 是
    -   * 2015-04-21 20:00:00
    -   * String(32)
    -   * 红包的发送时间
    +   * 字段含义:红包发送时间.
    +   * 字段名:send_time
    +   * 是否必填:是
    +   * 示例值:2015-04-21 20:00:00
    +   * 类型:String(32)
    +   * 字段说明:红包的发送时间
        * 
    */ @XStreamAlias("send_time") @@ -145,12 +153,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包退款时间
    -   * refund_time
    -   * 否
    -   * 2015-04-21 23:03:00
    -   * String(32)
    -   * 红包的退款时间(如果其未领取的退款)
    +   * 字段含义:红包退款时间.
    +   * 字段名: refund_time
    +   * 是否必填:否
    +   * 示例值:2015-04-21 23:03:00
    +   * 类型:String(32)
    +   * 字段说明:红包的退款时间(如果其未领取的退款)
        * 
    */ @XStreamAlias("refund_time") @@ -158,12 +166,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 红包退款金额
    -   * refund_amount
    -   * 否
    -   * 8000
    -   * Int
    -   * 红包退款金额
    +   * 字段含义:红包退款金额.
    +   * 字段名:refund_amount
    +   * 是否必填:否
    +   * 示例值:8000
    +   * 类型:Int
    +   * 字段说明:红包退款金额
        * 
    */ @XStreamAlias("refund_amount") @@ -171,12 +179,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 祝福语
    -   * wishing
    -   * 否
    -   * 新年快乐
    -   * String(128)
    -   * 祝福语
    +   * 字段含义:祝福语.
    +   * 字段名:wishing
    +   * 是否必填:否
    +   * 示例值:新年快乐
    +   * 类型:String(128)
    +   * 字段说明:祝福语
        * 
    */ @XStreamAlias("wishing") @@ -184,12 +192,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 活动描述
    -   * remark
    -   * 否
    -   * 新年红包
    -   * String(256)
    -   * 活动描述,低版本微信可见
    +   * 字段含义:活动描述.
    +   * 字段名:remark
    +   * 是否必填:否
    +   * 示例值:新年红包
    +   * 类型:String(256)
    +   * 字段说明:活动描述,低版本微信可见
        * 
    */ @XStreamAlias("remark") @@ -197,12 +205,12 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 活动名称
    -   * act_name
    -   * 否
    -   * 新年红包
    -   * String(32)
    -   * 发红包的活动名称
    +   * 字段含义:活动名称.
    +   * 字段名:act_name
    +   * 是否必填:否
    +   * 示例值:新年红包
    +   * 类型:String(32)
    +   * 字段说明:发红包的活动名称
        * 
    */ @XStreamAlias("act_name") @@ -210,197 +218,98 @@ public class WxPayRedpackQueryResult extends WxPayBaseResult { /** *
    -   * 裂变红包领取列表
    -   * hblist
    -   * 否
    -   *
    -   *
    -   * 裂变红包的领取列表
    +   * 字段含义:裂变红包领取列表.
    +   * 字段名:redpackList
    +   * 是否必填:否
    +   * 字段说明: 裂变红包的领取列表
        * 
    */ @XStreamAlias("hblist") - private String hblist; + private List redpackList; /** - *
    -   * 领取红包的Openid
    -   * openid
    -   * 是
    -   * ohO4GtzOAAYMp2yapORH3dQB3W18
    -   * String(32)
    -   * 领取红包的openid
    -   * 
    + * 从XML结构中加载额外的熟悉 + * + * @param d Document */ - @XStreamAlias("openid") - private String openid; + @Override + protected void loadXml(Document d) { + mchBillNo = readXmlString(d, "mch_billno"); + detailId = readXmlString(d, "detail_id"); + status = readXmlString(d, "status"); + sendType = readXmlString(d, "send_type"); + hbType = readXmlString(d, "hb_type"); + totalNum = readXmlInteger(d, "total_num"); + totalAmount = readXmlInteger(d, "total_amount"); + reason = readXmlString(d, "reason"); + sendTime = readXmlString(d, "send_time"); + refundTime = readXmlString(d, "refund_time"); + refundAmount = readXmlInteger(d, "refund_amount"); + wishing = readXmlString(d, "wishing"); + remark = readXmlString(d, "remark"); + actName = readXmlString(d, "act_name"); + + NodeList nodeList = d.getElementsByTagName("hbinfo"); + List list = new ArrayList<>(nodeList.getLength()); + + for (int i = 0, j = nodeList.getLength(); i < j; i++) { + Node node = nodeList.item(i); + RedpackInfo rp = new RedpackInfo(); + rp.amount = readXmlInteger(node, "amount"); + rp.openid = readXmlString(node, "openid"); + rp.receiveTime = readXmlString(node, "rcv_time"); + list.add(rp); + } + + redpackList = list; - /** - *
    -   * 金额
    -   * amount
    -   * 是
    -   * 100
    -   * int
    -   * 领取金额
    -   * 
    - */ - @XStreamAlias("amount") - private Integer amount; + } /** - *
    -   * 接收时间
    -   * rcv_time
    -   * 是
    -   * 2015-04-21 20:00:00
    -   * String(32)
    -   * 领取红包的时间
    -   * 
    + * The type Redpack info. */ - @XStreamAlias("rcv_time") - private String receiveTime; - - public String getMchBillNo() { - return mchBillNo; - } - - public void setMchBillNo(String mchBillNo) { - this.mchBillNo = mchBillNo; - } - - public String getDetailId() { - return detailId; - } - - public void setDetailId(String detailId) { - this.detailId = detailId; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getSendType() { - return sendType; - } - - public void setSendType(String sendType) { - this.sendType = sendType; - } - - public String getHbType() { - return hbType; - } - - public void setHbType(String hbType) { - this.hbType = hbType; - } - - public Integer getTotalNum() { - return totalNum; - } - - public void setTotalNum(Integer totalNum) { - this.totalNum = totalNum; - } - - public Integer getTotalAmount() { - return totalAmount; - } - - public void setTotalAmount(Integer totalAmount) { - this.totalAmount = totalAmount; - } - - public String getReason() { - return reason; + @Data + @XStreamAlias("hbinfo") + public static class RedpackInfo implements Serializable { + private static final long serialVersionUID = 7829773321457772100L; + /** + *
    +     * 字段含义:领取红包的Openid.
    +     * 字段名: openid
    +     * 是否必填:是
    +     * 示例值:ohO4GtzOAAYMp2yapORH3dQB3W18
    +     * 类型:String(32)
    +     * 字段说明:领取红包的openid
    +     * 
    + */ + @XStreamAlias("openid") + private String openid; + + /** + *
    +     * 字段含义:金额.
    +     * 字段名: amount
    +     * 是否必填:是
    +     * 示例值:100
    +     * 类型:int
    +     * 字段说明:领取金额
    +     * 
    + */ + @XStreamAlias("amount") + private Integer amount; + + /** + *
    +     * 字段含义:接收时间.
    +     * 字段名: rcv_time
    +     * 是否必填:是
    +     * 示例值:2015-04-21 20:00:00
    +     * 类型:String(32)
    +     * 字段说明:领取红包的时间
    +     * 
    + */ + @XStreamAlias("rcv_time") + private String receiveTime; } - public void setReason(String reason) { - this.reason = reason; - } - - public String getSendTime() { - return sendTime; - } - - public void setSendTime(String sendTime) { - this.sendTime = sendTime; - } - - public String getRefundTime() { - return refundTime; - } - - public void setRefundTime(String refundTime) { - this.refundTime = refundTime; - } - - public Integer getRefundAmount() { - return refundAmount; - } - - public void setRefundAmount(Integer refundAmount) { - this.refundAmount = refundAmount; - } - - public String getWishing() { - return wishing; - } - - public void setWishing(String wishing) { - this.wishing = wishing; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public String getActName() { - return actName; - } - - public void setActName(String actName) { - this.actName = actName; - } - - public String getHblist() { - return hblist; - } - - public void setHblist(String hblist) { - this.hblist = hblist; - } - - public String getOpenid() { - return openid; - } - - public void setOpenid(String openid) { - this.openid = openid; - } - - public Integer getAmount() { - return amount; - } - - public void setAmount(Integer amount) { - this.amount = amount; - } - - public String getReceiveTime() { - return receiveTime; - } - - public void setReceiveTime(String receiveTime) { - this.receiveTime = receiveTime; - } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundCouponInfo.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundCouponInfo.java new file mode 100644 index 0000000000..5db5c0581f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundCouponInfo.java @@ -0,0 +1,64 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
    + *  退款代金券信息.
    + *  Created by BinaryWang on 2018/4/21.
    + * 
    + * + * @author Binary Wang + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WxPayRefundCouponInfo implements Serializable { + private static final long serialVersionUID = -8493640819570138722L; + /** + *
    +   * 字段名:退款代金券ID.
    +   * 变量名:coupon_refund_id_$n_$m
    +   * 是否必填:否
    +   * 类型:String(20)
    +   * 示例值:10000
    +   * 描述:退款代金券ID, $n为下标,$m为下标,从0开始编号
    +   * 
    + */ + @XStreamAlias("coupon_refund_id") + private String couponRefundId; + + /** + *
    +   * 字段名:单个退款代金券支付金额.
    +   * 变量名:coupon_refund_fee_$n_$m
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:单个退款代金券支付金额, $n为下标,$m为下标,从0开始编号
    +   * 
    + */ + @XStreamAlias("coupon_refund_fee") + private Integer couponRefundFee; + + /** + *
    +   * 字段名:代金券类型.
    +   * 变量名:coupon_type_$n_$m
    +   * 是否必填:否
    +   * 类型:String(8)
    +   * 示例值:CASH
    +   * 描述:CASH--充值代金券 , NO_CASH---非充值代金券。
    +   * 开通免充值券功能,并且订单使用了优惠券后有返回(取值:CASH、NO_CASH)。
    +   * $n为下标,$m为下标,从0开始编号,举例:coupon_type_$0_$1
    +   * 
    + */ + @XStreamAlias("coupon_type") + private String couponType; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundPromotionDetail.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundPromotionDetail.java new file mode 100644 index 0000000000..414560b936 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundPromotionDetail.java @@ -0,0 +1,117 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 营销详情 . + * + * @author Binary Wang + * @date 2020-06-07 + */ +@Data +public class WxPayRefundPromotionDetail implements Serializable { + private static final long serialVersionUID = 2197712244944584263L; + + /** + * 字段名:券ID + * 变量名:promotion_id + * 是否必填:是 + * 类型:String(32) + * 示例例:109519 + * 描述:券或者立减优惠id + */ + @SerializedName("promotion_id") + private String promotionId; + /** + * 字段名:优惠范围 + * 变量名:scope + * 是否必填:是 + * 类型:String(32) + * 示例例:SINGLE + * 描述:GLOBAL- 全场代金券,SINGLE- 单品优惠 + */ + @SerializedName("scope") + private String scope; + /** + * 字段名:优惠类型 + * 变量名:type + * 是否必填:是 + * 类型:String(32) + * 示例例:DISCOUNT + * 描述:COUPON- 代金券,需要走结算资金的充值型代金券,(境外商户券币种与支付币种一致),DISCOUNT- 优惠券,不走结算资金的免充值型优惠券,(境外商户券币种与标价币种一致 + */ + @SerializedName("type") + private String type; + /** + * 字段名:代金券退款金额 + * 变量名:refund_amount + * 是否必填:是 + * 类型:Int + * 示例例:100 + * 描述:代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠 + */ + @SerializedName("refund_amount") + private Integer refundAmount; + /** + * 字段名:商品列表 + * 变量名:goods_detail + * 是否必填:否 + * 类型:String + * 示例例:见下文 + * 描述:商品信息,使用Json格式 + */ + @SerializedName("goods_detail") + private List goodsDetails; + + @Data + public static class GoodDetail { + /** + * 字段名:商品编码 + * 变量名:goods_id + * 是否必填:是 + * 类型:String(32) + * 示例值:商品编码 + * 描述:由半角的大小写字母、数字、中划线、下划线中的一种或几种组成 + */ + @SerializedName("goods_id") + private String goodsId; + + /** + * 字段名:优惠退款金额 + * 变量名:refund_amount + * 是否必填:是 + * 类型:int + * 示例值:528800 + * 描述:优惠退款金额 + */ + @SerializedName("refund_amount") + private Integer refundAmount; + + /** + * 字段名:商品退货数量 + * 变量名:refund_quantity + * 是否必填:是 + * 类型:int + * 示例值:1 + * 描述:单品的退货数量 + */ + @SerializedName("refund_quantity") + private Integer refundQuantity; + + /** + * 字段名:商品单价 + * 变量名:price + * 是否必填:是 + * 类型:int + * 示例值:528800 + * 描述:单位为:分。如果商户有优惠,需传输商户优惠后的单价(例如:用户对一笔100元的订单使用了商场发的优惠券100-50,则活动商品的单价应为原单价-50) + */ + @SerializedName("price") + private Integer price; + + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java index 1916fdb9e7..6a2d3b46d6 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java @@ -1,190 +1,168 @@ package com.github.binarywang.wxpay.bean.result; import com.google.common.collect.Lists; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.Document; +import java.io.Serializable; import java.util.List; /** *
    + * 微信支付-退款查询返回结果
      * Created by Binary Wang on 2016-11-24.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayRefundQueryResult extends WxPayBaseResult { +public class WxPayRefundQueryResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 5392369423225328754L; /** *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * String(32)
    -   * 013467007045764
    -   * 终端设备号
    +   * 字段名:设备号.
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:013467007045764
    +   * 描述:终端设备号
    +   * 
    */ @XStreamAlias("device_info") private String deviceInfo; /** *
    -   * 微信订单号
    -   * transaction_id
    -   * 是
    -   * String(32)
    -   * 1217752501201407033233368018
    -   * 微信订单号
    +   * 字段名:微信订单号.
    +   * 变量名:transaction_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1217752501201407033233368018
    +   * 描述:微信订单号
    +   * 
    */ @XStreamAlias("transaction_id") private String transactionId; /** *
    -   * 商户订单号
    -   * out_trade_no
    -   * 是
    -   * String(32)
    -   * 1217752501201407033233368018
    -   * 商户系统内部的订单号
    +   * 字段名:商户订单号.
    +   * 变量名:out_trade_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1217752501201407033233368018
    +   * 描述:商户系统内部的订单号
    +   * 
    */ @XStreamAlias("out_trade_no") private String outTradeNo; /** *
    -   * 订单金额
    -   * total_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 订单总金额,单位为分,只能为整数,详见支付金额
    +   * 字段名:订单金额.
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:订单总金额,单位为分,只能为整数,详见支付金额
    +   * 
    */ @XStreamAlias("total_fee") private Integer totalFee; /** *
    -   * 应结订单金额
    -   * settlement_total_fee
    -   * 否
    -   * Int
    -   * 100
    -   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +   * 字段名:应结订单金额.
    +   * 变量名:settlement_total_fee
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +   * 
    */ @XStreamAlias("settlement_total_fee") private Integer settlementTotalFee; /** *
    -   * 货币种类
    -   * fee_type
    -   * 否
    -   * String(8)
    -   * CNY
    -   * 订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 字段名:货币种类.
    +   * 变量名:fee_type
    +   * 是否必填:否
    +   * 类型:String(8)
    +   * 示例值:CNY
    +   * 描述:订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    */ @XStreamAlias("fee_type") private String feeType; /** *
    -   * 现金支付金额
    -   * cash_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 现金支付金额,单位为分,只能为整数,详见支付金额
    +   * 字段名:现金支付金额.
    +   * 变量名:cash_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:现金支付金额,单位为分,只能为整数,详见支付金额
    +   * 
    */ @XStreamAlias("cash_fee") private Integer cashFee; /** *
    -   * 退款笔数
    -   * refund_count
    -   * 是
    -   * Int
    -   * 1
    -   * 退款记录数
    +   * 字段名:退款笔数.
    +   * 变量名:refund_count
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:1
    +   * 描述:退款记录数
    +   * 
    */ @XStreamAlias("refund_count") private Integer refundCount; private List refundRecords; - public String getDeviceInfo() { - return deviceInfo; - } - - public void setDeviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - } - - public String getTransactionId() { - return transactionId; - } - - public void setTransactionId(String transactionId) { - this.transactionId = transactionId; - } - - public String getOutTradeNo() { - return outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public Integer getTotalFee() { - return totalFee; - } - - public void setTotalFee(Integer totalFee) { - this.totalFee = totalFee; - } - - public Integer getSettlementTotalFee() { - return settlementTotalFee; - } - - public void setSettlementTotalFee(Integer settlementTotalFee) { - this.settlementTotalFee = settlementTotalFee; - } - - public String getFeeType() { - return feeType; - } - - public void setFeeType(String feeType) { - this.feeType = feeType; - } + /** + * 营销详情. + */ + @XStreamAlias("promotion_detail") + private String promotionDetailString; - public Integer getCashFee() { - return cashFee; - } + private List promotionDetails; - public void setCashFee(Integer cashFee) { - this.cashFee = cashFee; - } + /** + * 组装生成营销详情信息. + */ + public void composePromotionDetails() { + if (StringUtils.isEmpty(this.promotionDetailString)) { + return; + } - public Integer getRefundCount() { - return refundCount; - } + JsonObject tmpJson = GsonParser.parse(this.promotionDetailString); - public void setRefundCount(Integer refundCount) { - this.refundCount = refundCount; - } + final List promotionDetail = WxGsonBuilder.create() + .fromJson(tmpJson.get("promotion_detail"), + new TypeToken>() { + }.getType() + ); - public List getRefundRecords() { - return refundRecords; + this.setPromotionDetails(promotionDetail); } - public void setRefundRecords(List refundRecords) { - this.refundRecords = refundRecords; - } /** - * 组装生成退款记录属性的内容 + * 组装生成退款记录属性的内容. */ public void composeRefundRecords() { if (this.refundCount != null && this.refundCount > 0) { @@ -199,39 +177,66 @@ public void composeRefundRecords() { refundRecord.setRefundChannel(this.getXmlValue("xml/refund_channel_" + i)); refundRecord.setRefundFee(this.getXmlValueAsInt("xml/refund_fee_" + i)); refundRecord.setSettlementRefundFee(this.getXmlValueAsInt("xml/settlement_refund_fee_" + i)); - refundRecord.setCouponType(this.getXmlValue("xml/coupon_type_" + i)); refundRecord.setCouponRefundFee(this.getXmlValueAsInt("xml/coupon_refund_fee_" + i)); refundRecord.setCouponRefundCount(this.getXmlValueAsInt("xml/coupon_refund_count_" + i)); refundRecord.setRefundStatus(this.getXmlValue("xml/refund_status_" + i)); - refundRecord.setRefundRecvAccout(this.getXmlValue("xml/refund_recv_accout_" + i)); + refundRecord.setRefundRecvAccount(this.getXmlValue("xml/refund_recv_accout_" + i)); + refundRecord.setRefundSuccessTime(this.getXmlValue("xml/refund_success_time_" + i)); if (refundRecord.getCouponRefundCount() == null || refundRecord.getCouponRefundCount() == 0) { continue; } - List coupons = Lists.newArrayList(); + List coupons = Lists.newArrayList(); for (int j = 0; j < refundRecord.getCouponRefundCount(); j++) { coupons.add( - new RefundRecord.RefundCoupon( + new WxPayRefundCouponInfo( this.getXmlValue("xml/coupon_refund_id_" + i + "_" + j), - this.getXmlValueAsInt("xml/coupon_refund_fee_" + i + "_" + j) + this.getXmlValueAsInt("xml/coupon_refund_fee_" + i + "_" + j), + this.getXmlValue("xml/coupon_type_" + i + "_" + j) ) ); } + + refundRecord.setRefundCoupons(coupons); } } } + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + deviceInfo = readXmlString(d, "device_info"); + transactionId = readXmlString(d, "transaction_id"); + outTradeNo = readXmlString(d, "out_trade_no"); + totalFee = readXmlInteger(d, "total_fee"); + settlementTotalFee = readXmlInteger(d, "settlement_total_fee"); + feeType = readXmlString(d, "fee_type"); + cashFee = readXmlInteger(d, "cash_fee"); + refundCount = readXmlInteger(d, "refund_count"); + } + + /** + * The type Refund record. + */ + @Data + @Builder(builderMethodName = "newBuilder") + @NoArgsConstructor + @AllArgsConstructor public static class RefundRecord { /** *
    -     * 商户退款单号
    -     * out_refund_no_$n
    -     * 是
    -     * String(32)
    -     * 1217752501201407033233368018
    -     * 商户退款单号
    +     * 字段名:商户退款单号.
    +     * 变量名:out_refund_no_$n
    +     * 是否必填:是
    +     * 类型:String(32)
    +     * 示例值:1217752501201407033233368018
    +     * 描述:商户退款单号
          * 
    */ @XStreamAlias("out_refund_no") @@ -239,12 +244,12 @@ public static class RefundRecord { /** *
    -     * 微信退款单号
    -     * refund_id_$n
    -     * 是
    -     * String(28)
    -     * 1217752501201407033233368018
    -     * 微信退款单号
    +     * 字段名:微信退款单号.
    +     * 变量名:refund_id_$n
    +     * 是否必填:是
    +     * 类型:String(28)
    +     * 示例值:1217752501201407033233368018
    +     * 描述:微信退款单号
          * 
    */ @XStreamAlias("refund_id") @@ -252,12 +257,12 @@ public static class RefundRecord { /** *
    -     * 退款渠道
    -     * refund_channel_$n
    -     * 否
    -     * String(16)
    -     * ORIGINAL
    -     * ORIGINAL—原路退款 BALANCE—退回到余额
    +     * 字段名:退款渠道.
    +     * 变量名:refund_channel_$n
    +     * 是否必填:否
    +     * 类型:String(16)
    +     * 示例值:ORIGINAL
    +     * 描述:ORIGINAL—原路退款 BALANCE—退回到余额
          * 
    */ @XStreamAlias("refund_channel") @@ -265,12 +270,12 @@ public static class RefundRecord { /** *
    -     * 申请退款金额
    -     * refund_fee_$n
    -     * 是
    -     * Int
    -     * 100
    -     * 退款总金额,单位为分,可以做部分退款
    +     * 字段名:申请退款金额.
    +     * 变量名:refund_fee_$n
    +     * 是否必填:是
    +     * 类型:Int
    +     * 示例值:100
    +     * 描述:退款总金额,单位为分,可以做部分退款
          * 
    */ @XStreamAlias("refund_fee") @@ -278,12 +283,12 @@ public static class RefundRecord { /** *
    -     * 退款金额
    -     * settlement_refund_fee_$n
    -     * 否
    -     * Int
    -     * 100
    -     * 退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
    +     * 字段名:退款金额.
    +     * 变量名:settlement_refund_fee_$n
    +     * 是否必填:否
    +     * 类型:Int
    +     * 示例值:100
    +     * 描述:退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
          * 
    */ @XStreamAlias("settlement_refund_fee") @@ -291,12 +296,12 @@ public static class RefundRecord { /** *
    -     * 退款资金来源
    -     * refund_account
    -     * 否
    -     * String(30)
    -     * REFUND_SOURCE_RECHARGE_FUNDS
    -     * REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款/基本账户, REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款
    +     * 字段名:退款资金来源.
    +     * 变量名:refund_account
    +     * 是否必填:否
    +     * 类型:String(30)
    +     * 示例值:REFUND_SOURCE_RECHARGE_FUNDS
    +     * 描述:REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款/基本账户, REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款
          * 
    */ @XStreamAlias("refund_account") @@ -304,25 +309,12 @@ public static class RefundRecord { /** *
    -     * 代金券类型
    -     * coupon_type_$n
    -     * 否
    -     * Int
    -     * CASH
    -     * CASH--充值代金券 , NO_CASH---非充值代金券。订单使用代金券时有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_$0
    -     * 
    - */ - @XStreamAlias("coupon_type") - private String couponType; - - /** - *
    -     * 代金券退款金额
    -     * coupon_refund_fee_$n
    -     * 否
    -     * Int
    -     * 100
    -     * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
    +     * 字段名:代金券退款金额.
    +     * 变量名:coupon_refund_fee_$n
    +     * 是否必填:否
    +     * 类型:Int
    +     * 示例值:100
    +     * 描述:代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
          * 
    */ @XStreamAlias("coupon_refund_fee") @@ -330,27 +322,27 @@ public static class RefundRecord { /** *
    -     * 退款代金券使用数量
    -     * coupon_refund_count_$n
    -     * 否
    -     * Int
    -     * 1
    -     * 退款代金券使用数量 ,$n为下标,从0开始编号
    +     * 字段名:退款代金券使用数量.
    +     * 变量名:coupon_refund_count_$n
    +     * 是否必填:否
    +     * 类型:Int
    +     * 示例值:1
    +     * 描述:退款代金券使用数量 ,$n为下标,从0开始编号
          * 
    */ @XStreamAlias("coupon_refund_count") private Integer couponRefundCount; - private List refundCoupons; + private List refundCoupons; /** *
    -     * 退款状态
    -     * refund_status_$n
    -     * 是
    -     * String(16)
    -     * SUCCESS
    -     * 退款状态:
    +     * 字段名:退款状态.
    +     * 变量名:refund_status_$n
    +     * 是否必填:是
    +     * 类型:String(16)
    +     * 示例值:SUCCESS
    +     * 描述:退款状态:
          *  SUCCESS—退款成功,
          *  FAIL—退款失败,
          *  PROCESSING—退款处理中,
    @@ -360,169 +352,32 @@ public static class RefundRecord {
          */
         @XStreamAlias("refund_status")
         private String refundStatus;
    +
         /**
          * 
    -     * 退款入账账户
    -     * refund_recv_accout_$n
    -     * 是
    -     * String(64)
    -     * 招商银行信用卡0403
    -     * 取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱
    +     * 字段名:退款入账账户.
    +     * 变量名:refund_recv_accout_$n
    +     * 是否必填:是
    +     * 类型:String(64)
    +     * 示例值:招商银行信用卡0403
    +     * 描述:取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱
          * 
    */ @XStreamAlias("refund_recv_accout") - private String refundRecvAccout; - - public String getOutRefundNo() { - return outRefundNo; - } - - public void setOutRefundNo(String outRefundNo) { - this.outRefundNo = outRefundNo; - } - - public String getRefundId() { - return refundId; - } - - public void setRefundId(String refundId) { - this.refundId = refundId; - } - - public String getRefundChannel() { - return refundChannel; - } - - public void setRefundChannel(String refundChannel) { - this.refundChannel = refundChannel; - } - - public Integer getRefundFee() { - return refundFee; - } - - public void setRefundFee(Integer refundFee) { - this.refundFee = refundFee; - } - - public Integer getSettlementRefundFee() { - return settlementRefundFee; - } + private String refundRecvAccount; - public void setSettlementRefundFee(Integer settlementRefundFee) { - this.settlementRefundFee = settlementRefundFee; - } - - public String getRefundAccount() { - return refundAccount; - } - - public void setRefundAccount(String refundAccount) { - this.refundAccount = refundAccount; - } - - public String getCouponType() { - return couponType; - } - - public void setCouponType(String couponType) { - this.couponType = couponType; - } - - public Integer getCouponRefundFee() { - return couponRefundFee; - } - - public void setCouponRefundFee(Integer couponRefundFee) { - this.couponRefundFee = couponRefundFee; - } - - public Integer getCouponRefundCount() { - return couponRefundCount; - } - - public void setCouponRefundCount(Integer couponRefundCount) { - this.couponRefundCount = couponRefundCount; - } - - public List getRefundCoupons() { - return refundCoupons; - } - - public void setRefundCoupons(List refundCoupons) { - this.refundCoupons = refundCoupons; - } - - public String getRefundStatus() { - return refundStatus; - } - - public void setRefundStatus(String refundStatus) { - this.refundStatus = refundStatus; - } - - public String getRefundRecvAccout() { - return refundRecvAccout; - } - - public void setRefundRecvAccout(String refundRecvAccout) { - this.refundRecvAccout = refundRecvAccout; - } - - public static class RefundCoupon { - /** - *
    -       * 退款代金券批次ID
    -       * coupon_refund_batch_id_$n_$m
    -       * 否
    -       * String(20)
    -       * 100
    -       * 退款代金券批次ID ,$n为下标,$m为下标,从0开始编号
    -       * 
    - * - * @deprecated 貌似是被去掉了,但不知是何时! - */ - @XStreamAlias("coupon_refund_batch_id") - private String couponRefundBatchId; - - /** - *
    -       * 退款代金券ID
    -       * coupon_refund_id_$n_$m
    -       * 否
    -       * String(20)
    -       * 10000
    -       * 退款代金券ID, $n为下标,$m为下标,从0开始编号
    -       * 
    - */ - @XStreamAlias("coupon_refund_id") - private String couponRefundId; - - /** - *
    -       * 单个退款代金券支付金额
    -       * coupon_refund_fee_$n_$m
    -       * 否
    -       * Int
    -       * 100
    -       * 单个退款代金券支付金额, $n为下标,$m为下标,从0开始编号
    -       * 
    - */ - @XStreamAlias("coupon_refund_fee") - private Integer couponRefundFee; - - public RefundCoupon(String couponRefundId, Integer couponRefundFee) { - this.couponRefundId = couponRefundId; - this.couponRefundFee = couponRefundFee; - } - - @Deprecated - public RefundCoupon(String couponRefundBatchId, String couponRefundId, Integer couponRefundFee) { - this.couponRefundBatchId = couponRefundBatchId; - this.couponRefundId = couponRefundId; - this.couponRefundFee = couponRefundFee; - } - } + /** + *
    +     * 字段名:退款成功时间.
    +     * 变量名:refund_success_time_$n
    +     * 是否必填:否
    +     * 类型:String(20)
    +     * 示例值:2016-07-25 15:26:26
    +     * 描述:退款成功时间,当退款状态为退款成功时有返回。$n为下标,从0开始编号。
    +     * 
    + */ + @XStreamAlias("refund_success_time") + private String refundSuccessTime; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java index 1e6126b210..6b4de3d639 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java @@ -1,173 +1,199 @@ package com.github.binarywang.wxpay.bean.result; +import com.google.common.collect.Lists; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.Document; import java.io.Serializable; +import java.util.List; /** *
    - * 微信支付-申请退款返回结果
    + * 微信支付-申请退款返回结果.
      * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
      * 
    * - * @author liukaitj + * @author liukaitj & Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayRefundResult extends WxPayBaseResult implements Serializable { - private static final long serialVersionUID = 1L; - - @XStreamAlias("device_info") - private String deviceInfo; - +public class WxPayRefundResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -3392333879907788033L; + /** + * 微信订单号. + */ @XStreamAlias("transaction_id") private String transactionId; + /** + * 商户订单号. + */ @XStreamAlias("out_trade_no") private String outTradeNo; + /** + * 商户退款单号. + */ @XStreamAlias("out_refund_no") private String outRefundNo; + /** + * 微信退款单号. + */ @XStreamAlias("refund_id") private String refundId; - @XStreamAlias("refund_channel") - private String refundChannel; - + /** + * 退款金额. + */ @XStreamAlias("refund_fee") - private String refundFee; + private Integer refundFee; + /** + * 应结退款金额. + */ + @XStreamAlias("settlement_refund_fee") + private Integer settlementRefundFee; + + /** + * 标价金额. + */ @XStreamAlias("total_fee") - private String totalFee; + private Integer totalFee; + + /** + * 应结订单金额. + */ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + /** + * 标价币种. + */ @XStreamAlias("fee_type") private String feeType; + /** + * 现金支付金额. + */ @XStreamAlias("cash_fee") - private String cashFee; + private Integer cashFee; - @XStreamAlias("cash_refund_fee") - private String cashRefundFee; + /** + * 现金支付币种. + */ + @XStreamAlias("cash_fee_type") + private String cashFeeType; - @XStreamAlias("coupon_refund_fee") - private String couponRefundFee; + /** + * 现金退款金额,单位为分,只能为整数,详见支付金额. + */ + @XStreamAlias("cash_refund_fee") + private Integer cashRefundFee; + /** + * 退款代金券使用数量. + */ @XStreamAlias("coupon_refund_count") - private String couponRefundCount; - - @XStreamAlias("coupon_refund_id") - private String couponRefundId; - - public String getDeviceInfo() { - return this.deviceInfo; - } - - public void setDeviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; - } - - public String getTransactionId() { - return this.transactionId; - } - - public void setTransactionId(String transactionId) { - this.transactionId = transactionId; - } - - public String getOutTradeNo() { - return this.outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public String getOutRefundNo() { - return this.outRefundNo; - } - - public void setOutRefundNo(String outRefundNo) { - this.outRefundNo = outRefundNo; - } - - public String getRefundId() { - return this.refundId; - } - - public void setRefundId(String refundId) { - this.refundId = refundId; - } - - public String getRefundChannel() { - return this.refundChannel; - } - - public void setRefundChannel(String refundChannel) { - this.refundChannel = refundChannel; - } - - public String getRefundFee() { - return this.refundFee; - } - - public void setRefundFee(String refundFee) { - this.refundFee = refundFee; - } - - public String getTotalFee() { - return this.totalFee; - } - - public void setTotalFee(String totalFee) { - this.totalFee = totalFee; - } - - public String getFeeType() { - return this.feeType; - } - - public void setFeeType(String feeType) { - this.feeType = feeType; - } - - public String getCashFee() { - return this.cashFee; - } - - public void setCashFee(String cashFee) { - this.cashFee = cashFee; - } - - public String getCashRefundFee() { - return this.cashRefundFee; - } - - public void setCashRefundFee(String cashRefundFee) { - this.cashRefundFee = cashRefundFee; - } - - public String getCouponRefundFee() { - return this.couponRefundFee; - } - - public void setCouponRefundFee(String couponRefundFee) { - this.couponRefundFee = couponRefundFee; - } - - public String getCouponRefundCount() { - return this.couponRefundCount; - } - - public void setCouponRefundCount(String couponRefundCount) { - this.couponRefundCount = couponRefundCount; - } - - public String getCouponRefundId() { - return this.couponRefundId; - } - - public void setCouponRefundId(String couponRefundId) { - this.couponRefundId = couponRefundId; + private Integer couponRefundCount; + + /** + *
    +   * 字段名:代金券退款总金额.
    +   * 变量名:coupon_refund_fee
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
    +   * 
    + */ + @XStreamAlias("coupon_refund_fee") + private Integer couponRefundFee; + + /** + * 营销详情. + */ + @XStreamAlias("promotion_detail") + private String promotionDetailString; + + private List promotionDetails; + + private List refundCoupons; + + /** + * 组装生成营销详情信息. + */ + public void composePromotionDetails() { + if (StringUtils.isEmpty(this.promotionDetailString)) { + return; + } + + JsonObject tmpJson = GsonParser.parse(this.promotionDetailString); + + final List promotionDetail = WxGsonBuilder.create() + .fromJson(tmpJson.get("promotion_detail"), + new TypeToken>() { + }.getType() + ); + + this.setPromotionDetails(promotionDetail); + } + + /** + * 组装生成退款代金券信息. + */ + public void composeRefundCoupons() { + List coupons = Lists.newArrayList(); + Integer refundCount = this.getCouponRefundCount(); + if (refundCount == null) { + //无退款代金券信息 + return; + } + + for (int i = 0; i < refundCount; i++) { + coupons.add( + new WxPayRefundCouponInfo( + this.getXmlValue("xml/coupon_refund_id_" + i), + this.getXmlValueAsInt("xml/coupon_refund_fee_" + i), + this.getXmlValue("xml/coupon_type_" + i) + ) + ); + } + + this.setRefundCoupons(coupons); + } + + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + transactionId = readXmlString(d, "transaction_id"); + outTradeNo = readXmlString(d, "out_trade_no"); + outRefundNo = readXmlString(d, "out_refund_no"); + refundId = readXmlString(d, "refund_id"); + refundFee = readXmlInteger(d, "refund_fee"); + settlementRefundFee = readXmlInteger(d, "settlement_refund_fee"); + totalFee = readXmlInteger(d, "total_fee"); + settlementTotalFee = readXmlInteger(d, "settlement_total_fee"); + feeType = readXmlString(d, "fee_type"); + cashFee = readXmlInteger(d, "cash_fee"); + cashFeeType = readXmlString(d, "cash_fee_type"); + cashRefundFee = readXmlInteger(d, "cash_refund_fee"); + couponRefundCount = readXmlInteger(d, "coupon_refund_count"); + couponRefundFee = readXmlInteger(d, "coupon_refund_fee"); } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java new file mode 100644 index 0000000000..aec190364c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java @@ -0,0 +1,47 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; + +/** + *
    + *  Created by BinaryWang on 2017/6/18.
    + * 
    + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxPaySandboxSignKeyResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -5793375529340378941L; + /** + *
    +   * 沙箱密钥
    +   * sandbox_signkey
    +   * 否
    +   * 013467007045764
    +   * String(32)
    +   * 返回的沙箱密钥
    +   * 
    + */ + @XStreamAlias("sandbox_signkey") + private String sandboxSignKey; + + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + sandboxSignKey = readXmlString(d, "sandbox_signkey"); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendMiniProgramRedpackResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendMiniProgramRedpackResult.java new file mode 100644 index 0000000000..f461e27b6d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendMiniProgramRedpackResult.java @@ -0,0 +1,67 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; + +/** + * 发送小程序红包的返回结果 + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class WxPaySendMiniProgramRedpackResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 5847928569755121611L; + /** + * 商户订单号. + */ + @XStreamAlias("mch_billno") + private String mchBillNo; + + /** + * 公众账号appid. + */ + @XStreamAlias("wxappid") + private String wxAppid; + + /** + * 用户openid. + */ + @XStreamAlias("re_openid") + private String reOpenid; + + /** + * 付款金额. + */ + @XStreamAlias("total_amount") + private int totalAmount; + + /** + * 返回jaspi的入参package的值. + */ + @XStreamAlias("package") + private String packageName; + + /** + * 微信单号. + */ + @XStreamAlias("send_listid") + private String sendListId; + + @Override + protected void loadXml(Document d) { + mchBillNo = readXmlString(d, "mch_billno"); + wxAppid = readXmlString(d, "wxappid"); + reOpenid = readXmlString(d, "re_openid"); + totalAmount = readXmlInteger(d, "total_amount"); + packageName = readXmlString(d, "package"); + sendListId = readXmlString(d, "send_listid"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java index 868f089b74..2002fd5b1e 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java @@ -1,82 +1,70 @@ package com.github.binarywang.wxpay.bean.result; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; import java.io.Serializable; /** * 向微信用户个人发现金红包返回结果 - * https://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5 + * https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 * * @author kane */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPaySendRedpackResult extends WxPayBaseResult implements Serializable { +public class WxPaySendRedpackResult extends BaseWxPayResult implements Serializable { private static final long serialVersionUID = -4837415036337132073L; + private static final String PROCESSING = "PROCESSING"; @XStreamAlias("mch_billno") - private String mchBillno; + private String mchBillNo; @XStreamAlias("wxappid") - private String wxappid; + private String wxAppId; @XStreamAlias("re_openid") private String reOpenid; @XStreamAlias("total_amount") - private int totalAmount; + private Integer totalAmount; @XStreamAlias("send_time") private String sendTime; @XStreamAlias("send_listid") - private String sendListid; - - public String getMchBillno() { - return this.mchBillno; - } - - public void setMchBillno(String mchBillno) { - this.mchBillno = mchBillno; - } - - public String getWxappid() { - return this.wxappid; - } - - public void setWxappid(String wxappid) { - this.wxappid = wxappid; - } - - public String getReOpenid() { - return this.reOpenid; - } - - public void setReOpenid(String reOpenid) { - this.reOpenid = reOpenid; - } - - public int getTotalAmount() { - return this.totalAmount; - } - - public void setTotalAmount(int totalAmount) { - this.totalAmount = totalAmount; - } - - public String getSendTime() { - return this.sendTime; - } - - public void setSendTime(String sendTime) { - this.sendTime = sendTime; - } - - public String getSendListid() { - return this.sendListid; + private String sendListId; + + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + mchBillNo = readXmlString(d, "mch_billno"); + wxAppId = readXmlString(d, "wxappid"); + reOpenid = readXmlString(d, "re_openid"); + totalAmount = readXmlInteger(d, "total_amount"); + sendTime = readXmlString(d, "send_time"); + sendListId = readXmlString(d, "send_listid"); } - public void setSendListid(String sendListid) { - this.sendListid = sendListid; + @Override + public void checkResult(WxPayService wxPayService, String signType, boolean checkSuccess) throws WxPayException { + try { + super.checkResult(wxPayService, signType, checkSuccess); + } catch (WxPayException e) { + if (!PROCESSING.equals(e.getErrCode())) { + throw e; + } + } } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java index 2f93cb81bf..da1c97d7d5 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java @@ -1,16 +1,27 @@ package com.github.binarywang.wxpay.bean.result; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; /** *
      * 转换短链接结果对象类
      * Created by Binary Wang on 2017-3-27.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author Binary Wang */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayShorturlResult extends WxPayBaseResult { +public class WxPayShorturlResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -2121902492357304418L; /** *
        * URL链接
    @@ -24,11 +35,14 @@ public class WxPayShorturlResult extends WxPayBaseResult {
       @XStreamAlias("short_url")
       private String shortUrl;
     
    -  public String getShortUrl() {
    -    return this.shortUrl;
    +  /**
    +   * 从XML结构中加载额外的熟悉
    +   *
    +   * @param d Document
    +   */
    +  @Override
    +  protected void loadXml(Document d) {
    +    shortUrl = readXmlString(d, "short_url");
       }
     
    -  public void setShortUrl(String shortUrl) {
    -    this.shortUrl = shortUrl;
    -  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
    index f6d82bd184..eec2e37fed 100644
    --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
    @@ -1,17 +1,27 @@
     package com.github.binarywang.wxpay.bean.result;
     
     import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import lombok.Data;
    +import lombok.EqualsAndHashCode;
    +import lombok.NoArgsConstructor;
    +import org.w3c.dom.Document;
    +
    +import java.io.Serializable;
     
     /**
      * 
      * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果
    - * 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
    + * 统一下单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
      * 
    * * @author chanjarster */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor @XStreamAlias("xml") -public class WxPayUnifiedOrderResult extends WxPayBaseResult { +public class WxPayUnifiedOrderResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = -4006038483273621997L; /** * 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 @@ -25,33 +35,29 @@ public class WxPayUnifiedOrderResult extends WxPayBaseResult { @XStreamAlias("trade_type") private String tradeType; + /** + * mweb_url 支付跳转链接 + */ + @XStreamAlias("mweb_url") + private String mwebUrl; + /** * trade_type为NATIVE时有返回,用于生成二维码,展示给用户进行扫码支付 */ @XStreamAlias("code_url") private String codeURL; - public String getPrepayId() { - return this.prepayId; - } - - public void setPrepayId(String prepayId) { - this.prepayId = prepayId; - } - - public String getTradeType() { - return this.tradeType; - } - - public void setTradeType(String tradeType) { - this.tradeType = tradeType; - } - - public String getCodeURL() { - return this.codeURL; + /** + * 从XML结构中加载额外的熟悉 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + prepayId = readXmlString(d, "prepay_id"); + tradeType = readXmlString(d, "trade_type"); + mwebUrl = readXmlString(d, "mweb_url"); + codeURL = readXmlString(d, "code_url"); } - public void setCodeURL(String codeURL) { - this.codeURL = codeURL; - } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java deleted file mode 100644 index 5bf110688f..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.binarywang.wxpay.bean.result; - -import java.io.Serializable; - -import com.thoughtworks.xstream.annotations.XStreamAlias; - -public class WxScanPayNotifyResult extends WxPayBaseResult implements Serializable{ - private static final long serialVersionUID = 3381324564266118986L; - - /** - * 预支付ID - */ - @XStreamAlias("prepay_id") - private String prepayId; - - public String getPrepayId() { - return prepayId; - } - - public void setPrepayId(String prepayId) { - this.prepayId = prepayId; - } - -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java index c03b248264..2769e22018 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java @@ -1,157 +1,318 @@ package com.github.binarywang.wxpay.config; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.v3.WxPayV3HttpClientBuilder; +import com.github.binarywang.wxpay.v3.auth.*; +import com.github.binarywang.wxpay.v3.util.PemUtils; +import jodd.util.ResourcesUtil; +import lombok.Data; +import lombok.SneakyThrows; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.ssl.SSLContexts; import javax.net.ssl.SSLContext; -import java.io.File; -import java.io.FileInputStream; +import java.io.*; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.security.KeyStore; +import java.security.PrivateKey; +import java.util.Collections; /** * 微信支付配置 * * @author Binary Wang (https://github.com/binarywang) */ +@Data public class WxPayConfig { + private static final String DEFAULT_PAY_BASE_URL = "https://api.mch.weixin.qq.com"; + private static final String PROBLEM_MSG = "证书文件【%s】有问题,请核实!"; + private static final String NOT_FOUND_MSG = "证书文件【%s】不存在,请核实!"; + + /** + * 微信支付接口请求地址域名部分. + */ + private String payBaseUrl = DEFAULT_PAY_BASE_URL; + + /** + * http请求连接超时时间. + */ + private int httpConnectionTimeout = 5000; + + /** + * http请求数据读取等待时间. + */ + private int httpTimeout = 10000; + + /** + * 公众号appid. + */ private String appId; + /** + * 服务商模式下的子商户公众账号ID. + */ private String subAppId; + /** + * 商户号. + */ private String mchId; + /** + * 商户密钥. + */ private String mchKey; + /** + * 企业支付密钥. + */ + private String entPayKey; + /** + * 服务商模式下的子商户号. + */ private String subMchId; + /** + * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数. + */ private String notifyUrl; + /** + * 交易类型. + *
    +   * JSAPI--公众号支付
    +   * NATIVE--原生扫码支付
    +   * APP--app支付
    +   * 
    + */ private String tradeType; + /** + * 签名方式. + * 有两种HMAC_SHA256 和MD5 + * + * @see com.github.binarywang.wxpay.constant.WxPayConstants.SignType + */ + private String signType; private SSLContext sslContext; + /** + * p12证书文件的绝对路径或者以classpath:开头的类路径. + */ private String keyPath; - public void setNotifyUrl(String notifyUrl) { - this.notifyUrl = notifyUrl; - } - - public void setTradeType(String tradeType) { - this.tradeType = tradeType; - } - /** - * 设置证书 - * @param keyPath apiclient_cert.p12的文件的绝对路径 + * apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径. */ - public void setKeyPath(String keyPath) { - this.keyPath = keyPath; - } - - public String getKeyPath() { - return keyPath; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public void setSubAppId(String subAppId) { - this.subAppId = subAppId; - } - - public void setMchId(String mchId) { - this.mchId = mchId; - } - - public void setMchKey(String mchKey) { - this.mchKey = mchKey; - } + private String privateKeyPath; + /** + * apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径. + */ + private String privateCertPath; - public void setSubMchId(String subMchId) { - this.subMchId = subMchId; - } + /** + * apiV3 秘钥值. + */ + private String apiV3Key; - public void setSslContext(SSLContext sslContext) { - this.sslContext = sslContext; - } + /** + * apiV3 证书序列号值 + */ + private String certSerialNo; + /** + * 微信支付分serviceId + */ + private String serviceId; /** - * 商户号 + * 微信支付分回调地址 */ - public String getMchId() { - return this.mchId; - } + private String payScoreNotifyUrl; + private CloseableHttpClient apiV3HttpClient; /** - * 商户密钥 + * 私钥信息 */ - public String getMchKey() { - return this.mchKey; - } + private PrivateKey privateKey; /** - * 公众号appid + * 证书自动更新时间差(分钟),默认一分钟 */ - public String getAppId() { - return this.appId; - } + private int certAutoUpdateTime = 60; /** - * 服务商模式下的子商户公众账号ID + * p12证书文件内容的字节数组. */ - public String getSubAppId() { - return this.subAppId; - } + private byte[] keyContent; + /** + * 微信支付是否使用仿真测试环境. + * 默认不使用 + */ + private boolean useSandboxEnv = false; /** - * 服务商模式下的子商户号 + * 是否将接口请求日志信息保存到threadLocal中. + * 默认不保存 */ - public String getSubMchId() { - return this.subMchId; - } + private boolean ifSaveApiData = false; + + private String httpProxyHost; + private Integer httpProxyPort; + private String httpProxyUsername; + private String httpProxyPassword; /** - * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 + * v3接口下证书检验对象,通过改对象可以获取到X509Certificate,进一步对敏感信息加密 + * 文档见 https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/min-gan-xin-xi-jia-mi */ - public String getNotifyUrl() { - return this.notifyUrl; - } + private Verifier verifier; /** - * 交易类型 - *
    -   * JSAPI--公众号支付
    -   * NATIVE--原生扫码支付
    -   * APP--app支付
    -   * 
    + * 返回所设置的微信支付接口请求地址域名. + * + * @return 微信支付接口请求地址域名 */ - public String getTradeType() { - return this.tradeType; + public String getPayBaseUrl() { + if (StringUtils.isEmpty(this.payBaseUrl)) { + return DEFAULT_PAY_BASE_URL; + } + + return this.payBaseUrl; } - public SSLContext getSslContext() { - return this.sslContext; + @SneakyThrows + public Verifier getVerifier() { + if (verifier == null) { + //当改对象为null时,初始化api v3的请求头 + initApiV3HttpClient(); + } + return verifier; } /** - * 微信支付是否使用仿真测试环境 - * 默认不使用 + * 初始化ssl. + * + * @return the ssl context + * @throws WxPayException the wx pay exception */ - public boolean useSandboxForWxPay() { - return false; - } - - public SSLContext initSSLContext() { - if (null == mchId) { - throw new IllegalArgumentException("请确保商户号mch_id已设置"); + public SSLContext initSSLContext() throws WxPayException { + if (StringUtils.isBlank(this.getMchId())) { + throw new WxPayException("请确保商户号mchId已设置"); } - File file = new File(this.keyPath); - if (!file.exists()) { - throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!"); + InputStream inputStream; + if (this.keyContent != null) { + inputStream = new ByteArrayInputStream(this.keyContent); + } else { + if (StringUtils.isBlank(this.getKeyPath())) { + throw new WxPayException("请确保证书文件地址keyPath已配置"); + } + inputStream = this.loadConfigInputStream(this.getKeyPath()); } try { - FileInputStream inputStream = new FileInputStream(file); KeyStore keystore = KeyStore.getInstance("PKCS12"); - char[] partnerId2charArray = mchId.toCharArray(); + char[] partnerId2charArray = this.getMchId().toCharArray(); keystore.load(inputStream, partnerId2charArray); this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); return this.sslContext; } catch (Exception e) { - throw new RuntimeException("证书文件有问题,请核实!", e); + throw new WxPayException("证书文件有问题,请核实!", e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + /** + * 初始化api v3请求头 自动签名验签 + * 方法参照微信官方https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient + * + * @return org.apache.http.impl.client.CloseableHttpClient + * @author doger.wang + **/ + public CloseableHttpClient initApiV3HttpClient() throws WxPayException { + String privateKeyPath = this.getPrivateKeyPath(); + String privateCertPath = this.getPrivateCertPath(); + String certSerialNo = this.getCertSerialNo(); + String apiV3Key = this.getApiV3Key(); + if (StringUtils.isBlank(privateKeyPath)) { + throw new WxPayException("请确保privateKeyPath已设置"); + } + if (StringUtils.isBlank(privateCertPath)) { + throw new WxPayException("请确保privateCertPath已设置"); + } + if (StringUtils.isBlank(certSerialNo)) { + throw new WxPayException("请确保certSerialNo证书序列号已设置"); + } + if (StringUtils.isBlank(apiV3Key)) { + throw new WxPayException("请确保apiV3Key值已设置"); + } + + InputStream keyInputStream = this.loadConfigInputStream(privateKeyPath); + InputStream certInputStream = this.loadConfigInputStream(privateCertPath); + try { + PrivateKey merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream); + + AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( + new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)), + apiV3Key.getBytes(StandardCharsets.UTF_8), this.getCertAutoUpdateTime()); + + CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create() + .withMerchant(mchId, certSerialNo, merchantPrivateKey) + .withWechatpay(Collections.singletonList(PemUtils.loadCertificate(certInputStream))) + .withValidator(new WxPayValidator(verifier)) + .build(); + this.apiV3HttpClient = httpClient; + this.verifier=verifier; + this.privateKey = merchantPrivateKey; + + return httpClient; + } catch (Exception e) { + throw new WxPayException("v3请求构造异常!", e); + } + } + + /** + * 从配置路径 加载配置 信息(支持 classpath、本地路径、网络url) + * @param configPath 配置路径 + * @return + * @throws WxPayException + */ + private InputStream loadConfigInputStream(String configPath) throws WxPayException { + InputStream inputStream; + final String prefix = "classpath:"; + String fileHasProblemMsg = String.format(PROBLEM_MSG, configPath); + String fileNotFoundMsg = String.format(NOT_FOUND_MSG, configPath); + if (configPath.startsWith(prefix)) { + String path = RegExUtils.removeFirst(configPath, prefix); + if (!path.startsWith("/")) { + path = "/" + path; + } + try { + inputStream = ResourcesUtil.getResourceAsStream(path); + if (inputStream == null) { + throw new WxPayException(fileNotFoundMsg); + } + } catch (Exception e) { + throw new WxPayException(fileNotFoundMsg, e); + } + } else if (configPath.startsWith("http://") || configPath.startsWith("https://")) { + try { + inputStream = new URL(configPath).openStream(); + if (inputStream == null) { + throw new WxPayException(fileNotFoundMsg); + } + } catch (IOException e) { + throw new WxPayException(fileNotFoundMsg, e); + } + } else { + try { + File file = new File(configPath); + if (!file.exists()) { + throw new WxPayException(fileNotFoundMsg); + } + + inputStream = new FileInputStream(file); + } catch (IOException e) { + throw new WxPayException(fileHasProblemMsg, e); + } } + return inputStream; } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java new file mode 100644 index 0000000000..fbc499fedd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java @@ -0,0 +1,348 @@ +package com.github.binarywang.wxpay.constant; + +import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; +import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; +import com.google.common.collect.Lists; +import org.apache.commons.lang3.time.FastDateFormat; + +import java.text.Format; +import java.util.List; + +/** + *
    + * 微信支付常量类
    + * Created by Binary Wang on 2017-8-24.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayConstants { + + /** + * 拉取订单评价数据接口的参数中日期格式. + */ + public static final Format QUERY_COMMENT_DATE_FORMAT = FastDateFormat.getInstance("yyyyMMddHHmmss"); + + /** + * 校验用户姓名选项,企业付款时使用. + */ + public static class CheckNameOption { + /** + * 不校验真实姓名. + */ + public static final String NO_CHECK = "NO_CHECK"; + + /** + * 强校验真实姓名. + */ + public static final String FORCE_CHECK = "FORCE_CHECK"; + } + + /** + * 压缩账单的类型. + */ + public static class TarType { + /** + * 固定值:GZIP,返回格式为.gzip的压缩包账单. + */ + public static final String GZIP = "GZIP"; + } + + /** + * 账单类型. + */ + public static class BillType { + /** + * 查询红包时使用:通过商户订单号获取红包信息. + */ + public static final String MCHT = "MCHT"; + + //以下为下载对账单时的账单类型 + /** + * 返回当日所有订单信息,默认值. + */ + public static final String ALL = "ALL"; + /** + * 返回当日成功支付的订单. + */ + public static final String SUCCESS = "SUCCESS"; + /** + * 返回当日退款订单. + */ + public static final String REFUND = "REFUND"; + /** + * 返回当日充值退款订单(相比其他对账单多一栏“返还手续费”). + */ + public static final String RECHARGE_REFUND = "RECHARGE_REFUND"; + } + + /** + * 交易类型. + */ + public static class TradeType { + /** + * 原生扫码支付. + */ + public static final String NATIVE = "NATIVE"; + + /** + * App支付. + */ + public static final String APP = "APP"; + + /** + * 公众号支付/小程序支付. + */ + public static final String JSAPI = "JSAPI"; + + /** + * H5支付. + */ + public static final String MWEB = "MWEB"; + + /** + * 刷卡支付. + * 刷卡支付有单独的支付接口,不调用统一下单接口 + */ + public static final String MICROPAY = "MICROPAY"; + + @SuppressWarnings("unused") + public abstract static class Specific { + + public abstract String getType(); + + private Specific() { + } + + public static Specific NATIVE = + new Specific() { + @Override + public String getType() { + return TradeType.NATIVE; + } + }; + + public static Specific APP = + new Specific() { + @Override + public String getType() { + return TradeType.APP; + } + }; + + public static Specific JSAPI = + new Specific() { + @Override + public String getType() { + return TradeType.JSAPI; + } + }; + + public static Specific MWEB = + new Specific() { + @Override + public String getType() { + return TradeType.MWEB; + } + }; + + public static Specific MICROPAY = + new Specific() { + @Override + public String getType() { + return TradeType.MICROPAY; + } + }; + } + } + + /** + * 账户类型 + */ + public static class AccountType { + /** + * 基本账户 + */ + public static final String BASIC = "Basic"; + /** + * 运营账户 + */ + public static final String OPERATION = "Operation"; + /** + * Fees + */ + public static final String FEES = "Fees"; + } + + /** + * 签名类型. + */ + public static class SignType { + /** + * The constant HMAC_SHA256. + */ + public static final String HMAC_SHA256 = "HMAC-SHA256"; + /** + * The constant MD5. + */ + public static final String MD5 = "MD5"; + /** + * The constant ALL_SIGN_TYPES. + */ + public static final List ALL_SIGN_TYPES = Lists.newArrayList(HMAC_SHA256, MD5); + } + + /** + * 限定支付方式. + */ + public static class LimitPay { + /** + * no_credit--指定不能使用信用卡支付. + */ + public static final String NO_CREDIT = "no_credit"; + } + + /** + * 业务结果代码. + */ + public static class ResultCode { + /** + * 成功. + */ + public static final String SUCCESS = "SUCCESS"; + + /** + * 失败. + */ + public static final String FAIL = "FAIL"; + } + + /** + * 退款资金来源. + */ + public static class RefundAccountSource { + /** + * 可用余额退款/基本账户. + */ + public static final String RECHARGE_FUNDS = "REFUND_SOURCE_RECHARGE_FUNDS"; + + /** + * 未结算资金退款. + */ + public static final String UNSETTLED_FUNDS = "REFUND_SOURCE_UNSETTLED_FUNDS"; + + } + + /** + * 退款渠道. + */ + public static class RefundChannel { + /** + * 原路退款. + */ + public static final String ORIGINAL = "ORIGINAL"; + + /** + * 退回到余额. + */ + public static final String BALANCE = "BALANCE"; + + /** + * 原账户异常退到其他余额账户. + */ + public static final String OTHER_BALANCE = "OTHER_BALANCE"; + + /** + * 原银行卡异常退到其他银行卡. + */ + public static final String OTHER_BANKCARD = "OTHER_BANKCARD"; + } + + /** + * 交易状态. + */ + public static class WxpayTradeStatus { + /** + * 支付成功. + */ + public static final String SUCCESS = "SUCCESS"; + + /** + * 支付失败(其他原因,如银行返回失败). + */ + public static final String PAY_ERROR = "PAYERROR"; + + /** + * 用户支付中. + */ + public static final String USER_PAYING = "USERPAYING"; + + /** + * 已关闭. + */ + public static final String CLOSED = "CLOSED"; + + /** + * 未支付. + */ + public static final String NOTPAY = "NOTPAY"; + + /** + * 转入退款. + */ + public static final String REFUND = "REFUND"; + + /** + * 已撤销(刷卡支付). + */ + public static final String REVOKED = "REVOKED"; + } + + /** + * 退款状态. + */ + public static class RefundStatus { + /** + * 退款成功. + */ + public static final String SUCCESS = "SUCCESS"; + + /** + * 退款关闭. + */ + public static final String REFUND_CLOSE = "REFUNDCLOSE"; + + /** + * 退款处理中. + */ + public static final String PROCESSING = "PROCESSING"; + + /** + * 退款异常. + * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台(pay.weixin.qq.com)-交易中心,手动处理此笔退款。 + */ + public static final String CHANGE = "CHANGE"; + } + + public static class ReceiverType { + /** + * 商户id + */ + public static final String MERCHANT_ID = "MERCHANT_ID"; + /** + * 个人微信号 + */ + public static final String PERSONAL_WECHATID = "PERSONAL_WECHATID"; + /** + * 个人openid + */ + public static final String PERSONAL_OPENID = "PERSONAL_OPENID"; + /** + * 个人sub_openid + */ + public static final String PERSONAL_SUB_OPENID = "PERSONAL_SUB_OPENID"; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayErrorCode.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayErrorCode.java new file mode 100644 index 0000000000..e2cafda90b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayErrorCode.java @@ -0,0 +1,496 @@ +package com.github.binarywang.wxpay.constant; + +/** + *
    + * 微信支付错误码
    + * Created by Binary Wang on 2018/11/18.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayErrorCode { + /** + * 统一下单接口的错误码. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1 + */ + public static class UnifiedOrder { + /** + *
    +     * 描述:商户无此接口权限.
    +     * 原因:商户未开通此接口权限
    +     * 解决方案:请商户前往申请此接口权限
    +     * 
    + */ + public static final String NOAUTH = "NOAUTH"; + /** + *
    +     * 描述:余额不足.
    +     * 原因:用户帐号余额不足
    +     * 解决方案:用户帐号余额不足,请用户充值或更换支付卡后再支付
    +     * 
    + */ + public static final String NOTENOUGH = "NOTENOUGH"; + /** + *
    +     * 描述:商户订单已支付.
    +     * 原因:商户订单已支付,无需重复操作
    +     * 解决方案:商户订单已支付,无需更多操作
    +     * 
    + */ + public static final String ORDERPAID = "ORDERPAID"; + /** + *
    +     * 描述:订单已关闭.
    +     * 原因:当前订单已关闭,无法支付
    +     * 解决方案:当前订单已关闭,请重新下单
    +     * 
    + */ + public static final String ORDERCLOSED = "ORDERCLOSED"; + /** + *
    +     * 描述:系统错误.
    +     * 原因:系统超时
    +     * 解决方案:系统异常,请用相同参数重新调用
    +     * 
    + */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + /** + *
    +     * 描述:APPID不存在.
    +     * 原因:参数中缺少APPID
    +     * 解决方案:请检查APPID是否正确
    +     * 
    + */ + public static final String APPID_NOT_EXIST = "APPID_NOT_EXIST"; + /** + *
    +     * 描述:MCHID不存在.
    +     * 原因:参数中缺少MCHID
    +     * 解决方案:请检查MCHID是否正确
    +     * 
    + */ + public static final String MCHID_NOT_EXIST = "MCHID_NOT_EXIST"; + /** + *
    +     * 描述:appid和mch_id不匹配.
    +     * 原因:appid和mch_id不匹配
    +     * 解决方案:请确认appid和mch_id是否匹配
    +     * 
    + */ + public static final String APPID_MCHID_NOT_MATCH = "APPID_MCHID_NOT_MATCH"; + /** + *
    +     * 描述:缺少参数.
    +     * 原因:缺少必要的请求参数
    +     * 解决方案:请检查参数是否齐全
    +     * 
    + */ + public static final String LACK_PARAMS = "LACK_PARAMS"; + /** + *
    +     * 描述:商户订单号重复.
    +     * 原因:同一笔交易不能多次提交
    +     * 解决方案:请核实商户订单号是否重复提交
    +     * 
    + */ + public static final String OUT_TRADE_NO_USED = "OUT_TRADE_NO_USED"; + /** + *
    +     * 描述:签名错误.
    +     * 原因:参数签名结果不正确
    +     * 解决方案:请检查签名参数和方法是否都符合签名算法要求
    +     * 
    + */ + public static final String SIGNERROR = "SIGNERROR"; + /** + *
    +     * 描述:XML格式错误.
    +     * 原因:XML格式错误
    +     * 解决方案:请检查XML参数格式是否正确
    +     * 
    + */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + /** + *
    +     * 描述:请使用post方法.
    +     * 原因:未使用post传递参数
    +     * 解决方案:请检查请求参数是否通过post方法提交
    +     * 
    + */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + /** + *
    +     * 描述:post数据为空.
    +     * 原因:post数据不能为空
    +     * 解决方案:请检查post数据是否为空
    +     * 
    + */ + public static final String POST_DATA_EMPTY = "POST_DATA_EMPTY"; + /** + *
    +     * 描述:编码格式错误.
    +     * 原因:未使用指定编码格式
    +     * 解决方案:请使用UTF-8编码格式
    +     * 
    + */ + public static final String NOT_UTF8 = "NOT_UTF8"; + } + + /** + * 关闭订单. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_3&index=5 + */ + public static class OrderClose { + /** + * 订单已支付. + */ + public static final String ORDER_PAID = "ORDERPAID"; + + /** + * 系统错误. + */ + public static final String SYSTEM_ERROR = "SYSTEMERROR"; + + /** + * 订单不存在. + */ + public static final String ORDER_NOT_EXIST = "ORDERNOTEXIST"; + + /** + * 订单已关闭. + */ + public static final String ORDER_CLOSED = "ORDERCLOSED"; + + /** + * 签名错误. + */ + public static final String SIGN_ERROR = "SIGNERROR"; + + /** + * 未使用POST传递参数. + */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + + /** + * XML格式错误. + */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + + /** + * 订单状态错误. + */ + public static final String TRADE_STATE_ERROR = "TRADE_STATE_ERROR"; + } + + /** + * 退款申请. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=6 + */ + public static class Refund { + /** + *
    +     * 描述:接口返回错误.
    +     * 原因:系统超时等
    +     * 解决方案:请不要更换商户退款单号,请使用相同参数再次调用API。
    +     * 
    + */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + /** + *
    +     * 描述:退款业务流程错误,需要商户触发重试来解决.
    +     * 原因:并发情况下,业务被拒绝,商户重试即可解决
    +     * 解决方案:请不要更换商户退款单号,请使用相同参数再次调用API。
    +     * 
    + */ + public static final String BIZERR_NEED_RETRY = "BIZERR_NEED_RETRY"; + /** + *
    +     * 描述:订单已经超过退款期限.
    +     * 原因:订单已经超过可退款的最大期限(支付后一年内可退款)
    +     * 解决方案:请选择其他方式自行退款
    +     * 
    + */ + public static final String TRADE_OVERDUE = "TRADE_OVERDUE"; + /** + *
    +     * 描述:业务错误.
    +     * 原因:申请退款业务发生错误
    +     * 解决方案:该错误都会返回具体的错误原因,请根据实际返回做相应处理。
    +     * 
    + */ + public static final String ERROR = "ERROR"; + /** + *
    +     * 描述:退款请求失败.
    +     * 原因:用户帐号注销
    +     * 解决方案:此状态代表退款申请失败,商户可自行处理退款。
    +     * 
    + */ + public static final String USER_ACCOUNT_ABNORMAL = "USER_ACCOUNT_ABNORMAL"; + /** + *
    +     * 描述:无效请求过多.
    +     * 原因:连续错误请求数过多被系统短暂屏蔽
    +     * 解决方案:请检查业务是否正常,确认业务正常后请在1分钟后再来重试
    +     * 
    + */ + public static final String INVALID_REQ_TOO_MUCH = "INVALID_REQ_TOO_MUCH"; + /** + *
    +     * 描述:余额不足.
    +     * 原因:商户可用退款余额不足
    +     * 解决方案:此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。
    +     * 
    + */ + public static final String NOTENOUGH = "NOTENOUGH"; + /** + *
    +     * 描述:无效transaction_id.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败
    +     * 
    + */ + public static final String INVALID_TRANSACTIONID = "INVALID_TRANSACTIONID"; + /** + *
    +     * 描述:参数错误.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:请求参数错误,请重新检查再调用退款申请
    +     * 
    + */ + public static final String PARAM_ERROR = "PARAM_ERROR"; + /** + *
    +     * 描述:APPID不存在.
    +     * 原因:参数中缺少APPID
    +     * 解决方案:请检查APPID是否正确
    +     * 
    + */ + public static final String APPID_NOT_EXIST = "APPID_NOT_EXIST"; + /** + *
    +     * 描述:MCHID不存在.
    +     * 原因:参数中缺少MCHID
    +     * 解决方案:请检查MCHID是否正确
    +     * 
    + */ + public static final String MCHID_NOT_EXIST = "MCHID_NOT_EXIST"; + /** + *
    +     * 描述:订单号不存在.
    +     * 原因:缺少有效的订单号
    +     * 解决方案:请检查你的订单号是否正确且是否已支付,未支付的订单不能发起退款
    +     * 
    + */ + public static final String ORDERNOTEXIST = "ORDERNOTEXIST"; + /** + *
    +     * 描述:请使用post方法.
    +     * 原因:未使用post传递参数
    +     * 解决方案:请检查请求参数是否通过post方法提交
    +     * 
    + */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + /** + *
    +     * 描述:签名错误.
    +     * 原因:参数签名结果不正确
    +     * 解决方案:请检查签名参数和方法是否都符合签名算法要求
    +     * 
    + */ + public static final String SIGNERROR = "SIGNERROR"; + /** + *
    +     * 描述:XML格式错误.
    +     * 原因:XML格式错误
    +     * 解决方案:请检查XML参数格式是否正确
    +     * 
    + */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + /** + *
    +     * 描述:频率限制.
    +     * 原因:2个月之前的订单申请退款有频率限制
    +     * 解决方案:该笔退款未受理,请降低频率后重试
    +     * 
    + */ + public static final String FREQUENCY_LIMITED = "FREQUENCY_LIMITED"; + } + + /** + * 退款查询. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=7 + */ + public static class RefundQuery { + /** + *
    +     * 描述:接口返回错误.
    +     * 原因:系统超时
    +     * 解决方案:请尝试再次掉调用API。
    +     * 
    + */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + + /** + *
    +     * 描述:退款订单查询失败.
    +     * 原因:订单号错误或订单状态不正确
    +     * 解决方案:请检查订单号是否有误以及订单状态是否正确,如:未支付、已支付未退款
    +     * 
    + */ + public static final String REFUNDNOTEXIST = "REFUNDNOTEXIST"; + + /** + *
    +     * 描述:无效transaction_id.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败
    +     * 
    + */ + public static final String INVALID_TRANSACTIONID = "INVALID_TRANSACTIONID"; + + /** + *
    +     * 描述:参数错误.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:请求参数错误,请检查参数再调用退款申请
    +     * 
    + */ + public static final String PARAM_ERROR = "PARAM_ERROR"; + + /** + *
    +     * 描述:APPID不存在.
    +     * 原因:参数中缺少APPID
    +     * 解决方案:请检查APPID是否正确
    +     * 
    + */ + public static final String APPID_NOT_EXIST = "APPID_NOT_EXIST"; + + /** + *
    +     * 描述:MCHID不存在.
    +     * 原因:参数中缺少MCHID
    +     * 解决方案:请检查MCHID是否正确
    +     * 
    + */ + public static final String MCHID_NOT_EXIST = "MCHID_NOT_EXIST"; + + /** + *
    +     * 描述:请使用post方法.
    +     * 原因:未使用post传递参数
    +     * 解决方案:请检查请求参数是否通过post方法提交
    +     * 
    + */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + + /** + *
    +     * 描述:签名错误.
    +     * 原因:参数签名结果不正确
    +     * 解决方案:请检查签名参数和方法是否都符合签名算法要求
    +     * 
    + */ + public static final String SIGNERROR = "SIGNERROR"; + + /** + *
    +     * 描述:XML格式错误.
    +     * 原因:XML格式错误
    +     * 解决方案:请检查XML参数格式是否正确
    +     * 
    + */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + } + + /** + * 下载对账单. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=8 + */ + public static class DownloadBill { + /** + *
    +     * 描述:下载失败.
    +     * 原因:系统超时
    +     * 解决方案:请尝试再次查询。
    +     * 
    + */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + + /** + *
    +     * 描述:参数错误.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:参数错误,请重新检查
    +     * 
    + */ + public static final String INVALID_BILL_TYPE = "invalid bill_type"; + + /** + *
    +     * 描述:参数错误.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:参数错误,请重新检查
    +     * 
    + */ + public static final String DATA_FORMAT_ERROR = "data format error"; + + /** + *
    +     * 描述:参数错误.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:参数错误,请重新检查
    +     * 
    + */ + public static final String MISSING_PARAMETER = "missing parameter"; + + /** + *
    +     * 描述:参数错误.
    +     * 原因:请求参数未按指引进行填写
    +     * 解决方案:参数错误,请重新检查
    +     * 
    + */ + public static final String SIGN_ERROR = "SIGN ERROR"; + + /** + *
    +     * 描述:账单不存在.
    +     * 原因:当前商户号没有已成交的订单,不生成对账单
    +     * 解决方案:请检查当前商户号在指定日期内是否有成功的交易。
    +     * 错误:微信官方文档这个错误的字符串显示是'NO Bill Exist'('o'是大写),实际返回是'No Bill Exist'('o'是小写)
    +     * 
    + */ + public static final String NO_Bill_Exist = "No Bill Exist"; + + /** + *
    +     * 描述:账单未生成.
    +     * 原因:当前商户号没有已成交的订单或对账单尚未生成
    +     * 解决方案:请先检查当前商户号在指定日期内是否有成功的交易,如指定日期有交易则表示账单正在生成中,请在上午10点以后再下载。
    +     * 
    + */ + public static final String BILL_CREATING = "Bill Creating"; + + /** + *
    +     * 描述:账单压缩失败.
    +     * 原因:账单压缩失败,请稍后重试
    +     * 解决方案:账单压缩失败,请稍后重试
    +     * 
    + */ + public static final String COMPRESSG_ZIP_ERROR = "CompressGZip Error"; + + /** + *
    +     * 描述:账单解压失败.
    +     * 原因:账单解压失败,请稍后重试
    +     * 解决方案:账单解压失败,请稍后重试
    +     * 
    + */ + public static final String UN_COMPRESSG_ZIP_ERROR = "UnCompressGZip Error"; + + + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java index 0e375bd62f..cc8a80dad1 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java @@ -1,8 +1,18 @@ package com.github.binarywang.wxpay.converter; -import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon; -import com.github.binarywang.wxpay.bean.result.WxPayOrderNotifyResult; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyCoupon; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.google.common.base.Function; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.converters.MarshallingContext; @@ -12,17 +22,20 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.mapper.Mapper; -import org.apache.commons.lang3.StringUtils; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +/** + * The type Wxpay order notify result converter. + * + * @author aimilin + */ public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter { + /** + * Instantiates a new Wx pay order notify result converter. + * + * @param mapper the mapper + * @param reflectionProvider the reflection provider + */ public WxPayOrderNotifyResultConverter(Mapper mapper, ReflectionProvider reflectionProvider) { super(mapper, reflectionProvider); } @@ -57,11 +70,11 @@ public void marshal(Object original, HierarchicalStreamWriter writer, Marshallin @Override protected void marshallField(MarshallingContext context, Object newObj, Field field) { - if (field.getName().equals("couponList")) { + if ("couponList".equals(field.getName())) { return; - } else { - super.marshallField(context, newObj, field); } + + super.marshallField(context, newObj, field); } @Override @@ -72,26 +85,26 @@ public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext co fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields())); Map fieldMap = getFieldMap(fields); - List coupons = new ArrayList<>(10); + Map coupons = Maps.newTreeMap(); while (reader.hasMoreChildren()) { reader.moveDown(); if (fieldMap.containsKey(reader.getNodeName())) { Field field = fieldMap.get(reader.getNodeName()); - setFieldValue(context, obj, field); + this.setFieldValue(context, obj, field); } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_id_")) { String id = (String) context.convertAnother(obj, String.class); - getIndex(coupons, reader.getNodeName()).setCouponId(id); + this.getElement(coupons, reader.getNodeName()).setCouponId(id); } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_type_")) { String type = (String) context.convertAnother(obj, String.class); - getIndex(coupons, reader.getNodeName()).setCouponType(type); + this.getElement(coupons, reader.getNodeName()).setCouponType(type); } else if (StringUtils.startsWith(reader.getNodeName(), "coupon_fee_")) { Integer fee = (Integer) context.convertAnother(obj, Integer.class); - getIndex(coupons, reader.getNodeName()).setCouponFee(fee); + this.getElement(coupons, reader.getNodeName()).setCouponFee(fee); } reader.moveUp(); } - obj.setCouponList(coupons); + obj.setCouponList(Lists.newArrayList(coupons.values())); return obj; } @@ -102,12 +115,12 @@ private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass()); pd.getWriteMethod().invoke(obj, val); } - } catch (Exception e) { + } catch (Exception ignored) { } } private Map getFieldMap(List fields) { - Map fieldMap = Maps.uniqueIndex(fields, new Function() { + return Maps.uniqueIndex(fields, new Function() { @Override public String apply(Field field) { if (field.isAnnotationPresent(XStreamAlias.class)) { @@ -116,14 +129,14 @@ public String apply(Field field) { return field.getName(); } }); - return fieldMap; } - private WxPayOrderNotifyCoupon getIndex(List coupons, String nodeName) { - Integer index = Integer.valueOf(StringUtils.substring(nodeName, nodeName.lastIndexOf("_") + 1)); - if (index >= coupons.size() || coupons.get(index) == null) { - coupons.add(index, new WxPayOrderNotifyCoupon()); + private WxPayOrderNotifyCoupon getElement(Map coupons, String nodeName) { + Integer index = Integer.valueOf(StringUtils.substringAfterLast(nodeName, "_")); + if (coupons.get(index) == null) { + coupons.put(index, new WxPayOrderNotifyCoupon()); } + return coupons.get(index); } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/exception/WxPayException.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/exception/WxPayException.java new file mode 100644 index 0000000000..a5a2552b3c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/exception/WxPayException.java @@ -0,0 +1,216 @@ +package com.github.binarywang.wxpay.exception; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.google.common.base.Joiner; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *
    + * 微信支付异常结果类
    + * Created by Binary Wang on 2017-6-6.
    + * 
    + * + * @author BinaryWang + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxPayException extends Exception { + private static final long serialVersionUID = 2214381471513460742L; + + /** + * 自定义错误讯息. + */ + private String customErrorMsg; + /** + * 返回状态码. + */ + private String returnCode; + /** + * 返回信息. + */ + private String returnMsg; + + /** + * 业务结果. + */ + private String resultCode; + + /** + * 错误代码. + */ + private String errCode; + + /** + * 错误代码描述. + */ + private String errCodeDes; + + /** + * 微信支付返回的结果xml字符串. + */ + private String xmlString; + + /** + * Instantiates a new Wx pay exception. + * + * @param customErrorMsg the custom error msg + */ + public WxPayException(String customErrorMsg) { + super(customErrorMsg); + this.customErrorMsg = customErrorMsg; + } + + /** + * Instantiates a new Wx pay exception. + * + * @param customErrorMsg the custom error msg + * @param tr the tr + */ + public WxPayException(String customErrorMsg, Throwable tr) { + super(customErrorMsg, tr); + this.customErrorMsg = customErrorMsg; + } + + private WxPayException(Builder builder) { + super(builder.buildErrorMsg()); + returnCode = builder.returnCode; + returnMsg = builder.returnMsg; + resultCode = builder.resultCode; + errCode = builder.errCode; + errCodeDes = builder.errCodeDes; + xmlString = builder.xmlString; + } + + /** + * 通过BaseWxPayResult生成异常对象. + * + * @param payBaseResult the pay base result + * @return the wx pay exception + */ + public static WxPayException from(BaseWxPayResult payBaseResult) { + return WxPayException.newBuilder() + .xmlString(payBaseResult.getXmlString()) + .returnMsg(payBaseResult.getReturnMsg()) + .returnCode(payBaseResult.getReturnCode()) + .resultCode(payBaseResult.getResultCode()) + .errCode(payBaseResult.getErrCode()) + .errCodeDes(payBaseResult.getErrCodeDes()) + .build(); + } + + /** + * New builder builder. + * + * @return the builder + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * The type Builder. + */ + public static final class Builder { + private String returnCode; + private String returnMsg; + private String resultCode; + private String errCode; + private String errCodeDes; + private String xmlString; + + private Builder() { + } + + /** + * Return code builder. + * + * @param returnCode the return code + * @return the builder + */ + public Builder returnCode(String returnCode) { + this.returnCode = returnCode; + return this; + } + + /** + * Return msg builder. + * + * @param returnMsg the return msg + * @return the builder + */ + public Builder returnMsg(String returnMsg) { + this.returnMsg = returnMsg; + return this; + } + + /** + * Result code builder. + * + * @param resultCode the result code + * @return the builder + */ + public Builder resultCode(String resultCode) { + this.resultCode = resultCode; + return this; + } + + /** + * Err code builder. + * + * @param errCode the err code + * @return the builder + */ + public Builder errCode(String errCode) { + this.errCode = errCode; + return this; + } + + /** + * Err code des builder. + * + * @param errCodeDes the err code des + * @return the builder + */ + public Builder errCodeDes(String errCodeDes) { + this.errCodeDes = errCodeDes; + return this; + } + + /** + * Xml string builder. + * + * @param xmlString the xml string + * @return the builder + */ + public Builder xmlString(String xmlString) { + this.xmlString = xmlString; + return this; + } + + /** + * Build wx pay exception. + * + * @return the wx pay exception + */ + public WxPayException build() { + return new WxPayException(this); + } + + /** + * Build error msg string. + * + * @return the string + */ + public String buildErrorMsg() { + return Joiner.on(",").skipNulls().join( + returnCode == null ? null : String.format("返回代码:[%s]", returnCode), + returnMsg == null ? null : String.format("返回信息:[%s]", returnMsg), + resultCode == null ? null : String.format("结果代码:[%s]", resultCode), + errCode == null ? null : String.format("错误代码:[%s]", errCode), + errCodeDes == null ? null : String.format("错误详情:[%s]", errCodeDes), + xmlString == null ? null : "微信返回的原始报文:\n" + xmlString + ); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/Applyment4SubService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/Applyment4SubService.java new file mode 100644 index 0000000000..4cb6785ffd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/Applyment4SubService.java @@ -0,0 +1,73 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.applyment.*; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + * 特约商户进件 + * 产品介绍:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment4sub/chapter1_1.shtml + * + * @author zhouyongshen + */ +public interface Applyment4SubService { + /** + * 提交申请单API + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment4sub/chapter3_1.shtml + * 接口链接:https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/ + * + * @param request 请求对象 + * @return WxPayApplymentCreateResult 响应结果 + * @throws WxPayException the wx pay exception + */ + WxPayApplymentCreateResult createApply(WxPayApplyment4SubCreateRequest request) throws WxPayException; + + /** + * 通过业务申请编号查询申请状态 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment4sub/chapter3_2.shtml + * 接口链接:https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/{business_code} + * + * @param businessCode 业务申请编号 + * 1、只能由数字、字母或下划线组成,建议前缀为服务商商户号。 + * 2、服务商自定义的唯一编号。 + * 3、每个编号对应一个申请单,每个申请单审核通过后生成一个微信支付商户号。 + * 4、若申请单被驳回,可填写相同的“业务申请编号”,即可覆盖修改原申请单信息。 + * 示例值:1900013511_10000 + * @return the applyment state query result + * @throws WxPayException the wx pay exception + */ + ApplymentStateQueryResult queryApplyStatusByBusinessCode(String businessCode) throws WxPayException; + + /** + * 通过申请单号查询申请状态 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment4sub/chapter3_2.shtml + * 接口链接:https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/applyment_id/{applyment_id} + * + * @param applymentId 微信支付分的申请单号。示例值:2000001234567890 + * @return the applyment state query result + * @throws WxPayException the wx pay exception + */ + ApplymentStateQueryResult queryApplyStatusByApplymentId(String applymentId) throws WxPayException; + + /** + * 通过申请单号查询申请状态 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment4sub/chapter3_4.shtml + * 接口链接:https://api.mch.weixin.qq.com/v3/apply4sub/sub_merchants/{sub_mchid}/settlement + * + * @param subMchid 本服务商进件、已签约的特约商户号。 + * @return the settlement info result + * @throws WxPayException the wx pay exception + */ + SettlementInfoResult querySettlementBySubMchid(String subMchid) throws WxPayException; + + /** + * 修改结算帐号 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment4sub/chapter3_3.shtml + * 接口链接:https://api.mch.weixin.qq.com/v3/apply4sub/sub_merchants/{sub_mchid}/modify-settlement + * + * @param subMchid 特约商户号 + * @param request 修改结算账户请求对象信息 + * @throws WxPayException the wx pay exception + */ + void modifySettlement(String subMchid, ModifySettlementRequest request) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java new file mode 100644 index 0000000000..5c86306b9f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java @@ -0,0 +1,58 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + *
    + *  电商收付通相关服务类.
    + *  接口规则:https://wechatpay-api.gitbook.io/wechatpay-api-v3
    + * 
    + * + * @author cloudX + * @date 2020/08/17 + */ +public interface EcommerceService { + /** + *
    +   * 二级商户进件API
    +   * 接口地址: https://api.mch.weixin.qq.com/v3/ecommerce/applyments/
    +   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_1.shtml
    +   *
    +   * 
    + * + * @param request 请求对象 + * @return . applyments result + * @throws WxPayException the wx pay exception + */ + ApplymentsResult createApply(ApplymentsRequest request) throws WxPayException; + + /** + *
    +   * 查询申请状态API
    +   * 请求URL: https://api.mch.weixin.qq.com/v3/ecommerce/applyments/{applyment_id}
    +   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_2.shtml
    +   * 
    + * + * @param applymentId 申请单ID + * @return . applyments status result + * @throws WxPayException the wx pay exception + */ + ApplymentsStatusResult queryApplyStatusByApplymentId(String applymentId) throws WxPayException; + + /** + *
    +   * 查询申请状态API
    +   * 请求URL: https://api.mch.weixin.qq.com/v3/ecommerce/applyments/out-request-no/{out_request_no}
    +   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_2.shtml
    +   * 
    + * + * @param outRequestNo 业务申请编号 + * @return . applyments status result + * @throws WxPayException the wx pay exception + */ + ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java new file mode 100644 index 0000000000..1b1b76b154 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java @@ -0,0 +1,146 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.entpay.*; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + *
    + *  企业付款相关服务类.
    + *  Created by BinaryWang on 2017/12/19.
    + * 
    + * + * @author Binary Wang + */ +public interface EntPayService { + /** + *
    +   * 企业付款API.
    +   * 企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。
    +   * 比如目前的保险行业向客户退保、给付、理赔。
    +   * 企业付款将使用商户的可用余额,需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”https://pay.weixin.qq.com/进行操作。
    +   * 注意:与商户微信支付收款资金并非同一账户,需要单独充值。
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
    +   * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
    +   * 
    + * + * @param request 请求对象 + * @return the ent pay result + * @throws WxPayException the wx pay exception + */ + EntPayResult entPay(EntPayRequest request) throws WxPayException; + + /** + *
    +   * 查询企业付款API.
    +   * 用于商户的企业付款操作进行结果查询,返回付款操作详细结果。
    +   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3
    +   * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo
    +   * 
    + * + * @param partnerTradeNo 商户订单号 + * @return the ent pay query result + * @throws WxPayException the wx pay exception + */ + EntPayQueryResult queryEntPay(String partnerTradeNo) throws WxPayException; + /** + *
    +   * 查询企业付款API.
    +   * 用于商户的企业付款操作进行结果查询,返回付款操作详细结果。
    +   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3
    +   * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo
    +   * 
    + * + * @param request 请求对象 + * @return the ent pay query result + * @throws WxPayException the wx pay exception + */ + EntPayQueryResult queryEntPay(EntPayQueryRequest request) throws WxPayException; + + /** + *
    +   * 获取RSA加密公钥API.
    +   * RSA算法使用说明(非对称加密算法,算法采用RSA/ECB/OAEPPadding模式)
    +   * 1、 调用获取RSA公钥API获取RSA公钥,落地成本地文件,假设为public.pem
    +   * 2、 确定public.pem文件的存放路径,同时修改代码中文件的输入路径,加载RSA公钥
    +   * 3、 用标准的RSA加密库对敏感信息进行加密,选择RSA_PKCS1_OAEP_PADDING填充模式
    +   * (eg:Java的填充方式要选 " RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING")
    +   * 4、 得到进行rsa加密并转base64之后的密文
    +   * 5、 将密文传给微信侧相应字段,如付款接口(enc_bank_no/enc_true_name)
    +   *
    +   * 接口默认输出PKCS#1格式的公钥,商户需根据自己开发的语言选择公钥格式
    +   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_7&index=4
    +   * 接口链接:https://fraud.mch.weixin.qq.com/risk/getpublickey
    +   * 
    + * + * @return the public key + * @throws WxPayException the wx pay exception + */ + String getPublicKey() throws WxPayException; + + /** + * 企业付款到银行卡. + *
    +   * 用于企业向微信用户银行卡付款
    +   * 目前支持接口API的方式向指定微信用户的银行卡付款。
    +   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_2
    +   * 接口链接:https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank
    +   * 
    + * + * @param request 请求对象 + * @return the ent pay bank result + * @throws WxPayException the wx pay exception + */ + EntPayBankResult payBank(EntPayBankRequest request) throws WxPayException; + + /** + * 企业付款到银行卡查询. + *
    +   * 用于对商户企业付款到银行卡操作进行结果查询,返回付款操作详细结果。
    +   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_3
    +   * 接口链接:https://api.mch.weixin.qq.com/mmpaysptrans/query_bank
    +   * 
    + * + * @param partnerTradeNo 商户订单号 + * @return the ent pay bank query result + * @throws WxPayException the wx pay exception + */ + EntPayBankQueryResult queryPayBank(String partnerTradeNo) throws WxPayException; + + /** + * 企业付款到银行卡查询. + *
    +   * 用于对商户企业付款到银行卡操作进行结果查询,返回付款操作详细结果。
    +   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_3
    +   * 接口链接:https://api.mch.weixin.qq.com/mmpaysptrans/query_bank
    +   * 
    + * + * @param request 请求对象 + * @return the ent pay bank query result + * @throws WxPayException the wx pay exception + */ + EntPayBankQueryResult queryPayBank(EntPayBankQueryRequest request) throws WxPayException; + + /** + * 企业发送微信红包给个人用户 + *
    +   *   文档地址:https://work.weixin.qq.com/api/doc
    +   *   接口地址: https://api.mch.weixin.qq.com/mmpaymkttransfers/sendworkwxredpack
    +   * 
    + * @param request 请求对象 + * @return the wx pay send redpack result + * @throws WxPayException the wx pay exception + */ + EntPayRedpackResult sendEnterpriseRedpack(EntPayRedpackRequest request) throws WxPayException; + + /** + * 企业发送微信红包查询 + *
    +   *   文档地址:https://work.weixin.qq.com/api/doc
    +   *   接口地址: https://api.mch.weixin.qq.com/mmpaymkttransfers/queryworkwxredpack
    +   * 
    + * @param request 请求对象 + * @return the wx pay send redpack result + * @throws WxPayException the wx pay exception + */ + EntPayRedpackQueryResult queryEnterpriseRedpack(EntPayRedpackQueryRequest request) throws WxPayException; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MerchantMediaService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MerchantMediaService.java new file mode 100644 index 0000000000..429ece394d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MerchantMediaService.java @@ -0,0 +1,31 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.media.ImageUploadResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +import java.io.File; +import java.io.IOException; + +/** + *
    + * 微信支付通用媒体接口.
    + * 
    + * + * @author zhouyongshen + */ +public interface MerchantMediaService { + /** + *
    +   * 通用接口-图片上传API
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/chapter3_1.shtml
    +   * 接口链接:https://api.mch.weixin.qq.com/v3/merchant/media/upload
    +   * 
    + * + * @param imageFile 需要上传的图片文件 + * @return ImageUploadResult 微信返回的媒体文件标识Id。示例值:6uqyGjGrCf2GtyXP8bxrbuH9-aAoTjH-rKeSl3Lf4_So6kdkQu4w8BYVP3bzLtvR38lxt4PjtCDXsQpzqge_hQEovHzOhsLleGFQVRF-U_0 + * @throws WxPayException the wx pay exception + */ + ImageUploadResult imageUploadV3(File imageFile) throws WxPayException, IOException; + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayScoreService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayScoreService.java new file mode 100644 index 0000000000..ff6aafe9f4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayScoreService.java @@ -0,0 +1,138 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.payscore.PayScoreNotifyData; +import com.github.binarywang.wxpay.bean.payscore.WxPayScoreRequest; +import com.github.binarywang.wxpay.bean.payscore.WxPayScoreResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + *
    + *  支付分相关服务类.
    + *   微信支付分是对个人的身份特质、支付行为、使用历史等情况的综合计算分值,旨在为用户提供更简单便捷的生活方式。
    + *   微信用户可以在具体应用场景中,开通微信支付分。开通后,用户可以在【微信—>钱包—>支付分】中查看分数和使用记录。
    + *   (即需在应用场景中使用过一次,钱包才会出现支付分入口)
    + *
    + *  Created by doger.wang on 2020/05/12.
    + * 
    + * + * @author doger.wang + */ +public interface PayScoreService { + /** + *
    +   * 支付分创建订单API.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_1.shtml
    +   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/serviceorder
    +   * 
    + * + * @param request 请求对象 + * @return WxPayScoreResult wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult createServiceOrder(WxPayScoreRequest request) throws WxPayException; + + /** + *
    +   * 支付分查询订单API.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_2.shtml
    +   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/serviceorder
    +   * 
    + * + * @param outOrderNo the out order no + * @param queryId the query id + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult queryServiceOrder(String outOrderNo, String queryId) throws WxPayException; + + /** + *
    +   * 支付分取消订单API.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_3.shtml
    +   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/cancel
    +   * 
    + * + * @param outOrderNo the out order no + * @param reason the reason + * @return com.github.binarywang.wxpay.bean.payscore.WxPayScoreResult wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult cancelServiceOrder(String outOrderNo, String reason) throws WxPayException; + + /** + *
    +   * 支付分修改订单金额API.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_4.shtml
    +   * 接口链接:https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/modify
    +   * 
    + * + * @param request the request + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult modifyServiceOrder(WxPayScoreRequest request) throws WxPayException; + + /** + *
    +   * 支付分完结订单API.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_5.shtml
    +   * 请求URL:https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/complete
    +   * 
    + * + * @param request the request + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult completeServiceOrder(WxPayScoreRequest request) throws WxPayException; + + /** + *
    +   * 支付分订单收款API.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_6.shtml
    +   * 请求URL:https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/pay
    +   *
    +   * 
    + * + * @param outOrderNo the out order no + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult payServiceOrder(String outOrderNo) throws WxPayException; + + /** + *
    +   * 支付分订单收款API.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_7.shtml
    +   * 请求URL: https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/sync
    +   * 
    + * + * @param request the request + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult syncServiceOrder(WxPayScoreRequest request) throws WxPayException; + + /** + *
    +   * 支付分回调内容解析方法
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter5_2.shtml
    +   * 
    + * + * @param data the data + * @return the wx pay score result + */ + PayScoreNotifyData parseNotifyData(String data); + + /** + *
    +   * 支付分回调NotifyData解密resource
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter5_2.shtml
    +   * 
    + * + * @param data the data + * @return the wx pay score result + * @throws WxPayException the wx pay exception + */ + WxPayScoreResult decryptNotifyDataResource(PayScoreNotifyData data) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ProfitSharingService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ProfitSharingService.java new file mode 100644 index 0000000000..7f8d6ad330 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ProfitSharingService.java @@ -0,0 +1,134 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.profitsharing.*; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + * 注意:微信最高分账比例为30% + * 可多次分账到同一个人,但是依然不能超过30% + * + * @author Wang GuangXin 2019/10/22 10:05 + * @version 1.0 + */ +public interface ProfitSharingService { + /** + *
    +   * 单次分账请求按照传入的分账接收方账号和资金进行分账,同时会将订单剩余的待分账金额解冻给特约商户。故操作成功后,订单不能再进行分账,也不能进行分账完结。
    +   * 接口频率:30QPS
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_1&index=1
    +   * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/profitsharing
    +   * 
    + * + * @param request . + * @return . + * @throws WxPayException the wx pay exception + */ + ProfitSharingResult profitSharing(ProfitSharingRequest request) throws WxPayException; + + /** + *
    +   * 微信订单支付成功后,服务商代子商户发起分账请求,将结算后的钱分到分账接收方。多次分账请求仅会按照传入的分账接收方进行分账,不会对剩余的金额进行任何操作。故操作成功后,在待分账金额不等于零时,订单依旧能够再次进行分账。
    +   * 多次分账,可以将本商户作为分账接收方直接传入,实现释放资金给本商户的功能
    +   * 对同一笔订单最多能发起20次多次分账请求
    +   * 接口频率:30QPS
    +   * 
    + * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_6&index=2 + * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/multiprofitsharing + * + * @param request . + * @return . + * @throws WxPayException the wx pay exception + */ + ProfitSharingResult multiProfitSharing(ProfitSharingRequest request) throws WxPayException; + + /** + *
    +   * 1、不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给特约商户
    +   * 2、调用多次分账接口后,需要解冻剩余资金时,调用本接口将剩余的分账金额全部解冻给特约商户
    +   * 3、已调用请求单次分账后,剩余待分账金额为零,不需要再调用此接口。
    +   * 接口频率:30QPS
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_5&index=6
    +   * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish
    +   * 
    + * + * @param request . + * @return . + * @throws WxPayException the wx pay exception + */ + ProfitSharingResult profitSharingFinish(ProfitSharingFinishRequest request) throws WxPayException; + + /** + *
    +   * 服务商代子商户发起添加分账接收方请求,后续可通过发起分账请求将结算后的钱分到该分账接收方。
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_3&index=4
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver
    +   * 
    + * + * @param request . + * @return . + * @throws WxPayException . + */ + ProfitSharingReceiverResult addReceiver(ProfitSharingReceiverRequest request) throws WxPayException; + + /** + *
    +   * 服务商代子商户发起删除分账接收方请求,删除后不支持将结算后的钱分到该分账接收方。
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_4&index=5
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver
    +   * 
    + * + * @param request . + * @return . + * @throws WxPayException . + */ + ProfitSharingReceiverResult removeReceiver(ProfitSharingReceiverRequest request) throws WxPayException; + + /** + * TODO:微信返回签名失败 + *
    +   * 发起分账请求后,可调用此接口查询分账结果;发起分账完结请求后,可调用此接口查询分账完结的执行结果。
    +   * 接口频率:80QPS
    +   * 
    + * + * @param request . + * @return . + * @throws WxPayException . + */ + ProfitSharingQueryResult profitSharingQuery(ProfitSharingQueryRequest request) throws WxPayException; + + /** + * TODO:这个接口用真实的数据返回【参数不正确】,我对比官方文档除了缺少sub_mch_id,和sub_appid之外其他相同,当我随便填了一个商户id的时候,提示【回退方没有开通分账回退功能】 + *
    +   * 仅对订单进行退款时,如果订单已经分账,可以先调用此接口将指定的金额从分账接收方(仅限商户类型的分账接收方)回退给特约商户,然后再退款。
    +   * 回退以原分账请求为依据,可以对分给分账接收方的金额进行多次回退,只要满足累计回退不超过该请求中分给接收方的金额。
    +   * 此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果。
    +   * 此功能需要接收方在商户平台-交易中心-分账-分账接收设置下,开启同意分账回退后,才能使用。
    +   * 接口频率:30QPS
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_7&index=7
    +   * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/profitsharingreturn
    +   * 
    + * + * @param returnRequest . + * @return . + * @throws WxPayException . + */ + ProfitSharingReturnResult profitSharingReturn(ProfitSharingReturnRequest returnRequest) throws WxPayException; + + /** + * TODO:因profitsharingReturn接口无法使用,没有办法对这里进行真实的测试,模拟数据这里返回【记录不存在】 + *
    +   * 商户需要核实回退结果,可调用此接口查询回退结果。
    +   * 如果分账回退接口返回状态为处理中,可调用此接口查询回退结果。
    +   * 接口频率:30QPS
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_8&index=8
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/profitsharingreturnquery
    +   * 
    + * + * @param queryRequest . + * @return . + * @throws WxPayException . + */ + ProfitSharingReturnResult profitSharingReturnQuery(ProfitSharingReturnQueryRequest queryRequest) + throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RedpackService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RedpackService.java new file mode 100644 index 0000000000..131205d07a --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RedpackService.java @@ -0,0 +1,76 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.request.WxPayRedpackQueryRequest; +import com.github.binarywang.wxpay.bean.request.WxPaySendMiniProgramRedpackRequest; +import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest; +import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendMiniProgramRedpackResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + * 红包相关接口. + * + * @author Binary Wang + * @date 2019-12-26 + */ +public interface RedpackService { + /** + *
    +   * 发送小程序红包.
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/tools/miniprogram_hb.php?chapter=13_9&index=2
    +   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb
    +   * 
    + * + * @param request 请求对象 + * @return the result + * @throws WxPayException the exception + */ + WxPaySendMiniProgramRedpackResult sendMiniProgramRedpack(WxPaySendMiniProgramRedpackRequest request) throws WxPayException; + + /** + * 发送微信红包给个人用户. + *
    +   * 文档详见:
    +   * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
    +   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
    +   * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4
    +   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack
    +   * 
    + * + * @param request 请求对象 + * @return the wx pay send redpack result + * @throws WxPayException the wx pay exception + */ + WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException; + + /** + *
    +   *   查询红包记录.
    +   *   用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
    +   *   请求Url:https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo
    +   *   是否需要证书:是(证书及使用说明详见商户证书)
    +   *   请求方式:POST
    +   * 
    + * + * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 + * @return the wx pay redpack query result + * @throws WxPayException the wx pay exception + */ + WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException; + + /** + *
    +   *   查询红包记录.
    +   *   用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
    +   *   请求Url:https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo
    +   *   是否需要证书:是(证书及使用说明详见商户证书)
    +   *   请求方式:POST
    +   * 
    + * + * @param request 红包查询请求 + * @return the wx pay redpack query result + * @throws WxPayException the wx pay exception + */ + WxPayRedpackQueryResult queryRedpack(WxPayRedpackQueryRequest request) throws WxPayException; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index e336cd21f3..27d86548ca 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1,26 +1,148 @@ package com.github.binarywang.wxpay.service; +import com.github.binarywang.wxpay.bean.WxPayApiData; +import com.github.binarywang.wxpay.bean.coupon.*; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; -import me.chanjar.weixin.common.exception.WxErrorException; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import org.apache.http.client.methods.HttpPost; import java.io.File; +import java.net.URI; +import java.util.Date; import java.util.Map; /** *
    - * 微信支付相关接口
    + * 微信支付相关接口.
      * Created by Binary Wang on 2016/7/28.
      * 
    * - * @author binarywang (https://github.com/binarywang) + * @author Binary Wang */ public interface WxPayService { + /** + * 获取微信支付请求url前缀,沙箱环境可能不一样. + * + * @return the pay base url + */ + String getPayBaseUrl(); + + /** + * 发送post请求,得到响应字节数组. + * + * @param url 请求地址 + * @param requestStr 请求信息 + * @param useKey 是否使用证书 + * @return 返回请求结果字节数组 byte [ ] + * @throws WxPayException the wx pay exception + */ + byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException; + + /** + * 发送post请求,得到响应字符串. + * + * @param url 请求地址 + * @param requestStr 请求信息 + * @param useKey 是否使用证书 + * @return 返回请求结果字符串 string + * @throws WxPayException the wx pay exception + */ + String post(String url, String requestStr, boolean useKey) throws WxPayException; + + /** + * 发送post请求,得到响应字符串. + * + * @param url 请求地址 + * @param requestStr 请求信息 + * @return 返回请求结果字符串 string + * @throws WxPayException the wx pay exception + */ + String postV3(String url, String requestStr) throws WxPayException; + + /** + * 发送post请求,得到响应字符串. + * + * 部分字段会包含敏感信息,所以在提交前需要在请求头中会包含"Wechatpay-Serial"信息 + * + * @param url 请求地址 + * @param requestStr 请求信息 + * @return 返回请求结果字符串 string + * @throws WxPayException the wx pay exception + */ + String postV3WithWechatpaySerial(String url, String requestStr) throws WxPayException; + + /** + * 发送post请求,得到响应字符串. + * + * @param url 请求地址 + * @param httpPost 请求信息 + * @return 返回请求结果字符串 string + * @throws WxPayException the wx pay exception + */ + String postV3(String url, HttpPost httpPost) throws WxPayException; + + /** + * 发送get V3请求,得到响应字符串. + * + * @param url 请求地址 + * @return 返回请求结果字符串 string + * @throws WxPayException the wx pay exception + */ + String getV3(URI url) throws WxPayException; + + /** + * 获取企业付款服务类. + * + * @return the ent pay service + */ + EntPayService getEntPayService(); + + /** + * 获取红包接口服务类. + * + * @return . + */ + RedpackService getRedpackService(); + + /** + * 获取分账服务类. + * + * @return the ent pay service + */ + ProfitSharingService getProfitSharingService(); + + + /** + * 获取支付分服务类. + * + * @return the ent pay service + */ + PayScoreService getPayScoreService(); + + /** + * 获取电商收付通服务类 + * @return + */ + EcommerceService getEcommerceService(); + + /** + * 设置企业付款服务类,允许开发者自定义实现类. + * + * @param entPayService the ent pay service + */ + void setEntPayService(EntPayService entPayService); + /** *
    -   * 查询订单(详见https://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
    +   * 查询订单.
    +   * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
        * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
        * 需要调用查询接口的情况:
        * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
    @@ -32,12 +154,33 @@ public interface WxPayService {
        *
        * @param transactionId 微信订单号
        * @param outTradeNo    商户系统内部的订单号,当没提供transactionId时需要传这个。
    +   * @return the wx pay order query result
    +   * @throws WxPayException the wx pay exception
    +   */
    +  WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxPayException;
    +
    +  /**
    +   * 
    +   * 查询订单(适合于需要自定义子商户号和子商户appid的情形).
    +   * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
    +   * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
    +   * 需要调用查询接口的情况:
    +   * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
    +   * ◆ 调用支付接口后,返回系统错误或未知交易状态情况;
    +   * ◆ 调用被扫支付API,返回USERPAYING的状态;
    +   * ◆ 调用关单或撤销接口API之前,需确认支付状态;
    +   * 接口地址:https://api.mch.weixin.qq.com/pay/orderquery
    +   * 
    + * + * @param request 查询订单请求对象 + * @return the wx pay order query result + * @throws WxPayException the wx pay exception */ - WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; + WxPayOrderQueryResult queryOrder(WxPayOrderQueryRequest request) throws WxPayException; /** *
    -   * 关闭订单
    +   * 关闭订单.
        * 应用场景
        * 以下情况需要调用关单接口:
        * 1. 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
    @@ -48,51 +191,130 @@ public interface WxPayService {
        * 
    * * @param outTradeNo 商户系统内部的订单号 + * @return the wx pay order close result + * @throws WxPayException the wx pay exception + */ + WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxPayException; + + /** + *
    +   * 关闭订单(适合于需要自定义子商户号和子商户appid的情形).
    +   * 应用场景
    +   * 以下情况需要调用关单接口:
    +   * 1. 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
    +   * 2. 系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
    +   * 注意:订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟。
    +   * 接口地址:https://api.mch.weixin.qq.com/pay/closeorder
    +   * 是否需要证书:   不需要。
    +   * 
    + * + * @param request 关闭订单请求对象 + * @return the wx pay order close result + * @throws WxPayException the wx pay exception */ - WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException; + WxPayOrderCloseResult closeOrder(WxPayOrderCloseRequest request) throws WxPayException; /** - * 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) + * 调用统一下单接口,并组装生成支付所需参数对象. + * + * @param 请使用{@link com.github.binarywang.wxpay.bean.order}包下的类 + * @param request 统一下单请求参数 + * @return 返回 {@link com.github.binarywang.wxpay.bean.order}包下的类对象 + * @throws WxPayException the wx pay exception + */ + T createOrder(WxPayUnifiedOrderRequest request) throws WxPayException; + + /** + * 调用统一下单接口,并组装生成支付所需参数对象. + * + * @param specificTradeType 将使用的交易方式,不能为 null + * @param request 统一下单请求参数,设定的 tradeType 及配置里的 tradeType 将被忽略,转而使用 specificTradeType + * @return 返回 {@link WxPayConstants.TradeType.Specific} 指定的类 + * @throws WxPayException the wx pay exception + * @see WxPayService#createOrder(WxPayUnifiedOrderRequest) + */ + T createOrder(WxPayConstants.TradeType.Specific specificTradeType, WxPayUnifiedOrderRequest request) throws WxPayException; + + /** + * 统一下单(详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1) * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder * * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置) + * @return the wx pay unified order result + * @throws WxPayException the wx pay exception */ - WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException; + WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxPayException; /** - * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数 - * 详见http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN + * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数. + * 详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5 * * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置) + * @return the pay info + * @throws WxPayException the wx pay exception + * @deprecated 建议使用 {@link com.github.binarywang.wxpay.service.WxPayService#createOrder(WxPayUnifiedOrderRequest)} */ - Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; + @Deprecated + Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxPayException; /** - * 获取配置 + * 获取配置. + * + * @return the config */ WxPayConfig getConfig(); /** - * 设置配置对象 + * 设置配置对象. + * + * @param config the config */ void setConfig(WxPayConfig config); /** *
    -   * 微信支付-申请退款
    +   * 微信支付-申请退款.
        * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
        * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund
        * 
    * * @param request 请求对象 - * @return 退款操作结果 + * @return 退款操作结果 wx pay refund result + * @throws WxPayException the wx pay exception */ - WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException; + WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayException; /** *
    -   * 微信支付-查询退款
    +   * 申请退款API(支持单品).
    +   * 详见 https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_103&index=3
    +   *
    +   * 应用场景
    +   * 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
    +   *
    +   * 注意:
    +   * 1、交易时间超过一年的订单无法提交退款;
    +   * 2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号。
    +   * 3、请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次
    +   *     错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
    +   * 4、每个支付订单的部分退款次数不能超过50次
    +   * 5、本接口支持单品优惠订单全额退款和单品优惠订单部分退款,推荐使用本接口,如果使用不支持单品优惠部分退款的历史接口,请看https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_4
    +   *
    +   * 接口地址
    +   * https://api.mch.weixin.qq.com/secapi/pay/refundv2
    +   * https://api2.mch.weixin.qq.com/secapi/pay/refundv2(备用域名)见跨城冗灾方案
    +   * 
    + * + * @param request 请求对象 + * @return 退款操作结果 wx pay refund result + * @throws WxPayException the wx pay exception + */ + WxPayRefundResult refundV2(WxPayRefundRequest request) throws WxPayException; + + /** + *
    +   * 微信支付-查询退款.
        * 应用场景:
        *  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,
        *  银行卡支付的退款3个工作日后重新查询退款状态。
    @@ -105,73 +327,95 @@ public interface WxPayService {
        * @param outTradeNo    商户订单号
        * @param outRefundNo   商户退款单号
        * @param refundId      微信退款单号
    -   * @return 退款信息
    +   * @return 退款信息 wx pay refund query result
    +   * @throws WxPayException the wx pay exception
        */
       WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId)
    -    throws WxErrorException;
    +    throws WxPayException;
     
       /**
    -   * 读取支付结果通知
    -   * 详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
    +   * 
    +   * 微信支付-查询退款(适合于需要自定义子商户号和子商户appid的情形).
    +   * 应用场景:
    +   *  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,
    +   *  银行卡支付的退款3个工作日后重新查询退款状态。
    +   * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/refundquery
    +   * 
    + * + * @param request 微信退款单号 + * @return 退款信息 wx pay refund query result + * @throws WxPayException the wx pay exception */ - WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException; + WxPayRefundQueryResult refundQuery(WxPayRefundQueryRequest request) throws WxPayException; /** - * 发送微信红包给个人用户 *
    -   * 文档详见:
    -   * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
    -   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
    -   * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4
    -   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack
    +   * 微信支付-查询退款API(支持单品).
    +   * 应用场景
    +   *    提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
    +   * 注意:
    +   * 1、本接口支持查询单品优惠相关退款信息,且仅支持按微信退款单号或商户退款单号查询,若继续调用老查询退款接口,
    +   *    请见https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_5
    +   * 2、请求频率限制:300qps,即每秒钟正常的退款查询请求次数不超过300次
    +   * 3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款查询请求不超过6次
    +   *
    +   * 接口地址
    +   * https://api.mch.weixin.qq.com/pay/refundqueryv2
    +   * https://api2.mch.weixin.qq.com/pay/refundqueryv2(备用域名)见跨城冗灾方案
    +   * 详见 https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_104&index=4
        * 
    * - * @param request 请求对象 + * @param request 微信退款单号 + * @return 退款信息 wx pay refund query result + * @throws WxPayException the wx pay exception */ - WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException; + WxPayRefundQueryResult refundQueryV2(WxPayRefundQueryRequest request) throws WxPayException; /** - *
    -   *   查询红包记录
    -   *   用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
    -   *   请求Url	https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo
    -   *   是否需要证书	是(证书及使用说明详见商户证书)
    -   *   请求方式	POST
    -   * 
    + * 解析支付结果通知. + * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 * - * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 + * @param xmlData the xml data + * @return the wx pay order notify result + * @throws WxPayException the wx pay exception */ - WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException; + WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) throws WxPayException; /** - *
    -   * 企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。
    -   * 比如目前的保险行业向客户退保、给付、理赔。
    -   * 企业付款将使用商户的可用余额,需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”https://pay.weixin.qq.com/进行操作。
    -   * 注意:与商户微信支付收款资金并非同一账户,需要单独充值。
    -   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
    -   * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
    -   * 
    + * 解析支付结果通知. + * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 * - * @param request 请求对象 + * @param xmlData the xml data + * @param signType 签名类型 + * @return the wx pay order notify result + * @throws WxPayException the wx pay exception */ - WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException; + WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String signType) throws WxPayException; /** - *
    -   * 查询企业付款API
    -   * 用于商户的企业付款操作进行结果查询,返回付款操作详细结果。
    -   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3
    -   * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo
    -   * 
    + * 解析退款结果通知 + * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9 * - * @param partnerTradeNo 商户订单号 + * @param xmlData the xml data + * @return the wx pay refund notify result + * @throws WxPayException the wx pay exception */ - WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException; + WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws WxPayException; + + /** + * 解析扫码支付回调通知 + * 详见https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4 + * + * @param xmlData the xml data + * @return the wx scan pay notify result + * @throws WxPayException the wx pay exception + */ + WxScanPayNotifyResult parseScanPayNotifyResult(String xmlData) throws WxPayException; /** *
    -   * 扫码支付模式一生成二维码的方法
    +   * 扫码支付模式一生成二维码的方法。
        * 二维码中的内容为链接,形式为:
        * weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
        * 其中XXXXX为商户需要填写的内容,商户将该链接生成二维码,如需要打印发布二维码,需要采用此格式。商户可调用第三方库生成二维码图片。
    @@ -179,15 +423,15 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * 
    * * @param productId 产品Id - * @param sideLength 要生成的二维码的边长,如果为空,则取默认值400 * @param logoFile 商户logo图片的文件对象,可以为空 - * @return 生成的二维码的字节数组 + * @param sideLength 要生成的二维码的边长,如果为空,则取默认值400 + * @return 生成的二维码的字节数组 byte [ ] */ byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength); /** *
    -   * 扫码支付模式一生成二维码的方法
    +   * 扫码支付模式一生成二维码的方法.
        * 二维码中的内容为链接,形式为:
        * weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
        * 其中XXXXX为商户需要填写的内容,商户将该链接生成二维码,如需要打印发布二维码,需要采用此格式。商户可调用第三方库生成二维码图片。
    @@ -195,13 +439,13 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * 
    * * @param productId 产品Id - * @return 生成的二维码URL连接 + * @return 生成的二维码URL连接 string */ String createScanPayQrcodeMode1(String productId); /** *
    -   * 扫码支付模式二生成二维码的方法
    +   * 扫码支付模式二生成二维码的方法.
        * 对应链接格式:weixin://wxpay/bizpayurl?sr=XXXXX。请商户调用第三方库将code_url生成二维码图片。
        * 该模式链接较短,生成的二维码打印到结账小票上的识别率较高。
        * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
    @@ -210,13 +454,13 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * @param codeUrl    微信返回的交易会话的二维码链接
        * @param logoFile   商户logo图片的文件对象,可以为空
        * @param sideLength 要生成的二维码的边长,如果为空,则取默认值400
    -   * @return 生成的二维码的字节数组
    +   * @return 生成的二维码的字节数组 byte [ ]
        */
       byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength);
     
       /**
        * 
    -   * 交易保障
    +   * 交易保障.
        * 应用场景:
        *  商户在调用微信支付提供的相关接口时,会得到微信支付返回的相关信息以及获得整个接口的响应时间。
        *  为提高整体的服务水平,协助商户一起提高服务质量,微信支付提供了相关接口调用耗时和返回信息的主动上报接口,
    @@ -224,12 +468,56 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * 接口地址: https://api.mch.weixin.qq.com/payitil/report
        * 是否需要证书:不需要
        * 
    + * + * @param request the request + * @throws WxPayException the wx pay exception + */ + void report(WxPayReportRequest request) throws WxPayException; + + /** + *
    +   * 下载对账单.
    +   * 商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。
    +   * 注意:
    +   * 1、微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账单中,跟原支付单订单号一致,bill_type为REVOKED;
    +   * 2、微信在次日9点启动生成前一天的对账单,建议商户10点后再获取;
    +   * 3、对账单中涉及金额的字段单位为“元”。
    +   * 4、对账单接口只能下载三个月以内的账单。
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/downloadbill
    +   * 详情请见: 下载对账单
    +   * 
    + * + * @param billDate 对账单日期 bill_date 下载对账单的日期,格式:20140603 + * @param billType 账单类型 bill_type ALL,返回当日所有订单信息,默认值,SUCCESS,返回当日成功支付的订单,REFUND,返回当日退款订单 + * @param tarType 压缩账单 tar_type 非必传参数,固定值:GZIP,返回格式为.gzip的压缩包账单。不传则默认为数据流形式。 + * @param deviceInfo 设备号 device_info 非必传参数,终端设备号 + * @return 对账内容原始字符串 + * @throws WxPayException the wx pay exception + */ + String downloadRawBill(String billDate, String billType, String tarType, String deviceInfo) throws WxPayException; + + /** + *
    +   * 下载对账单(适合于需要自定义子商户号和子商户appid的情形).
    +   * 商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。
    +   * 注意:
    +   * 1、微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账单中,跟原支付单订单号一致,bill_type为REVOKED;
    +   * 2、微信在次日9点启动生成前一天的对账单,建议商户10点后再获取;
    +   * 3、对账单中涉及金额的字段单位为“元”。
    +   * 4、对账单接口只能下载三个月以内的账单。
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/downloadbill
    +   * 详情请见: 下载对账单
    +   * 
    + * + * @param request 下载对账单请求 + * @return 对账内容原始字符串 + * @throws WxPayException the wx pay exception */ - void report(WxPayReportRequest request) throws WxErrorException; + String downloadRawBill(WxPayDownloadBillRequest request) throws WxPayException; /** *
    -   * 下载对账单
    +   * 下载对账单.
        * 商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。
        * 注意:
        * 1、微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账单中,跟原支付单订单号一致,bill_type为REVOKED;
    @@ -240,17 +528,75 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * 详情请见: 下载对账单
        * 
    * - * @param billDate 对账单日期 bill_date 下载对账单的日期,格式:20140603 - * @param billType 账单类型 bill_type ALL,返回当日所有订单信息,默认值,SUCCESS,返回当日成功支付的订单,REFUND,返回当日退款订单 - * @param tarType 压缩账单 tar_type 非必传参数,固定值:GZIP,返回格式为.gzip的压缩包账单。不传则默认为数据流形式。 - * @param deviceInfo 设备号 device_info 非必传参数,终端设备号 - * @return 保存到本地的临时文件 + * @param billDate 对账单日期 bill_date 下载对账单的日期,格式:20140603 + * @param billType 账单类型 bill_type ALL,返回当日所有订单信息,默认值,SUCCESS,返回当日成功支付的订单,REFUND,返回当日退款订单 + * @param tarType 压缩账单 tar_type 非必传参数,固定值:GZIP,返回格式为.gzip的压缩包账单。不传则默认为数据流形式。 + * @param deviceInfo 设备号 device_info 非必传参数,终端设备号 + * @return WxPayBillResult对象 wx pay bill result + * @throws WxPayException the wx pay exception */ - File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException; + WxPayBillResult downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxPayException; /** *
    -   * 提交刷卡支付
    +   * 下载对账单(适合于需要自定义子商户号和子商户appid的情形).
    +   * 商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。
    +   * 注意:
    +   * 1、微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账单中,跟原支付单订单号一致,bill_type为REVOKED;
    +   * 2、微信在次日9点启动生成前一天的对账单,建议商户10点后再获取;
    +   * 3、对账单中涉及金额的字段单位为“元”。
    +   * 4、对账单接口只能下载三个月以内的账单。
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/downloadbill
    +   * 详情请见: 下载对账单
    +   * 
    + * + * @param request 下载对账单请求 + * @return WxPayBillResult对象 wx pay bill result + * @throws WxPayException the wx pay exception + */ + WxPayBillResult downloadBill(WxPayDownloadBillRequest request) throws WxPayException; + + /** + *
    +   * 下载资金账单.
    +   * 商户可以通过该接口下载自2017年6月1日起 的历史资金流水账单。
    +   * 注意:
    +   * 1、资金账单中的数据反映的是商户微信账户资金变动情况;
    +   * 2、当日账单在次日上午9点开始生成,建议商户在上午10点以后获取;
    +   * 3、资金账单中涉及金额的字段单位为“元”。
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/downloadfundflow
    +   * 详情请见: 下载对账单
    +   * 
    + * + * @param billDate 资金账单日期 bill_date 下载对账单的日期,格式:20140603 + * @param accountType 资金账户类型 account_type Basic,基本账户,Operation,运营账户,Fees,手续费账户 + * @param tarType 压缩账单 tar_type 非必传参数,固定值:GZIP,返回格式为.gzip的压缩包账单。不传则默认为数据流形式。 + * @return WxPayFundFlowResult对象 wx pay fund flow result + * @throws WxPayException the wx pay exception + */ + WxPayFundFlowResult downloadFundFlow(String billDate, String accountType, String tarType) throws WxPayException; + + /** + *
    +   * 下载资金账单.
    +   * 商户可以通过该接口下载自2017年6月1日起 的历史资金流水账单。
    +   * 注意:
    +   * 1、资金账单中的数据反映的是商户微信账户资金变动情况;
    +   * 2、当日账单在次日上午9点开始生成,建议商户在上午10点以后获取;
    +   * 3、资金账单中涉及金额的字段单位为“元”。
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/downloadfundflow
    +   * 详情请见: 下载对账单
    +   * 
    + * + * @param request 下载资金流水请求 + * @return WxPayFundFlowResult对象 wx pay fund flow result + * @throws WxPayException the wx pay exception + */ + WxPayFundFlowResult downloadFundFlow(WxPayDownloadFundFlowRequest request) throws WxPayException; + + /** + *
    +   * 提交付款码支付.
        * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
        * 应用场景:
        * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
    @@ -259,12 +605,16 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * 接口地址:   https://api.mch.weixin.qq.com/pay/micropay
        * 是否需要证书:不需要。
        * 
    + * + * @param request the request + * @return the wx pay micropay result + * @throws WxPayException the wx pay exception */ - WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException; + WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayException; /** *
    -   * 撤销订单API
    +   * 撤销订单API.
        * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3
        * 应用场景:
        *  支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;
    @@ -275,12 +625,16 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *  接口链接 :https://api.mch.weixin.qq.com/secapi/pay/reverse
        *  是否需要证书:请求需要双向证书。
        * 
    + * + * @param request the request + * @return the wx pay order reverse result + * @throws WxPayException the wx pay exception */ - WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException; + WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException; /** *
    -   *  转换短链接
    +   *  转换短链接.
        *  文档地址:
        *     https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_9&index=8
        *  应用场景:
    @@ -288,40 +642,199 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *  接口地址:https://api.mch.weixin.qq.com/tools/shorturl
        *  是否需要证书:否
        * 
    + * * @param request 请求对象 + * @return the string + * @throws WxPayException the wx pay exception */ - String shorturl(WxPayShorturlRequest request) throws WxErrorException; + String shorturl(WxPayShorturlRequest request) throws WxPayException; /** *
    -   *  转换短链接
    +   *  转换短链接.
        * 
    - * @see WxPayService#shorturl(WxPayShorturlRequest) + * * @param longUrl 需要被压缩的网址 + * @return the string + * @throws WxPayException the wx pay exception + * @see WxPayService#shorturl(WxPayShorturlRequest) WxPayService#shorturl(WxPayShorturlRequest) */ - String shorturl(String longUrl) throws WxErrorException; + String shorturl(String longUrl) throws WxPayException; /** *
    -   * 授权码查询OPENID接口
    +   * 授权码查询OPENID接口.
        *    通过授权码查询公众号Openid,调用查询后,该授权码只能由此商户号发起扣款,直至授权码更新。
        * 文档地址:
        *    https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_13&index=9
        * 接口链接:
        *    https://api.mch.weixin.qq.com/tools/authcodetoopenid
        * 
    + * * @param request 请求对象 - * @return openid + * @return openid string + * @throws WxPayException the wx pay exception */ - String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException; + String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxPayException; /** *
    -   * 授权码查询OPENID接口
    +   * 授权码查询OPENID接口.
        * 
    - * @see WxPayService#authcode2Openid(WxPayAuthcode2OpenidRequest) + * * @param authCode 授权码 - * @return openid + * @return openid string + * @throws WxPayException the wx pay exception + * @see WxPayService#authcode2Openid(WxPayAuthcode2OpenidRequest) WxPayService#authcode2Openid(WxPayAuthcode2OpenidRequest) + */ + String authcode2Openid(String authCode) throws WxPayException; + + /** + *
    +   * 获取仿真测试系统的验签密钥.
    +   * 请求Url: https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey
    +   * 是否需要证书: 否
    +   * 请求方式: POST
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1
    +   * 
    + * + * @return the sandbox sign key + * @throws WxPayException the wx pay exception + */ + String getSandboxSignKey() throws WxPayException; + + /** + *
    +   * 发放代金券
    +   * 接口请求链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/send_coupon
    +   * 是否需要证书:请求需要双向证书。
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_3
    +   * 
    + * + * @param request the request + * @return the wx pay coupon send result + * @throws WxPayException the wx pay exception + */ + WxPayCouponSendResult sendCoupon(WxPayCouponSendRequest request) throws WxPayException; + + /** + *
    +   * 查询代金券批次.
    +   * 接口请求链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/query_coupon_stock
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_4
    +   * 
    + * + * @param request the request + * @return the wx pay coupon stock query result + * @throws WxPayException the wx pay exception + */ + WxPayCouponStockQueryResult queryCouponStock(WxPayCouponStockQueryRequest request) throws WxPayException; + + /** + *
    +   * 查询代金券信息.
    +   * 接口请求链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/querycouponsinfo
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_5
    +   * 
    + * + * @param request the request + * @return the wx pay coupon info query result + * @throws WxPayException the wx pay exception + */ + WxPayCouponInfoQueryResult queryCouponInfo(WxPayCouponInfoQueryRequest request) throws WxPayException; + + /** + * 获取微信请求数据,方便接口调用方获取处理. + * + * @return the wx api data + */ + WxPayApiData getWxApiData(); + + /** + *
    +   * 拉取订单评价数据.
    +   * 商户可以通过该接口拉取用户在微信支付交易记录中针对你的支付记录进行的评价内容。商户可结合商户系统逻辑对该内容数据进行存储、分析、展示、客服回访以及其他使用。如商户业务对评价内容有依赖,可主动引导用户进入微信支付交易记录进行评价。
    +   * 注意:
    +   * 1. 该内容所有权为提供内容的微信用户,商户在使用内容的过程中应遵从用户意愿
    +   * 2. 一次最多拉取200条评价数据,可根据时间区间分批次拉取
    +   * 3. 接口只能拉取最近三个月以内的评价数据
    +   * 接口链接:https://api.mch.weixin.qq.com/billcommentsp/batchquerycomment
    +   * 是否需要证书:需要
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=10
    +   * 
    + * + * @param beginDate 开始时间 + * @param endDate 结束时间 + * @param offset 位移 + * @param limit 条数,建议填null,否则接口会报签名错误 + * @return the string + * @throws WxPayException the wx pay exception + */ + String queryComment(Date beginDate, Date endDate, Integer offset, Integer limit) throws WxPayException; + + /** + *
    +   * 拉取订单评价数据.
    +   * 商户可以通过该接口拉取用户在微信支付交易记录中针对你的支付记录进行的评价内容。商户可结合商户系统逻辑对该内容数据进行存储、分析、展示、客服回访以及其他使用。如商户业务对评价内容有依赖,可主动引导用户进入微信支付交易记录进行评价。
    +   * 注意:
    +   * 1. 该内容所有权为提供内容的微信用户,商户在使用内容的过程中应遵从用户意愿
    +   * 2. 一次最多拉取200条评价数据,可根据时间区间分批次拉取
    +   * 3. 接口只能拉取最近三个月以内的评价数据
    +   * 接口链接:https://api.mch.weixin.qq.com/billcommentsp/batchquerycomment
    +   * 是否需要证书:需要
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=10
    +   * 
    + * + * @param request 查询请求 + * @return the string + * @throws WxPayException the wx pay exception + */ + String queryComment(WxPayQueryCommentRequest request) throws WxPayException; + + /** + *
    +   * 获取微信刷脸支付凭证.
    +   * 接口请求链接:https://payapp.weixin.qq.com/face/get_wxpayface_authinfo
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_5
    +   * 
    + * + * @param request the request + * @return the wx pay get face authinfo result + * @throws WxPayException the wx pay exception + */ + WxPayFaceAuthInfoResult getWxPayFaceAuthInfo(WxPayFaceAuthInfoRequest request) throws WxPayException; + + /** + *
    +   * 提交刷脸支付.
    +   * 文档地址:https://share.weiyun.com/5dxUgCw
    +   * 应用场景:
    +   * 用户在商超,便利店,餐饮等场景,在屏幕上通过刷脸完成支付。
    +   * 步骤1:用户在自助收银机上点击“刷脸支付”;
    +   * 步骤2:发起人脸识别,摄像头自动抓取识别用户人脸,提示用户输入11位手机号码;
    +   * 步骤3:商户收银系统提交刷脸支付;
    +   * 步骤4:微信支付后台收到支付请求,验证人脸信息,返回支付结果给商户收银系统。
    +   * 是否需要证书:不需要。
    +   * 
    + * + * @param request the request + * @return the wx pay facepay result + * @throws WxPayException the wx pay exception + */ + WxPayFacepayResult facepay(WxPayFacepayRequest request) throws WxPayException; + + /** + * 查询汇率 + *
    +   * 应用场景:商户网站的商品以外币标价时,通过该接口可以实时查询到微信使用的转换汇率。汇率更新时间为北京时间上午10:00,一天更新一次。
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app_jw.php?chapter=9_15&index=12
    +   * 接口链接:https://api.mch.weixin.qq.com/pay/queryexchagerate
    +   * 
    + * + * @param feeType 外币币种 + * @param date 日期,格式为yyyyMMdd,如2009年12月25日表示为20091225。时区为GMT+8 beijing + * @return . + * @throws WxPayException . */ - String authcode2Openid(String authCode) throws WxErrorException; + WxPayQueryExchangeRateResult queryExchangeRate(String feeType, String date) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/Applyment4SubServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/Applyment4SubServiceImpl.java new file mode 100644 index 0000000000..defb01a9fd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/Applyment4SubServiceImpl.java @@ -0,0 +1,68 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.applyment.*; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.Applyment4SubService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.net.URI; +import java.security.cert.X509Certificate; + +@Slf4j +@RequiredArgsConstructor +public class Applyment4SubServiceImpl implements Applyment4SubService { + + private static final Gson GSON = new GsonBuilder().create(); + private final WxPayService payService; + + private void encryptFiled(Object request) throws WxPayException { + + X509Certificate validCertificate = payService.getConfig().getVerifier().getValidCertificate(); + + RsaCryptoUtil.encryptFields(request, validCertificate); + } + + + @Override + public WxPayApplymentCreateResult createApply(WxPayApplyment4SubCreateRequest request) throws WxPayException { + String url = String.format("%s/v3/applyment4sub/applyment/", this.payService.getPayBaseUrl()); + + encryptFiled(request); + + String result = payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); + return GSON.fromJson(result, WxPayApplymentCreateResult.class); + } + + @Override + public ApplymentStateQueryResult queryApplyStatusByBusinessCode(String businessCode) throws WxPayException { + String url = String.format("%s/v3/applyment4sub/applyment/business_code/%s", this.payService.getPayBaseUrl(), businessCode); + String result = payService.getV3(URI.create(url)); + return GSON.fromJson(result, ApplymentStateQueryResult.class); + } + + @Override + public ApplymentStateQueryResult queryApplyStatusByApplymentId(String applymentId) throws WxPayException { + String url = String.format("%s/v3/applyment4sub/applyment/applyment_id/%s", this.payService.getPayBaseUrl(), applymentId); + String result = payService.getV3(URI.create(url)); + return GSON.fromJson(result, ApplymentStateQueryResult.class); + } + + @Override + public SettlementInfoResult querySettlementBySubMchid(String subMchid) throws WxPayException { + String url = String.format("%s/v3/apply4sub/sub_merchants/%s/settlement", this.payService.getPayBaseUrl(), subMchid); + String result = payService.getV3(URI.create(url)); + return GSON.fromJson(result, SettlementInfoResult.class); + } + + @Override + public void modifySettlement(String subMchid,ModifySettlementRequest request) throws WxPayException { + String url = String.format("%s/v3/apply4sub/sub_merchants/%s/modify-settlement", this.payService.getPayBaseUrl(), subMchid); + encryptFiled(request); + String result = payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java new file mode 100644 index 0000000000..b5012643ca --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -0,0 +1,876 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.github.binarywang.wxpay.bean.WxPayApiData; +import com.github.binarywang.wxpay.bean.coupon.*; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult; +import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; +import com.github.binarywang.wxpay.bean.request.*; +import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; +import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.*; +import com.github.binarywang.wxpay.util.SignUtils; +import com.github.binarywang.wxpay.util.XmlConfig; +import com.google.common.base.Joiner; +import com.google.common.collect.Maps; +import jodd.io.ZipUtil; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.zip.ZipException; + +import static com.github.binarywang.wxpay.constant.WxPayConstants.QUERY_COMMENT_DATE_FORMAT; +import static com.github.binarywang.wxpay.constant.WxPayConstants.TarType; + +/** + *
    + *  微信支付接口请求抽象实现类
    + * Created by Binary Wang on 2017-7-8.
    + * 
    + * + * @author Binary Wang + */ +public abstract class BaseWxPayServiceImpl implements WxPayService { + private static final String TOTAL_FUND_COUNT = "资金流水总笔数"; + + /** + * The Log. + */ + final Logger log = LoggerFactory.getLogger(this.getClass()); + /** + * The constant wxApiData. + */ + static ThreadLocal wxApiData = new ThreadLocal<>(); + + private EntPayService entPayService = new EntPayServiceImpl(this); + private ProfitSharingService profitSharingService = new ProfitSharingServiceImpl(this); + private RedpackService redpackService = new RedpackServiceImpl(this); + private PayScoreService payScoreService = new PayScoreServiceImpl(this); + private EcommerceService ecommerceService = new EcommerceServiceImpl(this); + + /** + * The Config. + */ + protected WxPayConfig config; + + @Override + public EntPayService getEntPayService() { + return entPayService; + } + + @Override + public ProfitSharingService getProfitSharingService() { + return profitSharingService; + } + + @Override + public PayScoreService getPayScoreService() { + return payScoreService; + } + + @Override + public RedpackService getRedpackService() { + return this.redpackService; + } + + @Override + public EcommerceService getEcommerceService() { + return ecommerceService; + } + + @Override + public void setEntPayService(EntPayService entPayService) { + this.entPayService = entPayService; + } + + @Override + public WxPayConfig getConfig() { + return this.config; + } + + @Override + public void setConfig(WxPayConfig config) { + this.config = config; + } + + @Override + public String getPayBaseUrl() { + if (this.getConfig().isUseSandboxEnv()) { + return this.getConfig().getPayBaseUrl() + "/sandboxnew"; + } + + return this.getConfig().getPayBaseUrl(); + } + + @Override + public WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/secapi/pay/refund"; + if (this.getConfig().isUseSandboxEnv()) { + url = this.getConfig().getPayBaseUrl() + "/sandboxnew/pay/refund"; + } + + String responseContent = this.post(url, request.toXML(), true); + WxPayRefundResult result = BaseWxPayResult.fromXML(responseContent, WxPayRefundResult.class); + result.composeRefundCoupons(); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayRefundResult refundV2(WxPayRefundRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/secapi/pay/refundv2"; + if (this.getConfig().isUseSandboxEnv()) { + url = this.getConfig().getPayBaseUrl() + "/sandboxnew/pay/refundv2"; + } + + String responseContent = this.post(url, request.toXML(), true); + WxPayRefundResult result = BaseWxPayResult.fromXML(responseContent, WxPayRefundResult.class); + result.composePromotionDetails(); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) + throws WxPayException { + WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.setTransactionId(StringUtils.trimToNull(transactionId)); + request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); + request.setRefundId(StringUtils.trimToNull(refundId)); + + return this.refundQuery(request); + } + + @Override + public WxPayRefundQueryResult refundQuery(WxPayRefundQueryRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/refundquery"; + String responseContent = this.post(url, request.toXML(), false); + WxPayRefundQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayRefundQueryResult.class); + result.composeRefundRecords(); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayRefundQueryResult refundQueryV2(WxPayRefundQueryRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/refundqueryv2"; + String responseContent = this.post(url, request.toXML(), false); + WxPayRefundQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayRefundQueryResult.class); + result.composePromotionDetails(); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) throws WxPayException { + return this.parseOrderNotifyResult(xmlData, null); + } + + @Override + public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String signType) throws WxPayException { + try { + log.debug("微信支付异步通知请求参数:{}", xmlData); + WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); + if (signType == null) { + if (result.getSignType() != null) { + // 如果解析的通知对象中signType有值,则使用它进行验签 + signType = result.getSignType(); + } else if (this.getConfig().getSignType() != null) { + // 如果配置中signType有值,则使用它进行验签 + signType = this.getConfig().getSignType(); + } + } + + log.debug("微信支付异步通知请求解析后的对象:{}", result); + result.checkResult(this, signType, false); + return result; + } catch (WxPayException e) { + throw e; + } catch (Exception e) { + throw new WxPayException("发生异常!", e); + } + } + + @Override + public WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws WxPayException { + try { + log.debug("微信支付退款异步通知参数:{}", xmlData); + WxPayRefundNotifyResult result; + if (XmlConfig.fastMode) { + result = BaseWxPayResult.fromXML(xmlData, WxPayRefundNotifyResult.class); + result.decryptReqInfo(this.getConfig().getMchKey()); + } else { + result = WxPayRefundNotifyResult.fromXML(xmlData, this.getConfig().getMchKey()); + } + log.debug("微信支付退款异步通知解析后的对象:{}", result); + return result; + } catch (Exception e) { + throw new WxPayException("发生异常," + e.getMessage(), e); + } + } + + @Override + public WxScanPayNotifyResult parseScanPayNotifyResult(String xmlData) throws WxPayException { + try { + log.debug("扫码支付回调通知请求参数:{}", xmlData); + WxScanPayNotifyResult result = BaseWxPayResult.fromXML(xmlData, WxScanPayNotifyResult.class); + log.debug("扫码支付回调通知解析后的对象:{}", result); + result.checkResult(this, this.getConfig().getSignType(), false); + return result; + } catch (WxPayException e) { + throw e; + } catch (Exception e) { + throw new WxPayException("发生异常," + e.getMessage(), e); + } + + } + + @Override + public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxPayException { + WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.setTransactionId(StringUtils.trimToNull(transactionId)); + + return this.queryOrder(request); + } + + @Override + public WxPayOrderQueryResult queryOrder(WxPayOrderQueryRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/orderquery"; + String responseContent = this.post(url, request.toXML(), false); + if (StringUtils.isBlank(responseContent)) { + throw new WxPayException("无响应结果"); + } + + WxPayOrderQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayOrderQueryResult.class); + result.composeCoupons(); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxPayException { + if (StringUtils.isBlank(outTradeNo)) { + throw new WxPayException("out_trade_no不能为空"); + } + + WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + + return this.closeOrder(request); + } + + @Override + public WxPayOrderCloseResult closeOrder(WxPayOrderCloseRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/closeorder"; + String responseContent = this.post(url, request.toXML(), false); + WxPayOrderCloseResult result = BaseWxPayResult.fromXML(responseContent, WxPayOrderCloseResult.class); + result.checkResult(this, request.getSignType(), true); + + return result; + } + + @Override + public T createOrder(WxPayUnifiedOrderRequest request) throws WxPayException { + WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); + String prepayId = unifiedOrderResult.getPrepayId(); + if (StringUtils.isBlank(prepayId)) { + throw new WxPayException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。", + unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); + } + + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = unifiedOrderResult.getNonceStr(); + switch (request.getTradeType()) { + case TradeType.MWEB: { + return (T) new WxPayMwebOrderResult(unifiedOrderResult.getMwebUrl()); + } + + case TradeType.NATIVE: { + return (T) new WxPayNativeOrderResult(unifiedOrderResult.getCodeURL()); + } + + case TradeType.APP: { + // APP支付绑定的是微信开放平台上的账号,APPID为开放平台上绑定APP后发放的参数 + String appId = unifiedOrderResult.getAppid(); + if (StringUtils.isNotEmpty(unifiedOrderResult.getSubAppId())) { + appId = unifiedOrderResult.getSubAppId(); + } + + Map configMap = new HashMap<>(8); + // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改 + String partnerId = unifiedOrderResult.getMchId(); + if (StringUtils.isNotEmpty(unifiedOrderResult.getSubMchId())) { + partnerId = unifiedOrderResult.getSubMchId(); + } + + configMap.put("prepayid", prepayId); + configMap.put("partnerid", partnerId); + String packageValue = "Sign=WXPay"; + configMap.put("package", packageValue); + configMap.put("timestamp", timestamp); + configMap.put("noncestr", nonceStr); + configMap.put("appid", appId); + + final WxPayAppOrderResult result = WxPayAppOrderResult.builder() + .sign(SignUtils.createSign(configMap, request.getSignType(), this.getConfig().getMchKey(), null)) + .prepayId(prepayId) + .partnerId(partnerId) + .appId(appId) + .packageValue(packageValue) + .timeStamp(timestamp) + .nonceStr(nonceStr) + .build(); + return (T) result; + } + + case TradeType.JSAPI: { + String signType = request.getSignType(); + if (signType == null) { + signType = SignType.MD5; + } + + String appid = unifiedOrderResult.getAppid(); + if (StringUtils.isNotEmpty(unifiedOrderResult.getSubAppId())) { + appid = unifiedOrderResult.getSubAppId(); + } + + WxPayMpOrderResult payResult = WxPayMpOrderResult.builder() + .appId(appid) + .timeStamp(timestamp) + .nonceStr(nonceStr) + .packageValue("prepay_id=" + prepayId) + .signType(signType) + .build(); + + payResult.setPaySign(SignUtils.createSign(payResult, signType, this.getConfig().getMchKey(), null)); + return (T) payResult; + } + + default: { + throw new WxPayException("该交易类型暂不支持"); + } + } + + } + + @Override + public T createOrder(TradeType.Specific specificTradeType, WxPayUnifiedOrderRequest request) throws WxPayException { + if (specificTradeType == null) { + throw new IllegalArgumentException("specificTradeType 不能为 null"); + } + request.setTradeType(specificTradeType.getType()); + return createOrder(request); + } + + @Override + public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/unifiedorder"; + String responseContent = this.post(url, request.toXML(), false); + WxPayUnifiedOrderResult result = BaseWxPayResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + @Deprecated + public Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxPayException { + WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); + String prepayId = unifiedOrderResult.getPrepayId(); + if (StringUtils.isBlank(prepayId)) { + throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。", + unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); + } + + Map payInfo = new HashMap<>(); + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = unifiedOrderResult.getNonceStr(); + if (TradeType.NATIVE.equals(request.getTradeType())) { + payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); + } else if (TradeType.APP.equals(request.getTradeType())) { + // APP支付绑定的是微信开放平台上的账号,APPID为开放平台上绑定APP后发放的参数 + String appId = getConfig().getAppId(); + Map configMap = new HashMap<>(); + // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改 + String partnerId = getConfig().getMchId(); + configMap.put("prepayid", prepayId); + configMap.put("partnerid", partnerId); + String packageValue = "Sign=WXPay"; + configMap.put("package", packageValue); + configMap.put("timestamp", timestamp); + configMap.put("noncestr", nonceStr); + configMap.put("appid", appId); + // 此map用于客户端与微信服务器交互 + payInfo.put("sign", SignUtils.createSign(configMap, request.getSignType(), this.getConfig().getMchKey(), null)); + payInfo.put("prepayId", prepayId); + payInfo.put("partnerId", partnerId); + payInfo.put("appId", appId); + payInfo.put("packageValue", packageValue); + payInfo.put("timeStamp", timestamp); + payInfo.put("nonceStr", nonceStr); + } else if (TradeType.JSAPI.equals(request.getTradeType())) { + payInfo.put("appId", unifiedOrderResult.getAppid()); + // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 + payInfo.put("timeStamp", timestamp); + payInfo.put("nonceStr", nonceStr); + payInfo.put("package", "prepay_id=" + prepayId); + payInfo.put("signType", request.getSignType()); + payInfo.put("paySign", SignUtils.createSign(payInfo, request.getSignType(), this.getConfig().getMchKey(), null)); + } + + return payInfo; + } + + @Override + public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { + String content = this.createScanPayQrcodeMode1(productId); + return this.createQrcode(content, logoFile, sideLength); + } + + @Override + public String createScanPayQrcodeMode1(String productId) { + //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX + StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); + Map params = Maps.newHashMap(); + params.put("appid", this.getConfig().getAppId()); + params.put("mch_id", this.getConfig().getMchId()); + params.put("product_id", productId); + //这里需要秒,10位数字 + params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000)); + params.put("nonce_str", String.valueOf(System.currentTimeMillis())); + + String sign = SignUtils.createSign(params, SignType.MD5, this.getConfig().getMchKey(), null); + params.put("sign", sign); + + for (String key : params.keySet()) { + codeUrl.append(key).append("=").append(params.get(key)).append("&"); + } + + String content = codeUrl.toString().substring(0, codeUrl.length() - 1); + log.debug("扫码支付模式一生成二维码的URL:{}", content); + return content; + } + + @Override + public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { + return this.createQrcode(codeUrl, logoFile, sideLength); + } + + private byte[] createQrcode(String content, File logoFile, Integer sideLength) { + if (sideLength == null || sideLength < 1) { + return QrcodeUtils.createQrcode(content, logoFile); + } + + return QrcodeUtils.createQrcode(content, sideLength, logoFile); + } + + @Override + public void report(WxPayReportRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/payitil/report"; + String responseContent = this.post(url, request.toXML(), false); + WxPayCommonResult result = BaseWxPayResult.fromXML(responseContent, WxPayCommonResult.class); + result.checkResult(this, request.getSignType(), true); + } + + @Override + public String downloadRawBill(String billDate, String billType, String tarType, String deviceInfo) + throws WxPayException { + return this.downloadRawBill(this.buildDownloadBillRequest(billDate, billType, tarType, deviceInfo)); + } + + @Override + public WxPayBillResult downloadBill(String billDate, String billType, String tarType, String deviceInfo) + throws WxPayException { + return this.downloadBill(this.buildDownloadBillRequest(billDate, billType, tarType, deviceInfo)); + } + + private WxPayDownloadBillRequest buildDownloadBillRequest(String billDate, String billType, String tarType, + String deviceInfo) { + WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); + request.setBillType(billType); + request.setBillDate(billDate); + request.setTarType(tarType); + request.setDeviceInfo(deviceInfo); + return request; + } + + @Override + public WxPayBillResult downloadBill(WxPayDownloadBillRequest request) throws WxPayException { + String responseContent = this.downloadRawBill(request); + + if (StringUtils.isEmpty(responseContent)) { + return null; + } + + return this.handleBill(request.getBillType(), responseContent); + } + + @Override + public String downloadRawBill(WxPayDownloadBillRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/downloadbill"; + + String responseContent; + if (TarType.GZIP.equals(request.getTarType())) { + responseContent = this.handleGzipBill(url, request.toXML()); + } else { + responseContent = this.post(url, request.toXML(), false); + if (responseContent.startsWith("<")) { + throw WxPayException.from(BaseWxPayResult.fromXML(responseContent, WxPayCommonResult.class)); + } + } + return responseContent; + } + + private WxPayBillResult handleBill(String billType, String responseContent) { + return WxPayBillResult.fromRawBillResultString(responseContent, billType); + } + + private String handleGzipBill(String url, String requestStr) throws WxPayException { + try { + byte[] responseBytes = this.postForBytes(url, requestStr, false); + Path tempDirectory = Files.createTempDirectory("bill"); + Path path = Paths.get(tempDirectory.toString(), System.currentTimeMillis() + ".gzip"); + Files.write(path, responseBytes); + try { + List allLines = Files.readAllLines(ZipUtil.ungzip(path.toFile()).toPath(), StandardCharsets.UTF_8); + return Joiner.on("\n").join(allLines); + } catch (ZipException e) { + if (e.getMessage().contains("Not in GZIP format")) { + throw WxPayException.from(BaseWxPayResult.fromXML(new String(responseBytes, StandardCharsets.UTF_8), + WxPayCommonResult.class)); + } else { + throw new WxPayException("解压zip文件出错!", e); + } + } + } catch (Exception e) { + throw new WxPayException("解析对账单文件时出错!", e); + } + } + + @Override + public WxPayFundFlowResult downloadFundFlow(String billDate, String accountType, String tarType) throws WxPayException { + + WxPayDownloadFundFlowRequest request = new WxPayDownloadFundFlowRequest(); + request.setBillDate(billDate); + request.setAccountType(accountType); + request.setTarType(tarType); + + return this.downloadFundFlow(request); + } + + @Override + public WxPayFundFlowResult downloadFundFlow(WxPayDownloadFundFlowRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/downloadfundflow"; + + String responseContent; + if (TarType.GZIP.equals(request.getTarType())) { + responseContent = this.handleGzipFundFlow(url, request.toXML()); + } else { + responseContent = this.post(url, request.toXML(), true); + if (responseContent.startsWith("<")) { + throw WxPayException.from(BaseWxPayResult.fromXML(responseContent, WxPayCommonResult.class)); + } + } + + return this.handleFundFlow(responseContent); + } + + private String handleGzipFundFlow(String url, String requestStr) throws WxPayException { + try { + byte[] responseBytes = this.postForBytes(url, requestStr, true); + Path tempDirectory = Files.createTempDirectory("fundFlow"); + Path path = Paths.get(tempDirectory.toString(), System.currentTimeMillis() + ".gzip"); + Files.write(path, responseBytes); + + try { + List allLines = Files.readAllLines(ZipUtil.ungzip(path.toFile()).toPath(), StandardCharsets.UTF_8); + return Joiner.on("\n").join(allLines); + } catch (ZipException e) { + if (e.getMessage().contains("Not in GZIP format")) { + throw WxPayException.from(BaseWxPayResult.fromXML(new String(responseBytes, StandardCharsets.UTF_8), + WxPayCommonResult.class)); + } else { + throw new WxPayException("解压zip文件出错", e); + } + } + } catch (WxPayException wxPayException) { + throw wxPayException; + } catch (Exception e) { + throw new WxPayException("解压zip文件出错", e); + } + } + + private WxPayFundFlowResult handleFundFlow(String responseContent) { + WxPayFundFlowResult wxPayFundFlowResult = new WxPayFundFlowResult(); + + String listStr = ""; + String objStr = ""; + + if (StringUtils.isNotBlank(responseContent) && responseContent.contains(TOTAL_FUND_COUNT)) { + listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_FUND_COUNT)); + objStr = responseContent.substring(responseContent.indexOf(TOTAL_FUND_COUNT)); + } + /* + * 记账时间:2018-02-01 04:21:23 微信支付业务单号:50000305742018020103387128253 资金流水单号:1900009231201802015884652186 业务名称:退款 + * 业务类型:退款 收支类型:支出 收支金额(元):0.02 账户结余(元):0.17 资金变更提交申请人:system 备注:缺货 业务凭证号:REF4200000068201801293084726067 + * 参考以上格式进行取值 + */ + List wxPayFundFlowBaseResultList = new LinkedList<>(); + // 去空格 + String newStr = listStr.replaceAll(",", " "); + // 数据分组 + String[] tempStr = newStr.split("`"); + // 分组标题 + String[] t = tempStr[0].split(" "); + // 计算循环次数 + int j = tempStr.length / t.length; + // 纪录数组下标 + int k = 1; + for (int i = 0; i < j; i++) { + WxPayFundFlowBaseResult wxPayFundFlowBaseResult = new WxPayFundFlowBaseResult(); + + wxPayFundFlowBaseResult.setBillingTime(tempStr[k].trim()); + wxPayFundFlowBaseResult.setBizTransactionId(tempStr[k + 1].trim()); + wxPayFundFlowBaseResult.setFundFlowId(tempStr[k + 2].trim()); + wxPayFundFlowBaseResult.setBizName(tempStr[k + 3].trim()); + wxPayFundFlowBaseResult.setBizType(tempStr[k + 4].trim()); + wxPayFundFlowBaseResult.setFinancialType(tempStr[k + 5].trim()); + wxPayFundFlowBaseResult.setFinancialFee(tempStr[k + 6].trim()); + wxPayFundFlowBaseResult.setAccountBalance(tempStr[k + 7].trim()); + wxPayFundFlowBaseResult.setFundApplicant(tempStr[k + 8].trim()); + wxPayFundFlowBaseResult.setMemo(tempStr[k + 9].trim()); + wxPayFundFlowBaseResult.setBizVoucherId(tempStr[k + 10].trim()); + + wxPayFundFlowBaseResultList.add(wxPayFundFlowBaseResult); + k += t.length; + } + wxPayFundFlowResult.setWxPayFundFlowBaseResultList(wxPayFundFlowBaseResultList); + + /* + * 资金流水总笔数,收入笔数,收入金额,支出笔数,支出金额 `20.0,`17.0,`0.35,`3.0,`0.18 + * 参考以上格式进行取值 + */ + String totalStr = objStr.replaceAll(",", " "); + String[] totalTempStr = totalStr.split("`"); + wxPayFundFlowResult.setTotalRecord(totalTempStr[1]); + wxPayFundFlowResult.setIncomeRecord(totalTempStr[2]); + wxPayFundFlowResult.setIncomeAmount(totalTempStr[3]); + wxPayFundFlowResult.setExpenditureRecord(totalTempStr[4]); + wxPayFundFlowResult.setExpenditureAmount(totalTempStr[5]); + + return wxPayFundFlowResult; + + } + + @Override + public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/micropay"; + String responseContent = this.post(url, request.toXML(), false); + WxPayMicropayResult result = BaseWxPayResult.fromXML(responseContent, WxPayMicropayResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/secapi/pay/reverse"; + String responseContent = this.post(url, request.toXML(), true); + WxPayOrderReverseResult result = BaseWxPayResult.fromXML(responseContent, WxPayOrderReverseResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public String shorturl(WxPayShorturlRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/tools/shorturl"; + String responseContent = this.post(url, request.toXML(), false); + WxPayShorturlResult result = BaseWxPayResult.fromXML(responseContent, WxPayShorturlResult.class); + result.checkResult(this, request.getSignType(), true); + return result.getShortUrl(); + } + + @Override + public String shorturl(String longUrl) throws WxPayException { + return this.shorturl(new WxPayShorturlRequest(longUrl)); + } + + @Override + public String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/tools/authcodetoopenid"; + String responseContent = this.post(url, request.toXML(), false); + WxPayAuthcode2OpenidResult result = BaseWxPayResult.fromXML(responseContent, WxPayAuthcode2OpenidResult.class); + result.checkResult(this, request.getSignType(), true); + return result.getOpenid(); + } + + @Override + public String authcode2Openid(String authCode) throws WxPayException { + return this.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); + } + + @Override + public String getSandboxSignKey() throws WxPayException { + WxPayDefaultRequest request = new WxPayDefaultRequest(); + request.checkAndSign(this.getConfig()); + + String url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"; + String responseContent = this.post(url, request.toXML(), false); + WxPaySandboxSignKeyResult result = BaseWxPayResult.fromXML(responseContent, WxPaySandboxSignKeyResult.class); + result.checkResult(this, request.getSignType(), true); + return result.getSandboxSignKey(); + } + + @Override + public WxPayCouponSendResult sendCoupon(WxPayCouponSendRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/send_coupon"; + String responseContent = this.post(url, request.toXML(), true); + WxPayCouponSendResult result = BaseWxPayResult.fromXML(responseContent, WxPayCouponSendResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayCouponStockQueryResult queryCouponStock(WxPayCouponStockQueryRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/query_coupon_stock"; + String responseContent = this.post(url, request.toXML(), false); + WxPayCouponStockQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayCouponStockQueryResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayCouponInfoQueryResult queryCouponInfo(WxPayCouponInfoQueryRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/querycouponsinfo"; + String responseContent = this.post(url, request.toXML(), false); + WxPayCouponInfoQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayCouponInfoQueryResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayApiData getWxApiData() { + try { + return wxApiData.get(); + } finally { + //一般来说,接口请求会在一个线程内进行,这种情况下,每个线程get的会是之前所存入的数据, + // 但以防万一有同一线程多次请求的问题,所以每次获取完数据后移除对应数据 + wxApiData.remove(); + } + } + + @Override + public String queryComment(Date beginDate, Date endDate, Integer offset, Integer limit) throws WxPayException { + WxPayQueryCommentRequest request = new WxPayQueryCommentRequest(); + request.setBeginTime(QUERY_COMMENT_DATE_FORMAT.format(beginDate)); + request.setEndTime(QUERY_COMMENT_DATE_FORMAT.format(endDate)); + request.setOffset(offset); + request.setLimit(limit); + + return this.queryComment(request); + } + + @Override + public String queryComment(WxPayQueryCommentRequest request) throws WxPayException { + request.setSignType(SignType.HMAC_SHA256);// 签名类型,目前仅支持HMAC-SHA256,默认就是HMAC-SHA256 + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/billcommentsp/batchquerycomment"; + String responseContent = this.post(url, request.toXML(), true); + if (responseContent.startsWith("<")) { + throw WxPayException.from(BaseWxPayResult.fromXML(responseContent, WxPayCommonResult.class)); + } + + return responseContent; + } + + @Override + public WxPayFaceAuthInfoResult getWxPayFaceAuthInfo(WxPayFaceAuthInfoRequest request) throws WxPayException { + if (StringUtils.isEmpty(request.getSignType())) { + request.setSignType(WxPayConstants.SignType.MD5); + } + + request.checkAndSign(this.getConfig()); + String url = "https://payapp.weixin.qq.com/face/get_wxpayface_authinfo"; + String responseContent = this.post(url, request.toXML(), false); + WxPayFaceAuthInfoResult result = BaseWxPayResult.fromXML(responseContent, WxPayFaceAuthInfoResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayFacepayResult facepay(WxPayFacepayRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/facepay"; + String responseContent = this.post(url, request.toXML(), false); + WxPayFacepayResult result = BaseWxPayResult.fromXML(responseContent, WxPayFacepayResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } + + @Override + public WxPayQueryExchangeRateResult queryExchangeRate(String feeType, String date) throws WxPayException { + WxPayQueryExchangeRateRequest request = new WxPayQueryExchangeRateRequest(); + request.setFeeType(feeType); + request.setDate(date); + + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/queryexchagerate"; + String responseContent = this.post(url, request.toXML(), false); + WxPayQueryExchangeRateResult result = BaseWxPayResult.fromXML(responseContent, WxPayQueryExchangeRateResult.class); + result.checkResult(this, request.getSignType(), true); + return result; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java new file mode 100644 index 0000000000..9631631272 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -0,0 +1,45 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.EcommerceService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import lombok.RequiredArgsConstructor; + +import java.net.URI; + +@RequiredArgsConstructor +public class EcommerceServiceImpl implements EcommerceService { + private static final Gson GSON = new GsonBuilder().create(); + private final WxPayService payService; + + @Override + public ApplymentsResult createApply(ApplymentsRequest request) throws WxPayException { + String url = String.format("%s/v3/ecommerce/applyments/", this.payService.getPayBaseUrl()); + RsaCryptoUtil.encryptFields(request, this.payService.getConfig().getVerifier().getValidCertificate()); + + String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); + return GSON.fromJson(result, ApplymentsResult.class); + } + + @Override + public ApplymentsStatusResult queryApplyStatusByApplymentId(String applymentId) throws WxPayException { + String url = String.format("%s/v3/ecommerce/applyments/%s", this.payService.getPayBaseUrl(), applymentId); + String result = this.payService.getV3(URI.create(url)); + return GSON.fromJson(result, ApplymentsStatusResult.class); + } + + @Override + public ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo) throws WxPayException { + String url = String.format("%s/v3/ecommerce/applyments/out-request-no/%s", this.payService.getPayBaseUrl(), outRequestNo); + String result = this.payService.getV3(URI.create(url)); + return GSON.fromJson(result, ApplymentsStatusResult.class); + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java new file mode 100644 index 0000000000..5e768bef99 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java @@ -0,0 +1,188 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.entpay.*; +import com.github.binarywang.wxpay.bean.request.WxPayDefaultRequest; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.EntPayService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.util.SignUtils; +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; + +import javax.crypto.Cipher; +import java.io.File; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.PublicKey; +import java.security.Security; + +/** + *
    + *  Created by BinaryWang on 2017/12/19.
    + * 
    + * + * @author Binary Wang + */ +public class EntPayServiceImpl implements EntPayService { + private WxPayService payService; + + /** + * Instantiates a new Ent pay service. + * + * @param payService the pay service + */ + public EntPayServiceImpl(WxPayService payService) { + this.payService = payService; + } + + @Override + public EntPayResult entPay(EntPayRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; + + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayResult result = BaseWxPayResult.fromXML(responseContent, EntPayResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public EntPayQueryResult queryEntPay(String partnerTradeNo) throws WxPayException { + EntPayQueryRequest request = new EntPayQueryRequest(); + request.setPartnerTradeNo(partnerTradeNo); + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public EntPayQueryResult queryEntPay(EntPayQueryRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public String getPublicKey() throws WxPayException { + WxPayDefaultRequest request = new WxPayDefaultRequest(); + request.setMchId(this.payService.getConfig().getMchId()); + request.setNonceStr(String.valueOf(System.currentTimeMillis())); + + request.checkAndSign(this.payService.getConfig()); + + String url = "https://fraud.mch.weixin.qq.com/risk/getpublickey"; + String responseContent = this.payService.post(url, request.toXML(), true); + GetPublicKeyResult result = BaseWxPayResult.fromXML(responseContent, GetPublicKeyResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result.getPubKey(); + } + + @Override + public EntPayBankResult payBank(EntPayBankRequest request) throws WxPayException { + File publicKeyFile = this.buildPublicKeyFile(); + request.setEncBankNo(this.encryptRSA(publicKeyFile, request.getEncBankNo())); + request.setEncTrueName(this.encryptRSA(publicKeyFile, request.getEncTrueName())); + publicKeyFile.deleteOnExit(); + + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaysptrans/pay_bank"; + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayBankResult result = BaseWxPayResult.fromXML(responseContent, EntPayBankResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public EntPayBankQueryResult queryPayBank(String partnerTradeNo) throws WxPayException { + EntPayBankQueryRequest request = new EntPayBankQueryRequest(); + request.setPartnerTradeNo(partnerTradeNo); + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaysptrans/query_bank"; + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayBankQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayBankQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public EntPayBankQueryResult queryPayBank(EntPayBankQueryRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaysptrans/query_bank"; + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayBankQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayBankQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public EntPayRedpackResult sendEnterpriseRedpack(EntPayRedpackRequest request) throws WxPayException { + //企业微信签名,需要在请求签名之前 + request.setNonceStr(String.valueOf(System.currentTimeMillis())); + request.setWorkWxSign(SignUtils.createEntSign(request.getActName(), request.getMchBillNo(), request.getMchId(), request.getNonceStr(), request.getReOpenid(), request.getTotalAmount(), request.getWxAppId(), payService.getConfig().getEntPayKey(), WxPayConstants.SignType.MD5)); + + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendworkwxredpack"; + String responseContent = this.payService.post(url, request.toXML(), true); + final EntPayRedpackResult result = BaseWxPayResult.fromXML(responseContent, EntPayRedpackResult.class); + + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public EntPayRedpackQueryResult queryEnterpriseRedpack(EntPayRedpackQueryRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/queryworkwxredpack"; + String responseContent = this.payService.post(url, request.toXML(), true); + final EntPayRedpackQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayRedpackQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + private String encryptRSA(File publicKeyFile, String srcString) throws WxPayException { + try { + Security.addProvider(new BouncyCastleProvider()); + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding"); + try (PEMParser reader = new PEMParser(new FileReader(publicKeyFile))) { + final PublicKey publicKey = new JcaPEMKeyConverter().setProvider("BC") + .getPublicKey((SubjectPublicKeyInfo) reader.readObject()); + + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encrypt = cipher.doFinal(srcString.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeBase64String(encrypt); + } + } catch (Exception e) { + throw new WxPayException("加密出错", e); + } + } + + private File buildPublicKeyFile() throws WxPayException { + try { + String publicKeyStr = this.getPublicKey(); + Path tmpFile = Files.createTempFile("payToBank", ".pem"); + Files.write(tmpFile, publicKeyStr.getBytes(StandardCharsets.UTF_8)); + return tmpFile.toFile(); + } catch (Exception e) { + throw new WxPayException("生成加密公钥文件时发生异常", e); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MerchantMediaServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MerchantMediaServiceImpl.java new file mode 100644 index 0000000000..863b706a28 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MerchantMediaServiceImpl.java @@ -0,0 +1,44 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.media.ImageUploadResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.MerchantMediaService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.WechatPayUploadHttpPost; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +/** + * 微信支付-媒体文件上传service + * @author zhouyongshen + */ +@Slf4j +@RequiredArgsConstructor +public class MerchantMediaServiceImpl implements MerchantMediaService { + + private final WxPayService payService; + + @Override + public ImageUploadResult imageUploadV3(File imageFile) throws WxPayException,IOException { + String url = String.format("%s/v3/merchant/media/upload", this.payService.getPayBaseUrl()); + + try (FileInputStream s1 = new FileInputStream(imageFile)) { + String sha256 = DigestUtils.sha256Hex(s1); + try (InputStream s2 = new FileInputStream(imageFile)) { + WechatPayUploadHttpPost request = new WechatPayUploadHttpPost.Builder(URI.create(url)) + .withImage(imageFile.getName(), sha256, s2) + .build(); + String result = this.payService.postV3(url, request); + return ImageUploadResult.fromJson(result); + } + } + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java new file mode 100644 index 0000000000..7fa7efa58b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java @@ -0,0 +1,169 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.payscore.PayScoreNotifyData; +import com.github.binarywang.wxpay.bean.payscore.WxPayScoreRequest; +import com.github.binarywang.wxpay.bean.payscore.WxPayScoreResult; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.PayScoreService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.AesUtils; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.utils.URIBuilder; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.GeneralSecurityException; +import java.util.HashMap; +import java.util.Map; + +/** + * @author doger.wang + * @date 2020/5/14 9:43 + */ +@RequiredArgsConstructor +public class PayScoreServiceImpl implements PayScoreService { + private final WxPayService payService; + + @Override + public WxPayScoreResult createServiceOrder(WxPayScoreRequest request) throws WxPayException { + boolean needUserConfirm = request.isNeedUserConfirm(); + WxPayConfig config = this.payService.getConfig(); + String url = this.payService.getPayBaseUrl() + "/v3/payscore/serviceorder"; + request.setAppid(config.getAppId()); + request.setServiceId(config.getServiceId()); + request.setNotifyUrl(config.getPayScoreNotifyUrl()); + String result = this.payService.postV3(url, request.toJson()); + WxPayScoreResult wxPayScoreCreateResult = WxPayScoreResult.fromJson(result); + + //补充算一下签名给小程序跳转用 + String currentTimeMillis = System.currentTimeMillis() + ""; + Map signMap = new HashMap<>(8); + signMap.put("mch_id", config.getMchId()); + if (needUserConfirm) { + signMap.put("package", wxPayScoreCreateResult.getPackageX()); + } else { + signMap.put("service_id", config.getServiceId()); + signMap.put("out_order_no", request.getOutOrderNo()); + } + signMap.put("timestamp", currentTimeMillis); + signMap.put("nonce_str", currentTimeMillis); + signMap.put("sign_type", "HMAC-SHA256"); + String sign = AesUtils.createSign(signMap, config.getMchKey()); + signMap.put("sign", sign); + wxPayScoreCreateResult.setPayScoreSignInfo(signMap); + return wxPayScoreCreateResult; + } + + @Override + public WxPayScoreResult queryServiceOrder(String outOrderNo, String queryId) throws WxPayException { + WxPayConfig config = this.payService.getConfig(); + String url = this.payService.getPayBaseUrl() + "/v3/payscore/serviceorder"; + + URIBuilder uriBuilder; + try { + uriBuilder = new URIBuilder(url); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + + if (StringUtils.isAllEmpty(outOrderNo, queryId) || !StringUtils.isAnyEmpty(outOrderNo, queryId)) { + throw new WxPayException("out_order_no,query_id不允许都填写或都不填写"); + } + if (StringUtils.isNotEmpty(outOrderNo)) { + uriBuilder.setParameter("out_order_no", outOrderNo); + } + if (StringUtils.isNotEmpty(queryId)) { + uriBuilder.setParameter("query_id", queryId); + } + uriBuilder.setParameter("service_id", config.getServiceId()); + uriBuilder.setParameter("appid", config.getAppId()); + try { + String result = payService.getV3(uriBuilder.build()); + return WxPayScoreResult.fromJson(result); + } catch (URISyntaxException e) { + throw new WxPayException("未知异常!", e); + } + + } + + @Override + public WxPayScoreResult cancelServiceOrder(String outOrderNo, String reason) throws WxPayException { + WxPayConfig config = this.payService.getConfig(); + String url = String.format("%s/v3/payscore/serviceorder/%s/cancel", this.payService.getPayBaseUrl(), outOrderNo); + Map map = new HashMap<>(4); + map.put("appid", config.getAppId()); + map.put("service_id", config.getServiceId()); + map.put("reason", reason); + String result = payService.postV3(url, WxGsonBuilder.create().toJson(map)); + return WxPayScoreResult.fromJson(result); + } + + @Override + public WxPayScoreResult modifyServiceOrder(WxPayScoreRequest request) throws WxPayException { + WxPayConfig config = this.payService.getConfig(); + String outOrderNo = request.getOutOrderNo(); + String url = String.format("%s/v3/payscore/serviceorder/%s/modify", this.payService.getPayBaseUrl(), outOrderNo); + request.setAppid(config.getAppId()); + request.setServiceId(config.getServiceId()); + request.setOutOrderNo(null); + String result = payService.postV3(url, request.toJson()); + return WxPayScoreResult.fromJson(result); + } + + @Override + public WxPayScoreResult completeServiceOrder(WxPayScoreRequest request) throws WxPayException { + WxPayConfig config = this.payService.getConfig(); + String outOrderNo = request.getOutOrderNo(); + String url = String.format("%s/v3/payscore/serviceorder/%s/complete", this.payService.getPayBaseUrl(), outOrderNo); + request.setAppid(config.getAppId()); + request.setServiceId(config.getServiceId()); + request.setOutOrderNo(null); + String result = payService.postV3(url, request.toJson()); + return WxPayScoreResult.fromJson(result); + } + + @Override + public WxPayScoreResult payServiceOrder(String outOrderNo) throws WxPayException { + WxPayConfig config = this.payService.getConfig(); + String url = String.format("%s/v3/payscore/serviceorder/%s/pay", this.payService.getPayBaseUrl(), outOrderNo); + Map map = new HashMap<>(2); + map.put("appid", config.getAppId()); + map.put("service_id", config.getServiceId()); + String result = payService.postV3(url, WxGsonBuilder.create().toJson(map)); + return WxPayScoreResult.fromJson(result); + } + + @Override + public WxPayScoreResult syncServiceOrder(WxPayScoreRequest request) throws WxPayException { + WxPayConfig config = this.payService.getConfig(); + String outOrderNo = request.getOutOrderNo(); + String url = String.format("%s/v3/payscore/serviceorder/%s/sync", this.payService.getPayBaseUrl(), outOrderNo); + request.setAppid(config.getAppId()); + request.setServiceId(config.getServiceId()); + request.setOutOrderNo(null); + String result = payService.postV3(url, request.toJson()); + return WxPayScoreResult.fromJson(result); + } + + @Override + public PayScoreNotifyData parseNotifyData(String data) { + return WxGsonBuilder.create().fromJson(data, PayScoreNotifyData.class); + } + + @Override + public WxPayScoreResult decryptNotifyDataResource(PayScoreNotifyData data) throws WxPayException { + PayScoreNotifyData.Resource resource = data.getResource(); + String cipherText = resource.getCipherText(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String apiV3Key = this.payService.getConfig().getApiV3Key(); + try { + return WxPayScoreResult.fromJson(AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key)); + } catch (GeneralSecurityException | IOException e) { + throw new WxPayException("解析报文异常!", e); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java new file mode 100644 index 0000000000..3500ad41ef --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java @@ -0,0 +1,110 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.profitsharing.*; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.ProfitSharingService; +import com.github.binarywang.wxpay.service.WxPayService; + +/** + * @author Wang GuangXin 2019/10/22 10:13 + * @version 1.0 + */ +public class ProfitSharingServiceImpl implements ProfitSharingService { + private WxPayService payService; + + public ProfitSharingServiceImpl(WxPayService payService) { + this.payService = payService; + } + + @Override + public ProfitSharingResult profitSharing(ProfitSharingRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/secapi/pay/profitsharing"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingResult multiProfitSharing(ProfitSharingRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/secapi/pay/multiprofitsharing"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingResult profitSharingFinish(ProfitSharingFinishRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/secapi/pay/profitsharingfinish"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReceiverResult addReceiver(ProfitSharingReceiverRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingaddreceiver"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingReceiverResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReceiverResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReceiverResult removeReceiver(ProfitSharingReceiverRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingremovereceiver"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingReceiverResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReceiverResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingQueryResult profitSharingQuery(ProfitSharingQueryRequest request) throws WxPayException { + request.setAppid(null); + + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingquery"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingQueryResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingQueryResult.class); + result.formatReceivers(); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReturnResult profitSharingReturn(ProfitSharingReturnRequest returnRequest) throws WxPayException { + returnRequest.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/secapi/pay/profitsharingreturn"; + + String responseContent = this.payService.post(url, returnRequest.toXML(), true); + ProfitSharingReturnResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReturnResult.class); + result.checkResult(this.payService, returnRequest.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReturnResult profitSharingReturnQuery(ProfitSharingReturnQueryRequest queryRequest) throws WxPayException { + queryRequest.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingreturnquery"; + + String responseContent = this.payService.post(url, queryRequest.toXML(), true); + ProfitSharingReturnResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReturnResult.class); + result.checkResult(this.payService, queryRequest.getSignType(), true); + return result; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImpl.java new file mode 100644 index 0000000000..03ce8333e2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImpl.java @@ -0,0 +1,72 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.request.WxPayRedpackQueryRequest; +import com.github.binarywang.wxpay.bean.request.WxPaySendMiniProgramRedpackRequest; +import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendMiniProgramRedpackResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.RedpackService; +import com.github.binarywang.wxpay.service.WxPayService; +import lombok.RequiredArgsConstructor; + +/** + * 老板加点注释吧. + * + * @author Binary Wang + * @date 2019-12-26 + */ +@RequiredArgsConstructor +public class RedpackServiceImpl implements RedpackService { + private final WxPayService payService; + + @Override + public WxPaySendMiniProgramRedpackResult sendMiniProgramRedpack(WxPaySendMiniProgramRedpackRequest request) + throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendminiprogramhb"; + String responseContent = this.payService.post(url, request.toXML(), true); + + WxPaySendMiniProgramRedpackResult result = BaseWxPayResult.fromXML(responseContent, WxPaySendMiniProgramRedpackResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack"; + if (request.getAmtType() != null) { + //裂变红包 + url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; + } + + String responseContent = this.payService.post(url, request.toXML(), true); + final WxPaySendRedpackResult result = BaseWxPayResult.fromXML(responseContent, WxPaySendRedpackResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException { + WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); + request.setMchBillNo(mchBillNo); + return this.queryRedpack(request); + } + + @Override + public WxPayRedpackQueryResult queryRedpack(WxPayRedpackQueryRequest request) throws WxPayException { + request.setBillType(WxPayConstants.BillType.MCHT); + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; + String responseContent = this.payService.post(url, request.toXML(), true); + WxPayRedpackQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayRedpackQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java new file mode 100644 index 0000000000..c037134732 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java @@ -0,0 +1,268 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.WxPayApiData; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.google.gson.JsonObject; +import jodd.util.Base64; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpStatus; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.SSLContext; +import java.net.URI; +import java.nio.charset.StandardCharsets; + +/** + *
    + * 微信支付请求实现类,apache httpclient实现.
    + * Created by Binary Wang on 2016/7/28.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl { + + @Override + public byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException { + try { + HttpClientBuilder httpClientBuilder = createHttpClientBuilder(useKey); + HttpPost httpPost = this.createHttpPost(url, requestStr); + try (CloseableHttpClient httpClient = httpClientBuilder.build()) { + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + final byte[] bytes = EntityUtils.toByteArray(response.getEntity()); + final String responseData = Base64.encodeToString(bytes); + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseData); + wxApiData.set(new WxPayApiData(url, requestStr, responseData, null)); + return bytes; + } + } finally { + httpPost.releaseConnection(); + } + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage())); + throw new WxPayException(e.getMessage(), e); + } + } + + @Override + public String post(String url, String requestStr, boolean useKey) throws WxPayException { + try { + HttpClientBuilder httpClientBuilder = this.createHttpClientBuilder(useKey); + HttpPost httpPost = this.createHttpPost(url, requestStr); + try (CloseableHttpClient httpClient = httpClientBuilder.build()) { + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString); + if (this.getConfig().isIfSaveApiData()) { + wxApiData.set(new WxPayApiData(url, requestStr, responseString, null)); + } + return responseString; + } + } finally { + httpPost.releaseConnection(); + } + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + if (this.getConfig().isIfSaveApiData()) { + wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage())); + } + throw new WxPayException(e.getMessage(), e); + } + } + + @Override + public String postV3(String url, String requestStr) throws WxPayException { + CloseableHttpClient httpClient = this.createApiV3HttpClient(); + HttpPost httpPost = this.createHttpPost(url, requestStr); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-Type", "application/json"); + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + //v3已经改为通过状态码判断200 204 成功 + int statusCode = response.getStatusLine().getStatusCode(); + String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) { + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString); + return responseString; + } else { + //有错误提示信息返回 + JsonObject jsonObject = GsonParser.parse(responseString); + throw new WxPayException(jsonObject.get("message").getAsString()); + } + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + throw new WxPayException(e.getMessage(), e); + } finally { + httpPost.releaseConnection(); + } + + + } + + @Override + public String postV3WithWechatpaySerial(String url, String requestStr) throws WxPayException { + CloseableHttpClient httpClient = this.createApiV3HttpClient(); + HttpPost httpPost = this.createHttpPost(url, requestStr); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-Type", "application/json"); + String serialNumber = getConfig().getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase(); + httpPost.addHeader("Wechatpay-Serial", serialNumber); + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + //v3已经改为通过状态码判断200 204 成功 + int statusCode = response.getStatusLine().getStatusCode(); + String responseString = "{}"; + HttpEntity entity = response.getEntity(); + if (entity != null) { + responseString = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } + + if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) { + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString); + return responseString; + } else { + //有错误提示信息返回 + JsonObject jsonObject = GsonParser.parse(responseString); + throw new WxPayException(jsonObject.get("message").getAsString()); + } + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + e.printStackTrace(); + throw new WxPayException(e.getMessage(), e); + } finally { + httpPost.releaseConnection(); + } + } + + @Override + public String postV3(String url, HttpPost httpPost) throws WxPayException { + + httpPost.setConfig(RequestConfig.custom() + .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout()) + .setConnectTimeout(this.getConfig().getHttpConnectionTimeout()) + .setSocketTimeout(this.getConfig().getHttpTimeout()) + .build()); + + CloseableHttpClient httpClient = this.createApiV3HttpClient(); + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + //v3已经改为通过状态码判断200 204 成功 + int statusCode = response.getStatusLine().getStatusCode(); + String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) { + this.log.info("\n【请求地址】:{}\n【响应数据】:{}", url, responseString); + return responseString; + } else { + //有错误提示信息返回 + JsonObject jsonObject = GsonParser.parse(responseString); + throw new WxPayException(jsonObject.get("message").getAsString()); + } + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage()); + throw new WxPayException(e.getMessage(), e); + } finally { + httpPost.releaseConnection(); + } + } + + @Override + public String getV3(URI url) throws WxPayException { + CloseableHttpClient httpClient = this.createApiV3HttpClient(); + HttpGet httpGet = new HttpGet(url); + httpGet.addHeader("Accept", "application/json"); + httpGet.addHeader("Content-Type", "application/json"); + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + //v3已经改为通过状态码判断200 204 成功 + int statusCode = response.getStatusLine().getStatusCode(); + String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) { + this.log.info("\n【请求地址】:{}\n【响应数据】:{}", url, responseString); + return responseString; + } else { + //有错误提示信息返回 + JsonObject jsonObject = GsonParser.parse(responseString); + throw new WxPayException(jsonObject.get("message").getAsString()); + } + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage()); + throw new WxPayException(e.getMessage(), e); + } finally { + httpGet.releaseConnection(); + } + } + + private CloseableHttpClient createApiV3HttpClient() throws WxPayException { + CloseableHttpClient apiV3HttpClient = this.getConfig().getApiV3HttpClient(); + if (null == apiV3HttpClient) { + return this.getConfig().initApiV3HttpClient(); + } + return apiV3HttpClient; + } + + private StringEntity createEntry(String requestStr) { + return new StringEntity(requestStr, ContentType.create("application/json", "utf-8")); + //return new StringEntity(new String(requestStr.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)); + } + + private HttpClientBuilder createHttpClientBuilder(boolean useKey) throws WxPayException { + HttpClientBuilder httpClientBuilder = HttpClients.custom(); + if (useKey) { + this.initSSLContext(httpClientBuilder); + } + + if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost()) && this.getConfig().getHttpProxyPort() > 0) { + if (StringUtils.isEmpty(this.getConfig().getHttpProxyUsername())) { + this.getConfig().setHttpProxyUsername("whatever"); + } + + // 使用代理服务器 需要用户认证的代理服务器 + CredentialsProvider provider = new BasicCredentialsProvider(); + provider.setCredentials(new AuthScope(this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort()), + new UsernamePasswordCredentials(this.getConfig().getHttpProxyUsername(), this.getConfig().getHttpProxyPassword())); + httpClientBuilder.setDefaultCredentialsProvider(provider); + httpClientBuilder.setProxy(new HttpHost(this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort())); + } + return httpClientBuilder; + } + + private HttpPost createHttpPost(String url, String requestStr) { + HttpPost httpPost = new HttpPost(url); + httpPost.setEntity(this.createEntry(requestStr)); + + httpPost.setConfig(RequestConfig.custom() + .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout()) + .setConnectTimeout(this.getConfig().getHttpConnectionTimeout()) + .setSocketTimeout(this.getConfig().getHttpTimeout()) + .build()); + + return httpPost; + } + + private void initSSLContext(HttpClientBuilder httpClientBuilder) throws WxPayException { + SSLContext sslContext = this.getConfig().getSslContext(); + if (null == sslContext) { + sslContext = this.getConfig().initSSLContext(); + } + + SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext, + new DefaultHostnameVerifier()); + httpClientBuilder.setSSLSocketFactory(connectionSocketFactory); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java index c26d058392..8e795966f4 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -1,416 +1,12 @@ package com.github.binarywang.wxpay.service.impl; -import com.github.binarywang.utils.qrcode.QrcodeUtils; -import com.github.binarywang.wxpay.bean.request.*; -import com.github.binarywang.wxpay.bean.result.*; -import com.github.binarywang.wxpay.config.WxPayConfig; -import com.github.binarywang.wxpay.service.WxPayService; -import com.github.binarywang.wxpay.util.SignUtils; -import com.google.common.collect.Maps; -import jodd.http.HttpRequest; -import jodd.http.HttpResponse; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.apache.commons.lang3.CharEncoding; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.Consts; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.ssl.DefaultHostnameVerifier; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.ssl.SSLContext; -import java.io.File; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; - /** - * Created by Binary Wang on 2016/7/28. + *
    + * 微信支付接口请求实现类,默认使用Apache HttpClient实现
    + * Created by Binary Wang on 2017-7-8.
    + * 
    * - * @author binarywang (https://github.com/binarywang) + * @author Binary Wang */ -public class WxPayServiceImpl implements WxPayService { - private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - private WxPayConfig config; - - @Override - public WxPayConfig getConfig() { - return this.config; - } - - @Override - public void setConfig(WxPayConfig config) { - this.config = config; - } - - private String getPayBaseUrl() { - if (this.getConfig().useSandboxForWxPay()) { - return PAY_BASE_URL + "/sandboxnew"; - } - - return PAY_BASE_URL; - } - - @Override - public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/secapi/pay/refund"; - String responseContent = this.postWithKey(url, request.toXML()); - WxPayRefundResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) - throws WxErrorException { - WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); - request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); - request.setTransactionId(StringUtils.trimToNull(transactionId)); - request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); - request.setRefundId(StringUtils.trimToNull(refundId)); - - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/refundquery"; - String responseContent = this.post(url, request.toXML()); - WxPayRefundQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundQueryResult.class); - result.composeRefundRecords(); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException { - try { - log.debug("微信支付回调参数详细:{}", xmlData); - WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); - log.debug("微信支付回调结果对象:{}", result); - result.checkResult(this); - return result; - } catch (WxErrorException e) { - log.error(e.getMessage(), e); - throw e; - } catch (Exception e) { - log.error(e.getMessage(), e); - throw new WxErrorException(WxError.newBuilder().setErrorMsg("发生异常" + e.getMessage()).build()); - } - } - - @Override - public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack"; - if (request.getAmtType() != null) { - //裂变红包 - url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; - } - - String responseContent = this.postWithKey(url, request.toXML()); - WxPaySendRedpackResult result = WxPayBaseResult.fromXML(responseContent, WxPaySendRedpackResult.class); - //毋须校验,因为没有返回签名信息 - // this.checkResult(result); - return result; - } - - @Override - public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException { - WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); - request.setMchBillNo(mchBillNo); - request.setBillType("MCHT"); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; - String responseContent = this.postWithKey(url, request.toXML()); - WxPayRedpackQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRedpackQueryResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { - WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); - request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); - request.setTransactionId(StringUtils.trimToNull(transactionId)); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/orderquery"; - String responseContent = this.post(url, request.toXML()); - if (StringUtils.isBlank(responseContent)) { - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无响应结果").build()); - } - - WxPayOrderQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderQueryResult.class); - result.composeCoupons(); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException { - if (StringUtils.isBlank(outTradeNo)) { - throw new IllegalArgumentException("out_trade_no不能为空"); - } - - WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); - request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/closeorder"; - String responseContent = this.post(url, request.toXML()); - WxPayOrderCloseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderCloseResult.class); - result.checkResult(this); - - return result; - } - - @Override - public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/unifiedorder"; - String responseContent = this.post(url, request.toXML()); - WxPayUnifiedOrderResult result = WxPayBaseResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); - result.checkResult(this); - return result; - } - - @Override - public Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException { - WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); - String prepayId = unifiedOrderResult.getPrepayId(); - if (StringUtils.isBlank(prepayId)) { - throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。", - unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); - } - - Map payInfo = new HashMap<>(); - payInfo.put("appId", getConfig().getAppId()); - // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。 - // 但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 - payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); - payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis())); - payInfo.put("package", "prepay_id=" + prepayId); - payInfo.put("signType", "MD5"); - if ("NATIVE".equals(request.getTradeType())) { - payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); - } - payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); - return payInfo; - } - - @Override - public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; - - String responseContent = this.postWithKey(url, request.toXML()); - WxEntPayResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException { - WxEntPayQueryRequest request = new WxEntPayQueryRequest(); - request.setPartnerTradeNo(partnerTradeNo); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; - String responseContent = this.postWithKey(url, request.toXML()); - WxEntPayQueryResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayQueryResult.class); - result.checkResult(this); - return result; - } - - @Override - public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { - String content = this.createScanPayQrcodeMode1(productId); - return this.createQrcode(content, logoFile, sideLength); - } - - @Override - public String createScanPayQrcodeMode1(String productId) { - //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX - StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); - Map params = Maps.newHashMap(); - params.put("appid", this.getConfig().getAppId()); - params.put("mch_id", this.getConfig().getMchId()); - params.put("product_id", productId); - params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000));//这里需要秒,10位数字 - params.put("nonce_str", String.valueOf(System.currentTimeMillis())); - - String sign = SignUtils.createSign(params, this.getConfig().getMchKey()); - params.put("sign", sign); - - - for (String key : params.keySet()) { - codeUrl.append(key + "=" + params.get(key) + "&"); - } - - String content = codeUrl.toString().substring(0, codeUrl.length() - 1); - log.debug("扫码支付模式一生成二维码的URL:{}", content); - return content; - } - - @Override - public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { - return this.createQrcode(codeUrl, logoFile, sideLength); - } - - private byte[] createQrcode(String content, File logoFile, Integer sideLength) { - if (sideLength == null || sideLength < 1) { - return QrcodeUtils.createQrcode(content, logoFile); - } - - return QrcodeUtils.createQrcode(content, sideLength, logoFile); - } - - public void report(WxPayReportRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/payitil/report"; - String responseContent = this.post(url, request.toXML()); - WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); - result.checkResult(this); - } - - @Override - public File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException { - WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); - request.setBillType(billType); - request.setBillDate(billDate); - request.setTarType(tarType); - request.setDeviceInfo(deviceInfo); - - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/downloadbill"; - //TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待 - String responseContent = this.post(url, request.toXML()); - - WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); - result.checkResult(this); - //TODO 待实现,暂时无测试帐号,无法调试 - return null; - } - - @Override - public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/micropay"; - String responseContent = this.post(url, request.toXML()); - WxPayMicropayResult result = WxPayBaseResult.fromXML(responseContent, WxPayMicropayResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/secapi/pay/reverse"; - String responseContent = this.postWithKey(url, request.toXML()); - WxPayOrderReverseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderReverseResult.class); - result.checkResult(this); - return result; - } - - @Override - public String shorturl(WxPayShorturlRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/tools/shorturl"; - String responseContent = this.post(url, request.toXML()); - WxPayShorturlResult result = WxPayBaseResult.fromXML(responseContent, WxPayShorturlResult.class); - result.checkResult(this); - return result.getShortUrl(); - } - - @Override - public String shorturl(String longUrl) throws WxErrorException { - return this.shorturl(new WxPayShorturlRequest(longUrl)); - } - - @Override - public String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/tools/authcodetoopenid"; - String responseContent = this.post(url, request.toXML()); - WxPayAuthcode2OpenidResult result = WxPayBaseResult.fromXML(responseContent, WxPayAuthcode2OpenidResult.class); - result.checkResult(this); - return result.getOpenid(); - } - - @Override - public String authcode2Openid(String authCode) throws WxErrorException { - return this.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); - } - - private String post(String url, String xmlParam) { - String requestString = xmlParam; - try { - requestString = new String(xmlParam.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1); - } catch (UnsupportedEncodingException e) { - //实际上不会发生该异常 - e.printStackTrace(); - } - - HttpRequest request = HttpRequest.post(url).body(requestString); - HttpResponse response = request.send(); - String responseString = null; - try { - responseString = new String(response.bodyText().getBytes(CharEncoding.ISO_8859_1), CharEncoding.UTF_8); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, xmlParam, responseString); - return responseString; - } - - /** - * 由于暂时未找到使用jodd-http实现证书配置的办法,故而暂时使用httpclient - */ - private String postWithKey(String url, String requestStr) throws WxErrorException { - try { - SSLContext sslContext = this.getConfig().getSslContext(); - if (null == sslContext) { - sslContext = this.getConfig().initSSLContext(); - } - - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, - new String[]{"TLSv1"}, null, new DefaultHostnameVerifier()); - - HttpPost httpPost = new HttpPost(url); - - try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) { - httpPost.setEntity(new StringEntity(new String(requestStr.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1))); - try (CloseableHttpResponse response = httpclient.execute(httpPost)) { - String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); - return result; - } - } finally { - httpPost.releaseConnection(); - } - } catch (Exception e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); - } - } - +public class WxPayServiceImpl extends WxPayServiceApacheHttpImpl { } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java new file mode 100644 index 0000000000..b7ef11695e --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java @@ -0,0 +1,138 @@ +package com.github.binarywang.wxpay.service.impl; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import javax.net.ssl.SSLContext; + +import org.apache.commons.lang3.StringUtils; + +import com.github.binarywang.wxpay.bean.WxPayApiData; +import com.github.binarywang.wxpay.exception.WxPayException; +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.http.ProxyInfo.ProxyType; +import jodd.http.net.SSLSocketHttpConnectionProvider; +import jodd.http.net.SocketHttpConnectionProvider; +import jodd.util.Base64; +import org.apache.http.client.methods.HttpPost; + +/** + * 微信支付请求实现类,jodd-http实现. + * Created by Binary Wang on 2016/7/28. + * + * @author Binary Wang + */ +public class WxPayServiceJoddHttpImpl extends BaseWxPayServiceImpl { + @Override + public byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException { + try { + HttpRequest request = this.buildHttpRequest(url, requestStr, useKey); + byte[] responseBytes = request.send().bodyBytes(); + final String responseString = Base64.encodeToString(responseBytes); + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseString); + if (this.getConfig().isIfSaveApiData()) { + wxApiData.set(new WxPayApiData(url, requestStr, responseString, null)); + } + return responseBytes; + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage())); + throw new WxPayException(e.getMessage(), e); + } + } + + @Override + public String post(String url, String requestStr, boolean useKey) throws WxPayException { + try { + HttpRequest request = this.buildHttpRequest(url, requestStr, useKey); + String responseString = this.getResponseString(request.send()); + + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString); + if (this.getConfig().isIfSaveApiData()) { + wxApiData.set(new WxPayApiData(url, requestStr, responseString, null)); + } + return responseString; + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage())); + throw new WxPayException(e.getMessage(), e); + } + } + + @Override + public String postV3(String url, String requestStr) throws WxPayException { + return null; + } + + @Override + public String postV3WithWechatpaySerial(String url, String requestStr) throws WxPayException { + return null; + } + + @Override + public String postV3(String url, HttpPost httpPost) throws WxPayException { + return null; + } + + @Override + public String getV3(URI url) throws WxPayException { + return null; + } + + private HttpRequest buildHttpRequest(String url, String requestStr, boolean useKey) throws WxPayException { + HttpRequest request = HttpRequest + .post(url) + .timeout(this.getConfig().getHttpTimeout()) + .connectionTimeout(this.getConfig().getHttpConnectionTimeout()) + .bodyText(requestStr); + + if (useKey) { + SSLContext sslContext = this.getConfig().getSslContext(); + if (null == sslContext) { + sslContext = this.getConfig().initSSLContext(); + } + final SSLSocketHttpConnectionProvider provider = new SSLSocketHttpConnectionProvider(sslContext); + request.withConnectionProvider(provider); + } + + if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost()) && this.getConfig().getHttpProxyPort() > 0) { + if (StringUtils.isEmpty(this.getConfig().getHttpProxyUsername())) { + this.getConfig().setHttpProxyUsername("whatever"); + } + + ProxyInfo httpProxy = new ProxyInfo(ProxyType.HTTP, this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort(), + this.getConfig().getHttpProxyUsername(), this.getConfig().getHttpProxyPassword()); + HttpConnectionProvider provider = request.connectionProvider(); + if (null == provider) { + provider = new SocketHttpConnectionProvider(); + } + provider.useProxy(httpProxy); + request.withConnectionProvider(provider); + } + return request; + } + + private String getResponseString(HttpResponse response) throws WxPayException { + try { + this.log.debug("【微信服务器响应头信息】:\n{}", response.toString(false)); + } catch (NullPointerException e) { + this.log.warn("HttpResponse.toString() 居然抛出空指针异常了", e); + } + + String responseString = response.bodyText(); + + if (StringUtils.isBlank(responseString)) { + throw new WxPayException("响应信息为空"); + } + + if (StringUtils.isBlank(response.charset())) { + responseString = new String(responseString.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + } + + return responseString; + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java index 2a6030f399..0ce39a7312 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java @@ -1,84 +1,222 @@ package com.github.binarywang.wxpay.util; -import me.chanjar.weixin.common.util.BeanUtils; +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.*; /** *
    - * 签名相关工具类
    + * 签名相关工具类.
      * Created by Binary Wang on 2017-3-23.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author binarywang(Binary Wang) */ +@Slf4j public class SignUtils { + /** + * 签名的时候不携带的参数 + */ + private static final List NO_SIGN_PARAMS = Lists.newArrayList("sign", "key", "xmlString", "xmlDoc", "couponList"); /** - * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) + * 请参考并使用 {@link #createSign(Object, String, String, String[])}. * - * @param xmlBean Bean需要标记有XML注解 - * @param signKey 签名Key - * @return 签名字符串 + * @param xmlBean the xml bean + * @param signKey the sign key + * @return the string */ + @Deprecated public static String createSign(Object xmlBean, String signKey) { - return createSign(BeanUtils.xmlBean2Map(xmlBean), signKey); + return createSign(xmlBean2Map(xmlBean), signKey); } /** - * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) + * 请参考并使用 {@link #createSign(Map, String, String, String[])} . * - * @param params 参数信息 - * @param signKey 签名Key - * @return 签名字符串 + * @param params the params + * @param signKey the sign key + * @return the string */ + @Deprecated public static String createSign(Map params, String signKey) { -// if (this.getConfig().useSandboxForWxPay()) { -// //使用仿真测试环境 -// //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用仿真测试环境 -// signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; -// //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; -// } + return createSign(params, SignType.MD5, signKey, new String[0]); + } + + /** + * 微信支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3). + * + * @param xmlBean Bean里的属性如果存在XML注解,则使用其作为key,否则使用变量名 + * @param signType 签名类型,如果为空,则默认为MD5 + * @param signKey 签名Key + * @param ignoredParams 签名时需要忽略的特殊参数 + * @return 签名字符串 string + */ + public static String createSign(Object xmlBean, String signType, String signKey, String[] ignoredParams) { + Map map = null; + + if (XmlConfig.fastMode) { + if (xmlBean instanceof BaseWxPayRequest) { + map = ((BaseWxPayRequest) xmlBean).getSignParams(); + } + } + if (map == null) { + map = xmlBean2Map(xmlBean); + } - SortedMap sortedMap = new TreeMap<>(params); + return createSign(map, signType, signKey, ignoredParams); + } + /** + * 微信支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3). + * + * @param params 参数信息 + * @param signType 签名类型,如果为空,则默认为MD5 + * @param signKey 签名Key + * @param ignoredParams 签名时需要忽略的特殊参数 + * @return 签名字符串 string + */ + public static String createSign(Map params, String signType, String signKey, String[] ignoredParams) { StringBuilder toSign = new StringBuilder(); - for (String key : sortedMap.keySet()) { + for (String key : new TreeMap<>(params).keySet()) { String value = params.get(key); - if (StringUtils.isNotEmpty(value) && !"sign".equals(key) && !"key".equals(key)) { - toSign.append(key + "=" + value + "&"); + boolean shouldSign = false; + if (StringUtils.isNotEmpty(value) && !ArrayUtils.contains(ignoredParams, key) + && !NO_SIGN_PARAMS.contains(key)) { + shouldSign = true; + } + + if (shouldSign) { + toSign.append(key).append("=").append(value).append("&"); + } + } + + toSign.append("key=").append(signKey); + if (SignType.HMAC_SHA256.equals(signType)) { + return me.chanjar.weixin.common.util.SignUtils.createHmacSha256Sign(toSign.toString(), signKey); + } else { + return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); + } + } + + /** + * 企业微信签名 + * + * @param signType md5 目前接口要求使用的加密类型 + */ + public static String createEntSign(String actName, String mchBillNo, String mchId, String nonceStr, + String reOpenid, Integer totalAmount, String wxAppId, String signKey, + String signType) { + Map sortedMap = new HashMap<>(8); + sortedMap.put("act_name", actName); + sortedMap.put("mch_billno", mchBillNo); + sortedMap.put("mch_id", mchId); + sortedMap.put("nonce_str", nonceStr); + sortedMap.put("re_openid", reOpenid); + sortedMap.put("total_amount", totalAmount + ""); + sortedMap.put("wxappid", wxAppId); + + Iterator> iterator = new TreeMap<>(sortedMap).entrySet().iterator(); + StringBuilder toSign = new StringBuilder(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String value = entry.getValue(); + boolean shouldSign = false; + if (StringUtils.isNotEmpty(value)) { + shouldSign = true; } + + if (shouldSign) { + toSign.append(entry.getKey()).append("=").append(value).append("&"); + } + } + //企业微信这里字段名不一样 + toSign.append("secret=").append(signKey); + if (SignType.HMAC_SHA256.equals(signType)) { + return me.chanjar.weixin.common.util.SignUtils.createHmacSha256Sign(toSign.toString(), signKey); + } else { + return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); } - toSign.append("key=" + signKey); - return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); } /** - * 校验签名是否正确 + * 校验签名是否正确. * - * @param xmlBean Bean需要标记有XML注解 - * @param signKey 校验的签名Key + * @param xmlBean Bean需要标记有XML注解 + * @param signType 签名类型,如果为空,则默认为MD5 + * @param signKey 校验的签名Key * @return true - 签名校验成功,false - 签名校验失败 - * @see #checkSign(Map, String) */ - public static boolean checkSign(Object xmlBean, String signKey) { - return checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey); + public static boolean checkSign(Object xmlBean, String signType, String signKey) { + return checkSign(xmlBean2Map(xmlBean), signType, signKey); } /** - * 校验签名是否正确 + * 校验签名是否正确. * - * @param params 需要校验的参数Map - * @param signKey 校验的签名Key + * @param params 需要校验的参数Map + * @param signType 签名类型,如果为空,则默认为MD5 + * @param signKey 校验的签名Key * @return true - 签名校验成功,false - 签名校验失败 - * @see #checkSign(Map, String) */ - public static boolean checkSign(Map params, String signKey) { - String sign = createSign(params, signKey); + public static boolean checkSign(Map params, String signType, String signKey) { + String sign = createSign(params, signType, signKey, new String[0]); return sign.equals(params.get("sign")); } + + /** + * 将bean按照@XStreamAlias标识的字符串内容生成以之为key的map对象. + * + * @param bean 包含@XStreamAlias的xml bean对象 + * @return map对象 map + */ + public static Map xmlBean2Map(Object bean) { + Map result = Maps.newHashMap(); + List fields = new ArrayList<>(Arrays.asList(bean.getClass().getDeclaredFields())); + fields.addAll(Arrays.asList(bean.getClass().getSuperclass().getDeclaredFields())); + if (bean.getClass().getSuperclass().getSuperclass() == BaseWxPayRequest.class) { + fields.addAll(Arrays.asList(BaseWxPayRequest.class.getDeclaredFields())); + } + + if (bean.getClass().getSuperclass().getSuperclass() == BaseWxPayResult.class) { + fields.addAll(Arrays.asList(BaseWxPayResult.class.getDeclaredFields())); + } + + for (Field field : fields) { + try { + boolean isAccessible = field.isAccessible(); + field.setAccessible(true); + if (field.get(bean) == null) { + field.setAccessible(isAccessible); + continue; + } + + if (field.isAnnotationPresent(XStreamAlias.class)) { + result.put(field.getAnnotation(XStreamAlias.class).value(), field.get(bean).toString()); + } else if (!Modifier.isStatic(field.getModifiers())) { + //忽略掉静态成员变量 + result.put(field.getName(), field.get(bean).toString()); + } + + field.setAccessible(isAccessible); + } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) { + log.error(e.getMessage(), e); + } + + } + + return result; + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/XmlConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/XmlConfig.java new file mode 100644 index 0000000000..3cd776841d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/XmlConfig.java @@ -0,0 +1,23 @@ +package com.github.binarywang.wxpay.util; + +public class XmlConfig { + + /** + * 是否使用快速模式 + * + * 如果设置为true,将会影响下面的方法,不再使用反射的方法来进行xml转换。 + * 1: BaseWxPayRequest的toXML方法 + * 2: BaseWxPayResult的fromXML方法 + * @see com.github.binarywang.wxpay.bean.request.BaseWxPayRequest#toXML + * @see com.github.binarywang.wxpay.bean.result.BaseWxPayResult#fromXML + * + * 启用快速模式后,将能: + * 1:性能提升约 10 ~ 15倍 + * 2:可以通过 graalvm 生成native image,大大减少系统开销(CPU,RAM),加快应用启动速度(亚秒级),加快系统部署速度(脱离JRE). + * + * 参考测试案例: com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResultTest#benchmark + * 参考网址: https://www.graalvm.org/ + */ + public static boolean fastMode = false; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/Credentials.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/Credentials.java new file mode 100644 index 0000000000..495e883f5a --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/Credentials.java @@ -0,0 +1,12 @@ +package com.github.binarywang.wxpay.v3; + +import java.io.IOException; + +import org.apache.http.client.methods.HttpRequestWrapper; + +public interface Credentials { + + String getSchema(); + + String getToken(HttpRequestWrapper request) throws IOException; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/SignatureExec.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/SignatureExec.java new file mode 100644 index 0000000000..2345e3c68b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/SignatureExec.java @@ -0,0 +1,91 @@ +package com.github.binarywang.wxpay.v3; + +import java.io.IOException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpException; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpExecutionAware; +import org.apache.http.client.methods.HttpRequestWrapper; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.execchain.ClientExecChain; +import org.apache.http.util.EntityUtils; + +public class SignatureExec implements ClientExecChain { + final ClientExecChain mainExec; + final Credentials credentials; + final Validator validator; + + SignatureExec(Credentials credentials, Validator validator, ClientExecChain mainExec) { + this.credentials = credentials; + this.validator = validator; + this.mainExec = mainExec; + } + + protected HttpEntity newRepeatableEntity(HttpEntity entity) throws IOException { + byte[] content = EntityUtils.toByteArray(entity); + ByteArrayEntity newEntity = new ByteArrayEntity(content); + newEntity.setContentEncoding(entity.getContentEncoding()); + newEntity.setContentType(entity.getContentType()); + + return newEntity; + } + + protected void convertToRepeatableResponseEntity(CloseableHttpResponse response) throws IOException { + HttpEntity entity = response.getEntity(); + if (entity != null && !entity.isRepeatable()) { + response.setEntity(newRepeatableEntity(entity)); + } + } + + protected void convertToRepeatableRequestEntity(HttpRequestWrapper request) throws IOException { + if (request instanceof HttpEntityEnclosingRequest) { + HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); + if (entity != null) { + ((HttpEntityEnclosingRequest) request).setEntity(new BufferedHttpEntity(entity)); + } + } + } + + @Override + public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request, + HttpClientContext context, HttpExecutionAware execAware) throws IOException, HttpException { + if (request.getURI().getHost().endsWith(".mch.weixin.qq.com")) { + return executeWithSignature(route, request, context, execAware); + } else { + return mainExec.execute(route, request, context, execAware); + } + } + + private CloseableHttpResponse executeWithSignature(HttpRoute route, HttpRequestWrapper request, + HttpClientContext context, HttpExecutionAware execAware) throws IOException, HttpException { + // 上传类不需要消耗两次故不做转换 + if (!(request.getOriginal() instanceof WechatPayUploadHttpPost)) { + convertToRepeatableRequestEntity(request); + } + // 添加认证信息 + request.addHeader("Authorization", + credentials.getSchema() + " " + credentials.getToken(request)); + + // 执行 + CloseableHttpResponse response = mainExec.execute(route, request, context, execAware); + + // 对成功应答验签 + StatusLine statusLine = response.getStatusLine(); + if (statusLine.getStatusCode() >= 200 && statusLine.getStatusCode() < 300) { + convertToRepeatableResponseEntity(response); + if (!validator.validate(response)) { + throw new HttpException("应答的微信支付签名验证失败"); + } + } + return response; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/SpecEncrypt.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/SpecEncrypt.java new file mode 100644 index 0000000000..4f1eb9e582 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/SpecEncrypt.java @@ -0,0 +1,16 @@ +package com.github.binarywang.wxpay.v3; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 敏感信息字段 + * @author zhouyognshen + **/ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SpecEncrypt { + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/Validator.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/Validator.java new file mode 100644 index 0000000000..cdeb8ac279 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/Validator.java @@ -0,0 +1,8 @@ +package com.github.binarywang.wxpay.v3; + +import java.io.IOException; +import org.apache.http.client.methods.CloseableHttpResponse; + +public interface Validator { + boolean validate(CloseableHttpResponse response) throws IOException; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WechatPayUploadHttpPost.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WechatPayUploadHttpPost.java new file mode 100644 index 0000000000..df0ee4e2fb --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WechatPayUploadHttpPost.java @@ -0,0 +1,76 @@ +package com.github.binarywang.wxpay.v3; + +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; + +import java.io.InputStream; +import java.net.URI; +import java.net.URLConnection; + +public class WechatPayUploadHttpPost extends HttpPost { + + private String meta; + + private WechatPayUploadHttpPost(URI uri, String meta) { + super(uri); + + this.meta = meta; + } + + public String getMeta() { + return meta; + } + + public static class Builder { + + private String fileName; + private String fileSha256; + private InputStream fileInputStream; + private ContentType fileContentType; + private URI uri; + + public Builder(URI uri) { + this.uri = uri; + } + + public Builder withImage(String fileName, String fileSha256, InputStream inputStream) { + this.fileName = fileName; + this.fileSha256 = fileSha256; + this.fileInputStream = inputStream; + + String mimeType = URLConnection.guessContentTypeFromName(fileName); + if (mimeType == null) { + // guess this is a video uploading + this.fileContentType = ContentType.APPLICATION_OCTET_STREAM; + } else { + this.fileContentType = ContentType.create(mimeType); + } + return this; + } + + public WechatPayUploadHttpPost build() { + if (fileName == null || fileSha256 == null || fileInputStream == null) { + throw new IllegalArgumentException("缺少待上传图片文件信息"); + } + + if (uri == null) { + throw new IllegalArgumentException("缺少上传图片接口URL"); + } + + String meta = String.format("{\"filename\":\"%s\",\"sha256\":\"%s\"}", fileName, fileSha256); + WechatPayUploadHttpPost request = new WechatPayUploadHttpPost(uri, meta); + + MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); + entityBuilder.setMode(HttpMultipartMode.RFC6532) + .addBinaryBody("file", fileInputStream, fileContentType, fileName) + .addTextBody("meta", meta, ContentType.APPLICATION_JSON); + + request.setEntity(entityBuilder.build()); + request.addHeader("Accept", ContentType.APPLICATION_JSON.toString()); + + return request; + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WxPayV3HttpClientBuilder.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WxPayV3HttpClientBuilder.java new file mode 100644 index 0000000000..986a8f4cb1 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WxPayV3HttpClientBuilder.java @@ -0,0 +1,75 @@ +package com.github.binarywang.wxpay.v3; + + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.List; + +import com.github.binarywang.wxpay.v3.auth.CertificatesVerifier; +import com.github.binarywang.wxpay.v3.auth.PrivateKeySigner; +import com.github.binarywang.wxpay.v3.auth.WxPayCredentials; +import com.github.binarywang.wxpay.v3.auth.WxPayValidator; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.execchain.ClientExecChain; + +public class WxPayV3HttpClientBuilder extends HttpClientBuilder { + private Credentials credentials; + private Validator validator; + + static final String OS = System.getProperty("os.name") + "/" + System.getProperty("os.version"); + static final String VERSION = System.getProperty("java.version"); + + private WxPayV3HttpClientBuilder() { + super(); + + String userAgent = String.format( + "WechatPay-Apache-HttpClient/%s (%s) Java/%s", + getClass().getPackage().getImplementationVersion(), + OS, + VERSION == null ? "Unknown" : VERSION); + setUserAgent(userAgent); + } + + public static WxPayV3HttpClientBuilder create() { + return new WxPayV3HttpClientBuilder(); + } + + public WxPayV3HttpClientBuilder withMerchant(String merchantId, String serialNo, PrivateKey privateKey) { + this.credentials = + new WxPayCredentials(merchantId, new PrivateKeySigner(serialNo, privateKey)); + return this; + } + + public WxPayV3HttpClientBuilder withCredentials(Credentials credentials) { + this.credentials = credentials; + return this; + } + + public WxPayV3HttpClientBuilder withWechatpay(List certificates) { + this.validator = new WxPayValidator(new CertificatesVerifier(certificates)); + return this; + } + + public WxPayV3HttpClientBuilder withValidator(Validator validator) { + this.validator = validator; + return this; + } + + @Override + public CloseableHttpClient build() { + if (credentials == null) { + throw new IllegalArgumentException("缺少身份认证信息"); + } + if (validator == null) { + throw new IllegalArgumentException("缺少签名验证信息"); + } + + return super.build(); + } + + @Override + protected ClientExecChain decorateProtocolExec(final ClientExecChain requestExecutor) { + return new SignatureExec(this.credentials, this.validator, requestExecutor); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java new file mode 100644 index 0000000000..a490108146 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java @@ -0,0 +1,196 @@ +package com.github.binarywang.wxpay.v3.auth; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.binarywang.wxpay.v3.Credentials; +import com.github.binarywang.wxpay.v3.Validator; +import com.github.binarywang.wxpay.v3.WxPayV3HttpClientBuilder; +import com.github.binarywang.wxpay.v3.util.AesUtils; +import com.github.binarywang.wxpay.v3.util.PemUtils; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.joda.time.Instant; +import org.joda.time.Minutes; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 在原有CertificatesVerifier基础上,增加自动更新证书功能 + * + * @author doger.wang + */ +@Slf4j +public class AutoUpdateCertificatesVerifier implements Verifier { + /** + * 证书下载地址 + */ + private static final String CERT_DOWNLOAD_PATH = "https://api.mch.weixin.qq.com/v3/certificates"; + + /** + * 上次更新时间 + */ + private volatile Instant instant; + + /** + * 证书更新间隔时间,单位为分钟 + */ + private final int minutesInterval; + + private CertificatesVerifier verifier; + + private final Credentials credentials; + + private final byte[] apiV3Key; + + private final ReentrantLock lock = new ReentrantLock(); + + /** + * 时间间隔枚举,支持一小时、六小时以及十二小时 + */ + @Getter + @RequiredArgsConstructor + public enum TimeInterval { + /** + * 一小时 + */ + OneHour(60), + /** + * 六小时 + */ + SixHours(60 * 6), + /** + * 十二小时 + */ + TwelveHours(60 * 12); + + private final int minutes; + } + + public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key) { + this(credentials, apiV3Key, TimeInterval.OneHour.getMinutes()); + } + + public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key, int minutesInterval) { + this.credentials = credentials; + this.apiV3Key = apiV3Key; + this.minutesInterval = minutesInterval; + //构造时更新证书 + try { + autoUpdateCert(); + instant = Instant.now(); + } catch (IOException | GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean verify(String serialNumber, byte[] message, String signature) { + checkAndAutoUpdateCert(); + return verifier.verify(serialNumber, message, signature); + } + + /** + * 检查证书是否在有效期内,如果不在有效期内则进行更新 + */ + private void checkAndAutoUpdateCert() { + if (instant == null || Minutes.minutesBetween(instant, Instant.now()).getMinutes() >= minutesInterval) { + if (lock.tryLock()) { + try { + autoUpdateCert(); + //更新时间 + instant = Instant.now(); + } catch (GeneralSecurityException | IOException e) { + log.warn("Auto update cert failed, exception = " + e); + } finally { + lock.unlock(); + } + } + } + } + + private void autoUpdateCert() throws IOException, GeneralSecurityException { + CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create() + .withCredentials(credentials) + .withValidator(verifier == null ? new Validator() { + @Override + public boolean validate(CloseableHttpResponse response) throws IOException { + return true; + } + } : new WxPayValidator(verifier)) + .build(); + + HttpGet httpGet = new HttpGet(CERT_DOWNLOAD_PATH); + httpGet.addHeader("Accept", "application/json"); + + CloseableHttpResponse response = httpClient.execute(httpGet); + int statusCode = response.getStatusLine().getStatusCode(); + String body = EntityUtils.toString(response.getEntity()); + if (statusCode == 200) { + List newCertList = deserializeToCerts(apiV3Key, body); + if (newCertList.isEmpty()) { + log.warn("Cert list is empty"); + return; + } + this.verifier = new CertificatesVerifier(newCertList); + } else { + log.warn("Auto update cert failed, statusCode = " + statusCode + ",body = " + body); + } + } + + /** + * 反序列化证书并解密 + */ + private List deserializeToCerts(byte[] apiV3Key, String body) throws GeneralSecurityException, IOException { + AesUtils aesUtils = new AesUtils(apiV3Key); + ObjectMapper mapper = new ObjectMapper(); + JsonNode dataNode = mapper.readTree(body).get("data"); + if (dataNode == null) { + return Collections.emptyList(); + } + + List newCertList = new ArrayList<>(); + for (int i = 0, count = dataNode.size(); i < count; i++) { + JsonNode encryptCertificateNode = dataNode.get(i).get("encrypt_certificate"); + //解密 + String cert = aesUtils.decryptToString( + encryptCertificateNode.get("associated_data").toString().replaceAll("\"", "") + .getBytes(StandardCharsets.UTF_8), + encryptCertificateNode.get("nonce").toString().replaceAll("\"", "") + .getBytes(StandardCharsets.UTF_8), + encryptCertificateNode.get("ciphertext").toString().replaceAll("\"", "")); + + X509Certificate x509Cert = PemUtils + .loadCertificate(new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8))); + try { + x509Cert.checkValidity(); + } catch (CertificateExpiredException | CertificateNotYetValidException e) { + continue; + } + newCertList.add(x509Cert); + } + + return newCertList; + } + + @Override + public X509Certificate getValidCertificate() { + checkAndAutoUpdateCert(); + return verifier.getValidCertificate(); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/CertificatesVerifier.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/CertificatesVerifier.java new file mode 100644 index 0000000000..7239d9e64d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/CertificatesVerifier.java @@ -0,0 +1,63 @@ +package com.github.binarywang.wxpay.v3.auth; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.NoSuchElementException; + +public class CertificatesVerifier implements Verifier { + private final HashMap certificates = new HashMap<>(); + + public CertificatesVerifier(List list) { + + for (X509Certificate item : list) { + certificates.put(item.getSerialNumber(), item); + } + } + + private boolean verify(X509Certificate certificate, byte[] message, String signature) { + try { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initVerify(certificate); + sign.update(message); + return sign.verify(Base64.getDecoder().decode(signature)); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持SHA256withRSA", e); + } catch (SignatureException e) { + throw new RuntimeException("签名验证过程发生了错误", e); + } catch (InvalidKeyException e) { + throw new RuntimeException("无效的证书", e); + } + } + + @Override + public boolean verify(String serialNumber, byte[] message, String signature) { + BigInteger val = new BigInteger(serialNumber, 16); + return certificates.containsKey(val) && verify(certificates.get(val), message, signature); + } + + + @Override + public X509Certificate getValidCertificate() { + for (X509Certificate x509Cert : certificates.values()) { + try { + x509Cert.checkValidity(); + + return x509Cert; + } catch (CertificateExpiredException | CertificateNotYetValidException e) { + continue; + } + } + + throw new NoSuchElementException("没有有效的微信支付平台证书"); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/PrivateKeySigner.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/PrivateKeySigner.java new file mode 100644 index 0000000000..37ec51cf58 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/PrivateKeySigner.java @@ -0,0 +1,37 @@ +package com.github.binarywang.wxpay.v3.auth; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; +import java.util.Base64; + +public class PrivateKeySigner implements Signer { + private String certificateSerialNumber; + + private PrivateKey privateKey; + + public PrivateKeySigner(String serialNumber, PrivateKey privateKey) { + this.certificateSerialNumber = serialNumber; + this.privateKey = privateKey; + } + + @Override + public SignatureResult sign(byte[] message) { + try { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(privateKey); + sign.update(message); + + return new SignatureResult( + Base64.getEncoder().encodeToString(sign.sign()), certificateSerialNumber); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持SHA256withRSA", e); + } catch (SignatureException e) { + throw new RuntimeException("签名计算失败", e); + } catch (InvalidKeyException e) { + throw new RuntimeException("无效的私钥", e); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Signer.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Signer.java new file mode 100644 index 0000000000..7255a1b433 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Signer.java @@ -0,0 +1,15 @@ +package com.github.binarywang.wxpay.v3.auth; + +public interface Signer { + SignatureResult sign(byte[] message); + + class SignatureResult { + String sign; + String certificateSerialNumber; + + public SignatureResult(String sign, String serialNumber) { + this.sign = sign; + this.certificateSerialNumber = serialNumber; + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Verifier.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Verifier.java new file mode 100644 index 0000000000..49f92e2f5b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Verifier.java @@ -0,0 +1,10 @@ +package com.github.binarywang.wxpay.v3.auth; + +import java.security.cert.X509Certificate; + +public interface Verifier { + boolean verify(String serialNumber, byte[] message, String signature); + + + X509Certificate getValidCertificate(); +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/WxPayCredentials.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/WxPayCredentials.java new file mode 100644 index 0000000000..80eea8f686 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/WxPayCredentials.java @@ -0,0 +1,93 @@ +package com.github.binarywang.wxpay.v3.auth; + + +import com.github.binarywang.wxpay.v3.Credentials; +import com.github.binarywang.wxpay.v3.WechatPayUploadHttpPost; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.client.methods.HttpRequestWrapper; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; + +@Slf4j +public class WxPayCredentials implements Credentials { + private static final String SYMBOLS = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final SecureRandom RANDOM = new SecureRandom(); + protected String merchantId; + protected Signer signer; + + public WxPayCredentials(String merchantId, Signer signer) { + this.merchantId = merchantId; + this.signer = signer; + } + + public String getMerchantId() { + return merchantId; + } + + protected long generateTimestamp() { + return System.currentTimeMillis() / 1000; + } + + protected String generateNonceStr() { + char[] nonceChars = new char[32]; + for (int index = 0; index < nonceChars.length; ++index) { + nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); + } + return new String(nonceChars); + } + + @Override + public final String getSchema() { + return "WECHATPAY2-SHA256-RSA2048"; + } + + @Override + public final String getToken(HttpRequestWrapper request) throws IOException { + String nonceStr = generateNonceStr(); + long timestamp = generateTimestamp(); + + String message = buildMessage(nonceStr, timestamp, request); + log.debug("authorization message=[{}]", message); + + Signer.SignatureResult signature = signer.sign(message.getBytes(StandardCharsets.UTF_8)); + + String token = "mchid=\"" + getMerchantId() + "\"," + + "nonce_str=\"" + nonceStr + "\"," + + "timestamp=\"" + timestamp + "\"," + + "serial_no=\"" + signature.certificateSerialNumber + "\"," + + "signature=\"" + signature.sign + "\""; + log.debug("authorization token=[{}]", token); + + return token; + } + + protected final String buildMessage(String nonce, long timestamp, HttpRequestWrapper request) + throws IOException { + URI uri = request.getURI(); + String canonicalUrl = uri.getRawPath(); + if (uri.getQuery() != null) { + canonicalUrl += "?" + uri.getRawQuery(); + } + + String body = ""; + // PATCH,POST,PUT + if (request.getOriginal() instanceof WechatPayUploadHttpPost) { + body = ((WechatPayUploadHttpPost) request.getOriginal()).getMeta(); + } else if (request instanceof HttpEntityEnclosingRequest) { + body = EntityUtils.toString(((HttpEntityEnclosingRequest) request).getEntity()); + } + + return request.getRequestLine().getMethod() + "\n" + + canonicalUrl + "\n" + + timestamp + "\n" + + nonce + "\n" + + body + "\n"; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/WxPayValidator.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/WxPayValidator.java new file mode 100644 index 0000000000..9b9b0ad4de --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/WxPayValidator.java @@ -0,0 +1,52 @@ +package com.github.binarywang.wxpay.v3.auth; + + +import java.io.IOException; + +import com.github.binarywang.wxpay.v3.Validator; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.util.EntityUtils; + +@Slf4j +public class WxPayValidator implements Validator { + private Verifier verifier; + + public WxPayValidator(Verifier verifier) { + this.verifier = verifier; + } + + @Override + public final boolean validate(CloseableHttpResponse response) throws IOException { + Header serialNo = response.getFirstHeader("Wechatpay-Serial"); + Header sign = response.getFirstHeader("Wechatpay-Signature"); + Header timestamp = response.getFirstHeader("Wechatpay-TimeStamp"); + Header nonce = response.getFirstHeader("Wechatpay-Nonce"); + + // todo: check timestamp + if (timestamp == null || nonce == null || serialNo == null || sign == null) { + return false; + } + + String message = buildMessage(response); + return verifier.verify(serialNo.getValue(), message.getBytes("utf-8"), sign.getValue()); + } + + protected final String buildMessage(CloseableHttpResponse response) throws IOException { + String timestamp = response.getFirstHeader("Wechatpay-TimeStamp").getValue(); + String nonce = response.getFirstHeader("Wechatpay-Nonce").getValue(); + + String body = getResponseBody(response); + return timestamp + "\n" + + nonce + "\n" + + body + "\n"; + } + + protected final String getResponseBody(CloseableHttpResponse response) throws IOException { + HttpEntity entity = response.getEntity(); + + return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : ""; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/AesUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/AesUtils.java new file mode 100644 index 0000000000..4030965ebe --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/AesUtils.java @@ -0,0 +1,109 @@ +package com.github.binarywang.wxpay.v3.util; + +import com.google.common.base.CharMatcher; +import com.google.common.io.BaseEncoding; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AesUtils { + + static final int KEY_LENGTH_BYTE = 32; + static final int TAG_LENGTH_BIT = 128; + private final byte[] aesKey; + + public AesUtils(byte[] key) { + if (key.length != KEY_LENGTH_BYTE) { + throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); + } + this.aesKey = key; + } + + public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) + throws GeneralSecurityException, IOException { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + SecretKeySpec key = new SecretKeySpec(aesKey, "AES"); + GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce); + + cipher.init(Cipher.DECRYPT_MODE, key, spec); + cipher.updateAAD(associatedData); + + return new String(cipher.doFinal(BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(ciphertext))), "utf-8"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IllegalStateException(e); + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new IllegalArgumentException(e); + } + } + + public static String decryptToString(String associatedData, String nonce, String ciphertext,String apiV3Key) + throws GeneralSecurityException, IOException { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(), "AES"); + GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce.getBytes()); + + cipher.init(Cipher.DECRYPT_MODE, key, spec); + cipher.updateAAD(associatedData.getBytes()); + + return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IllegalStateException(e); + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new IllegalArgumentException(e); + } + } + + + public static String createSign(Map map, String mchKey) { + Map params = map; + SortedMap sortedMap = new TreeMap<>(params); + + StringBuilder toSign = new StringBuilder(); + for (String key : sortedMap.keySet()) { + String value = params.get(key); + if ("sign".equals(key) || StringUtils.isEmpty(value)) { + continue; + } + toSign.append(key).append("=").append(value).append("&"); + } + toSign.append("key=" + mchKey); + return HMACSHA256(toSign.toString(), mchKey); + + } + + public static String HMACSHA256(String data, String key) { + try { + Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); + SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); + sha256_HMAC.init(secret_key); + byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte item : array) { + sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); + } + return sb.toString().toUpperCase(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/PemUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/PemUtils.java new file mode 100644 index 0000000000..bf4d2657b5 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/PemUtils.java @@ -0,0 +1,60 @@ +package com.github.binarywang.wxpay.v3.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +public class PemUtils { + + public static PrivateKey loadPrivateKey(InputStream inputStream) { + try { + ByteArrayOutputStream array = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + array.write(buffer, 0, length); + } + + String privateKey = array.toString("utf-8") + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate( + new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持RSA", e); + } catch (InvalidKeySpecException e) { + throw new RuntimeException("无效的密钥格式"); + } catch (IOException e) { + throw new RuntimeException("无效的密钥"); + } + } + + public static X509Certificate loadCertificate(InputStream inputStream) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream); + cert.checkValidity(); + return cert; + } catch (CertificateExpiredException e) { + throw new RuntimeException("证书已过期", e); + } catch (CertificateNotYetValidException e) { + throw new RuntimeException("证书尚未生效", e); + } catch (CertificateException e) { + throw new RuntimeException("无效的证书", e); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java new file mode 100644 index 0000000000..d88c67e419 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java @@ -0,0 +1,97 @@ +package com.github.binarywang.wxpay.v3.util; + +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.v3.SpecEncrypt; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Base64; + +/** + * 微信支付敏感信息加密 + * 文档见: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/min-gan-xin-xi-jia-mi + * + * @author zhouyongshen + **/ +public class RsaCryptoUtil { + + + static String JAVA_LANG_STRING = "java.lang.String"; + + public static void encryptFields(Object encryptObject, X509Certificate certificate) throws WxPayException { + try { + encryptField(encryptObject, certificate); + } catch (Exception e) { + throw new WxPayException("敏感信息加密失败", e); + } + } + + private static void encryptField(Object encryptObject, X509Certificate certificate) throws IllegalAccessException, IllegalBlockSizeException { + Class infoClass = encryptObject.getClass(); + Field[] infoFieldArray = infoClass.getDeclaredFields(); + for (Field field : infoFieldArray) { + if (field.isAnnotationPresent(SpecEncrypt.class)) { + //字段使用了@SpecEncrypt进行标识 + if (field.getType().getTypeName().equals(JAVA_LANG_STRING)) { + field.setAccessible(true); + Object oldValue = field.get(encryptObject); + if (oldValue != null) { + String oldStr = (String) oldValue; + if (!oldStr.trim().equals("'")) { + field.set(encryptObject, encryptOAEP(oldStr, certificate)); + } + } + } else { + field.setAccessible(true); + Object obj = field.get(encryptObject); + if (obj != null) { + encryptField(field.get(encryptObject), certificate); + } + } + } + } + } + + public static String encryptOAEP(String message, X509Certificate certificate) + throws IllegalBlockSizeException { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey()); + + byte[] data = message.getBytes(StandardCharsets.UTF_8); + byte[] ciphertext = cipher.doFinal(data); + return Base64.getEncoder().encodeToString(ciphertext); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("无效的证书", e); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new IllegalBlockSizeException("加密原串的长度不能超过214字节"); + } + } + + public static String decryptOAEP(String ciphertext, PrivateKey privateKey) + throws BadPaddingException { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + byte[] data = Base64.getDecoder().decode(ciphertext); + return new String(cipher.doFinal(data), StandardCharsets.UTF_8); + } catch (NoSuchPaddingException | NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("无效的私钥", e); + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new BadPaddingException("解密失败"); + } + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequestTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequestTest.java new file mode 100644 index 0000000000..5cd4dcdc10 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequestTest.java @@ -0,0 +1,17 @@ +package com.github.binarywang.wxpay.bean.entpay; + +import org.testng.annotations.Test; + +/** + * . + * + * @author Binary Wang + * @date 2019-08-18 + */ +public class EntPayRequestTest { + + @Test + public void testToString() { + System.out.println(EntPayRequest.newBuilder().mchId("123").build().toString()); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponseTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponseTest.java new file mode 100644 index 0000000000..60be34e357 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyResponseTest.java @@ -0,0 +1,41 @@ +package com.github.binarywang.wxpay.bean.notify; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * WxPayNotifyResponse 测试. + * + * @author Binary Wang + * @date 2019-06-30 + */ +public class WxPayNotifyResponseTest { + + @Test + public void testSuccess() { + final String result = WxPayNotifyResponse.success("OK"); + assertThat(result).isEqualTo("" + + "" + + "" + + ""); + } + + @Test + public void testSuccessResp() { + final String result = WxPayNotifyResponse.successResp("OK"); + assertThat(result).isEqualTo("" + + "" + + "" + + ""); + } + + @Test + public void testFailResp() { + final String result = WxPayNotifyResponse.failResp("500"); + assertThat(result).isEqualTo("" + + "" + + "" + + ""); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java new file mode 100644 index 0000000000..f79e0859f0 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java @@ -0,0 +1,85 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.util.XmlConfig; +import org.testng.*; +import org.testng.annotations.*; + +/** + *
    + * Created by Binary Wang on 2017-6-15.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayOrderNotifyResultTest { + /** + * Test from xml. + */ + @Test + public void testFromXML() { + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " \n" + + " \n" + + " 2\n" + + " \n" + + " 10001\n" + + " 200\n" + + " \n" + + " 10000\n" + + " 100\n" + + ""; + + WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlString); + + Assert.assertEquals(result.getCouponCount().intValue(), 2); + Assert.assertNotNull(result.getCouponList()); + Assert.assertEquals(result.getCouponList().size(), 2); + + Assert.assertEquals(result.getCouponList().get(0).getCouponFee().intValue(), 100); + Assert.assertEquals(result.getCouponList().get(1).getCouponFee().intValue(), 200); + + Assert.assertEquals(result.getCouponList().get(0).getCouponType(), "CASH"); + Assert.assertEquals(result.getCouponList().get(1).getCouponType(), "NO_CASH"); + + Assert.assertEquals(result.getCouponList().get(0).getCouponId(), "10000"); + Assert.assertEquals(result.getCouponList().get(1).getCouponId(), "10001"); + + //fast mode test + XmlConfig.fastMode = true; + try { + result = BaseWxPayResult.fromXML(xmlString, WxPayOrderNotifyResult.class); + + Assert.assertEquals(result.getCouponCount().intValue(), 2); + Assert.assertNotNull(result.getCouponList()); + Assert.assertEquals(result.getCouponList().size(), 2); + + Assert.assertEquals(result.getCouponList().get(0).getCouponFee().intValue(), 100); + Assert.assertEquals(result.getCouponList().get(1).getCouponFee().intValue(), 200); + + Assert.assertEquals(result.getCouponList().get(0).getCouponType(), "CASH"); + Assert.assertEquals(result.getCouponList().get(1).getCouponType(), "NO_CASH"); + + Assert.assertEquals(result.getCouponList().get(0).getCouponId(), "10000"); + Assert.assertEquals(result.getCouponList().get(1).getCouponId(), "10001"); + } finally { + XmlConfig.fastMode = false; + } + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java new file mode 100644 index 0000000000..963afb2618 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java @@ -0,0 +1,129 @@ +package com.github.binarywang.wxpay.bean.notify; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.inject.Inject; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.util.XmlConfig; +import org.apache.commons.codec.binary.Base64; +import org.testng.annotations.*; + +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.testbase.ApiTestModule; + +import static org.testng.Assert.*; + +/** + *
    + *  Created by BinaryWang on 2017/8/27.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxPayRefundNotifyResultTest { + @Inject + private WxPayConfig wxPayConfig; + + /** + * Test from xml. + * + * @throws WxPayException the wx pay exception + */ + public void testFromXML() throws WxPayException { + String xmlString = "" + + "SUCCESS" + + "" + + "" + + "" + + ""; + + WxPayRefundNotifyResult refundNotifyResult = WxPayRefundNotifyResult.fromXML(xmlString, this.wxPayConfig.getMchKey()); + + assertNotNull(refundNotifyResult); + System.out.println(refundNotifyResult); + } + + /** + * Encode req info. + * + * @throws Exception the exception + */ + public void encodeReqInfo() throws Exception { + String xml = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + final MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(this.wxPayConfig.getMchKey().getBytes(StandardCharsets.UTF_8)); + final String keyMd5String = new BigInteger(1, md5.digest()).toString(16).toLowerCase(); + SecretKeySpec key = new SecretKeySpec(keyMd5String.getBytes(StandardCharsets.UTF_8), "AES"); + cipher.init(Cipher.ENCRYPT_MODE, key); + System.out.println(Base64.encodeBase64String(cipher.doFinal(xml.getBytes(StandardCharsets.UTF_8)))); + } + + /** + * Test from xml. + * fast mode + * + * @throws WxPayException the wx pay exception + */ + public void testFromXMLFastMode() throws WxPayException { + String xmlString = "" + + "SUCCESS" + + "" + + "" + + "" + + ""; + + String xmlDecryptedReqInfo = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + + XmlConfig.fastMode = true; + try { + WxPayRefundNotifyResult refundNotifyResult = BaseWxPayResult.fromXML(xmlString, WxPayRefundNotifyResult.class); + System.out.println(refundNotifyResult.getReqInfoString()); + + refundNotifyResult.loadReqInfo(xmlDecryptedReqInfo); + assertEquals(refundNotifyResult.getReqInfo().getRefundFee().intValue(), 15); + assertEquals(refundNotifyResult.getReqInfo().getRefundStatus(), "SUCCESS"); + assertEquals(refundNotifyResult.getReqInfo().getRefundRecvAccout(), "用户零钱"); + System.out.println(refundNotifyResult); + } finally { + XmlConfig.fastMode = false; + } + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResultTest.java new file mode 100644 index 0000000000..3d77eb34ae --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResultTest.java @@ -0,0 +1,88 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.github.binarywang.wxpay.util.XmlConfig; +import org.testng.annotations.*; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/2/2.
    + * 
    + * + * @author Binary Wang + */ +public class WxScanPayNotifyResultTest { + + /** + * Test to map. + */ + @Test + public void testToMap() { + } + + /** + * Test from xml. + */ + @Test + public void testFromXML() { + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + WxScanPayNotifyResult result = BaseWxPayResult.fromXML(xmlString, WxScanPayNotifyResult.class); + + assertThat(result).isNotNull(); + + assertThat(result.getAppid()).isEqualTo("wx8888888888888888"); + assertThat(result.getOpenid()).isEqualTo("o8GeHuLAsgefS_80exEr1cTqekUs"); + assertThat(result.getMchId()).isEqualTo("1900000109"); + assertThat(result.getNonceStr()).isEqualTo("5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); + assertThat(result.getProductId()).isEqualTo("88888"); + assertThat(result.getSign()).isEqualTo("C380BEC2BFD727A4B6845133519F3AD6"); + } + + + /** + * Test from xml. + * fast mode. + */ + @Test + public void testFromXMLFastMode() { + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + XmlConfig.fastMode = true; + try { + WxScanPayNotifyResult result = BaseWxPayResult.fromXML(xmlString, WxScanPayNotifyResult.class); + + assertThat(result).isNotNull(); + + assertThat(result.getAppid()).isEqualTo("wx8888888888888888"); + assertThat(result.getOpenid()).isEqualTo("o8GeHuLAsgefS_80exEr1cTqekUs"); + assertThat(result.getMchId()).isEqualTo("1900000109"); + assertThat(result.getNonceStr()).isEqualTo("5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); + assertThat(result.getProductId()).isEqualTo("88888"); + assertThat(result.getSign()).isEqualTo("C380BEC2BFD727A4B6845133519F3AD6"); + } finally { + XmlConfig.fastMode = false; + } + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequestTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequestTest.java new file mode 100644 index 0000000000..6976bba38c --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/payscore/WxPayScoreRequestTest.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.bean.payscore; + +import org.testng.annotations.Test; + +/** + * @author Binary Wang + * @date 2020-07-11 + */ +public class WxPayScoreRequestTest { + @Test + public void testToJson() { + WxPayScoreRequest request = WxPayScoreRequest.builder() + .outOrderNo("QLS202005201058000201") + .appid("123") + .serviceId("345") + .serviceIntroduction("租借服务") + .timeRange(new TimeRange("OnAccept", "20200520225840")) + .build(); + System.out.println(request.toJson()); + /* { + "out_order_no":"QLS202005201058000201", + "appid":"123", + "service_id":"345", + "service_introduction":"租借服务", + "time_range":{ + "start_time":"OnAccept", + "end_time":"20200520225840" + }, + "location":{ + "start_location":"山", + "end_location":"山" + }, + "risk_fund":{ + "name":"DEPOSIT", + "amount":200, + "description":"丢失偿还费用2元/台" + }, + "attach":"", + "notify_url":"/pay/notify/payScore", + "openid":"", + "need_user_confirm":true, + "profit_sharing":false, + "post_payments":[ + { + "name":"租借服务", + "amount":100, + "description":"服务费:1元/台", + "count":1 + } + ], + "total_amount":0 + }*/ + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResultTest.java new file mode 100644 index 0000000000..386101fed0 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResultTest.java @@ -0,0 +1,59 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import org.testng.annotations.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试. + * + * @author Binary Wang + * @date 2020-03-22 + */ +@Test +public class ProfitSharingQueryResultTest { + + @Test + public void testFormatReceivers() { + ProfitSharingQueryResult result = new ProfitSharingQueryResult(); + result.setReceiversJson("[\n" + + "{\n" + + "\"type\": \"MERCHANT_ID\",\n" + + "\"account\":\"190001001\",\n" + + "\"amount\":100,\n" + + "\"description\": \"分到商户\",\n" + + "\"result\": \"SUCCESS\",\n" + + "\"finish_time\": \"20180608170132\"\n" + + "},\n" + + "{\n" + + "\"type\": \"PERSONAL_WECHATID\",\n" + + "\"account\":\"86693952\",\n" + + "\"amount\":888,\n" + + "\"description\": \"分到个人\",\n" + + "\"result\": \"SUCCESS\",\n" + + "\"finish_time\": \"20180608170132\"\n" + + "}\n" + + "]"); + + List receivers = result.formatReceivers(); + assertThat(receivers).isNotEmpty(); + + assertThat(receivers.get(0)).isNotNull(); + assertThat(receivers.get(0).getType()).isEqualTo("MERCHANT_ID"); + assertThat(receivers.get(0).getAccount()).isEqualTo("190001001"); + assertThat(receivers.get(0).getAmount()).isEqualTo(100); + assertThat(receivers.get(0).getDescription()).isEqualTo("分到商户"); + assertThat(receivers.get(0).getResult()).isEqualTo("SUCCESS"); + assertThat(receivers.get(0).getFinishTime()).isEqualTo("20180608170132"); + + assertThat(receivers.get(1)).isNotNull(); + assertThat(receivers.get(1).getType()).isEqualTo("PERSONAL_WECHATID"); + assertThat(receivers.get(1).getAccount()).isEqualTo("86693952"); + assertThat(receivers.get(1).getAmount()).isEqualTo(888); + assertThat(receivers.get(1).getDescription()).isEqualTo("分到个人"); + assertThat(receivers.get(1).getResult()).isEqualTo("SUCCESS"); + assertThat(receivers.get(1).getFinishTime()).isEqualTo("20180608170132"); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequestTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequestTest.java new file mode 100644 index 0000000000..1118edea87 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequestTest.java @@ -0,0 +1,47 @@ +package com.github.binarywang.wxpay.bean.request; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Binary Wang + * @date 2020-06-07 + */ +public class WxPayRefundRequestTest { + + @Test + public void testToXML() { + WxPayRefundRequest refundRequest = new WxPayRefundRequest(); + refundRequest.setAppid("wx2421b1c4370ec43b"); + refundRequest.setMchId("10000100"); + refundRequest.setNonceStr("6cefdb308e1e2e8aabd48cf79e546a02"); + refundRequest.setNotifyUrl("https://weixin.qq.com/"); + refundRequest.setOutRefundNo("1415701182"); + refundRequest.setOutTradeNo("1415757673"); + refundRequest.setRefundFee(1); + refundRequest.setTotalFee(1); + refundRequest.setTransactionId(""); + refundRequest.setDetail("{\"goods_detail\":[{\"goods_id\":\"商品编码\",\"wxpay_goods_id\":\"1001\",\"goods_name\":\"iPhone6s\n" + + "16G\",\"refund_amount\":528800,\"refund_quantity\":1,\"price\":528800},{\"goods_id\":\"商品编码\",\"wxpay_goods_id\":\"1001\",\"goods_name\":\"iPhone6s\n" + + "16G\",\"refund_amount\"\":528800,\"refund_quantity\":1,\"price\":608800}]}"); + refundRequest.setSign("FE56DD4AA85C0EECA82C35595A69E153"); + + assertThat(refundRequest.toXML()) + .isEqualTo("\n" + + " wx2421b1c4370ec43b\n" + + " 10000100\n" + + " 6cefdb308e1e2e8aabd48cf79e546a02\n" + + " FE56DD4AA85C0EECA82C35595A69E153\n" + + " \n" + + " 1415757673\n" + + " 1415701182\n" + + " 1\n" + + " 1\n" + + " https://weixin.qq.com/\n" + + " \n" + + ""); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResultTest.java similarity index 81% rename from weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResultTest.java index d6a1f7b827..6b70d5542f 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResultTest.java @@ -1,26 +1,42 @@ package com.github.binarywang.wxpay.bean.result; +import java.util.Map; + import org.testng.*; import org.testng.annotations.*; -import java.util.Map; - /** *
      * Created by Binary Wang on 2017-01-04.
    - * @author binarywang(Binary Wang)
      * 
    + * + * @author binarywang(Binary Wang) */ -public class WxPayBaseResultTest { +public class BaseWxPayResultTest { + /** + * Test get xml value. + * + * @throws Exception the exception + */ @Test public void testGetXmlValue() throws Exception { } + /** + * Test xml 2 doc. + * + * @throws Exception the exception + */ @Test public void testXml2Doc() throws Exception { } + /** + * Test to map. + * + * @throws Exception the exception + */ @Test public void testToMap() throws Exception { WxPayOrderQueryResult result = new WxPayOrderQueryResult(); @@ -53,10 +69,15 @@ public void testToMap() throws Exception { } + /** + * Test to map with empty xml string. + */ @Test(expectedExceptions = {RuntimeException.class}) public void testToMap_with_empty_xmlString() { WxPayOrderQueryResult result = new WxPayOrderQueryResult(); - result.setXmlString(" "); + result.setXmlString( "]" + + ">&win;"); Map map = result.toMap(); System.out.println(map); } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBillResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBillResultTest.java new file mode 100644 index 0000000000..6d5d322602 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBillResultTest.java @@ -0,0 +1,84 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.github.binarywang.wxpay.constant.WxPayConstants; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +/** + * @author m8cool + */ +public class WxPayBillResultTest { + private static final String PAY_BILL_RESULT_ALL_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注\n" + + "`2019-07-25 08:35:41,`WWWW,`XXXXXXXX,`0,`,`XXXXXXXXXXXXX,`XXXXXXXXXX,`XXXXXXXXXXX,`JSAPI,`SUCCESS,`PSBC_DEBIT,`CNY,`6.00,`0.00,`0,`0,`0.00,`0.00,`,`,`XXXXXX,`XXXXXXX,`0.04000,`0.60%,`6.00,`0.00,`\n" + + "总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额\n" + + "`48,`5.76,`1.42,`0.00,`0.01000,`5.76,`1.42\n"; + private static final String PAY_BILL_RESULT_ALL_CONTENT_1 = "交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,总金额,代金券或立减优惠金额,微信退款单号,商户退款单号,退款金额,代金券或立减优惠退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率\n" + + "`2014-11-10 16:33:45,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1001690740201411100005734289,`1415640626,`085e9858e3ba5186aafcbaed1,`MICROPAY,`SUCCESS,`OTHERS,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被扫支付测试,`订单额外描述,`0,`0.60%\n" + + "`2014-11-10 16:46:14,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1002780740201411100005729794,`1415635270,`085e9858e90ca40c0b5aee463,`MICROPAY,`SUCCESS,`OTHERS,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被扫支付测试,`订单额外描述,`0,`0.60%\n" + + "总交易单数,总交易额,总退款金额,总代金券或立减优惠退款金额,手续费总金额\n" + + "`2,`0.02,`0.0,`0.0,`0"; + + private static final String PAY_BILL_RESULT_SUCCESS_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,商品名称,商户数据包,手续费,费率,订单金额,费率备注\n" + + "`2019-07-23 18:46:41,`XXXX,`XXX,`XXX,`,`XXX,`XXX,`XXX,`JSAPI,`SUCCESS,`CFT,`CNY,`0.01,`0.00,`XXX,`XXXX,`0.00000,`0.60%,`0.01,`\n" + + "总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额\n" + + "`31,`3.05,`0.00,`0.00,`0.02000,`3.05,`0.00"; + private static final String PAY_BILL_RESULT_REFUND_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,退款申请时间,退款成功时间,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注\n" + + "`2019-07-23 20:53:36,`xxx,`xxx,`xxx,`,`xxx,`xxxx,`xxxxx,`JSAPI,`REFUND,`CFT,`CNY,`0.00,`0.00,`2019-07-23 20:53:36,`2019-07-23 20:53:40,`xxxx,`xxx,`0.01,`0.00,`ORIGINAL,`SUCCESS,`xxxx,`xxxx,`0.00000,`0.60%,`0.00,`0.01,`\n" + + "总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额\n" + + "`4,`0.00,`2.02,`0.00,`-0.02000,`0.00,`2.02"; + + + /** + * 测试微信返回类型为ALL时,解析结果是否正确 + */ + @Test + public void testFromRawBillResultStringAll() { + WxPayBillResult result = WxPayBillResult.fromRawBillResultString(PAY_BILL_RESULT_ALL_CONTENT, WxPayConstants.BillType.ALL); + + assertEquals(result.getTotalRecord(), "48"); + assertEquals(result.getTotalFee(), "5.76"); + assertEquals(result.getTotalRefundFee(), "1.42"); + assertEquals(result.getTotalCouponFee(), "0.00"); + assertEquals(result.getTotalPoundageFee(), "0.01000"); + assertEquals(result.getTotalAmount(), "5.76"); + assertEquals(result.getTotalAppliedRefundFee(), "1.42"); + assertEquals(result.getBillInfoList().get(0).getTotalAmount(), "6.00"); + assertEquals(result.getBillInfoList().get(0).getAppliedRefundAmount(), "0.00"); + assertEquals(result.getBillInfoList().get(0).getFeeRemark(), ""); + + result = WxPayBillResult.fromRawBillResultString(PAY_BILL_RESULT_ALL_CONTENT_1, WxPayConstants.BillType.ALL); + + assertEquals(result.getTotalRecord(), "2"); + assertEquals(result.getTotalFee(), "0.02"); + assertEquals(result.getTotalRefundFee(), "0.0"); + assertEquals(result.getTotalCouponFee(), "0.0"); + assertEquals(result.getTotalPoundageFee(), "0"); + assertNull(result.getTotalAmount()); + assertNull(result.getTotalAppliedRefundFee()); + assertNull(result.getBillInfoList().get(0).getTotalAmount()); + assertNull(result.getBillInfoList().get(0).getAppliedRefundAmount()); + assertNull(result.getBillInfoList().get(0).getFeeRemark()); + + } + + /** + * 测试微信返回类型为SUCCESS时,解析结果是否正确 + */ + @Test + public void testFromRawBillResultStringSuccess() { + WxPayBillResult result = WxPayBillResult.fromRawBillResultString(PAY_BILL_RESULT_SUCCESS_CONTENT, WxPayConstants.BillType.SUCCESS); + + assertEquals(result.getTotalRecord(), "31"); + assertEquals(result.getTotalFee(), "3.05"); + assertEquals(result.getTotalRefundFee(), "0.00"); + assertEquals(result.getTotalCouponFee(), "0.00"); + assertEquals(result.getTotalPoundageFee(), "0.02000"); + assertEquals(result.getTotalAmount(), "3.05"); + assertEquals(result.getTotalAppliedRefundFee(), "0.00"); + assertEquals(result.getBillInfoList().get(0).getTotalAmount(), "0.01"); + assertEquals(result.getBillInfoList().get(0).getFeeRemark(), ""); + + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResultTest.java index d847a155b9..c930b7ccb8 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResultTest.java @@ -6,13 +6,17 @@ /** *
      * Created by Binary Wang on 2017-01-04.
    + *  
    + * * @author binarywang(Binary Wang) - *
    */ public class WxPayOrderQueryResultTest { + /** + * Test compose coupons. + */ @Test - public void testComposeCoupons() throws Exception { - /** + public void testComposeCoupons() { + /* * xml样例字符串来自于官方文档,并稍加改造加入了coupon相关的数据便于测试 */ String xmlString = "\n" + diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResultTest.java new file mode 100644 index 0000000000..617e2afdf4 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResultTest.java @@ -0,0 +1,116 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.github.binarywang.wxpay.util.XmlConfig; +import org.testng.annotations.*; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *
    + * Created by Binary Wang on 2018/1/24.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayRedpackQueryResultTest { + /** + * Test from xml. + */ + @Test + public void testFromXML() { + String xmlString = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "1\n" + + "100\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "100\n" + + "\n" + + "\n" + + "\n" + + ""; + + WxPayRedpackQueryResult orderQueryResult = BaseWxPayResult.fromXML(xmlString, WxPayRedpackQueryResult.class); +// System.out.println(orderQueryResult); + assertThat(orderQueryResult).isNotNull(); + + assertThat(orderQueryResult.getRedpackList()).isNotEmpty(); + assertThat(orderQueryResult.getRedpackList().get(0).getAmount()).isEqualTo(100); + assertThat(orderQueryResult.getRedpackList().get(0).getOpenid()).isEqualTo("o3yHF0uHuckI3yE6lwWiFQBQdVDI"); + assertThat(orderQueryResult.getRedpackList().get(0).getReceiveTime()).isEqualTo("2018-01-23 13:45:31"); + } + + /** + * Test from xml. + * FastMode + */ + @Test + public void testFromXMLFastMode() { + XmlConfig.fastMode = true; + String xmlString = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "1\n" + + "100\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "100\n" + + "\n" + + "\n" + + "\n" + + ""; + + try { + WxPayRedpackQueryResult orderQueryResult = BaseWxPayResult.fromXML(xmlString, WxPayRedpackQueryResult.class); +// System.out.println(orderQueryResult); + assertThat(orderQueryResult).isNotNull(); + + assertThat(orderQueryResult.getRedpackList()).isNotEmpty(); + assertThat(orderQueryResult.getRedpackList().get(0).getAmount()).isEqualTo(100); + assertThat(orderQueryResult.getRedpackList().get(0).getOpenid()).isEqualTo("o3yHF0uHuckI3yE6lwWiFQBQdVDI"); + assertThat(orderQueryResult.getRedpackList().get(0).getReceiveTime()).isEqualTo("2018-01-23 13:45:31"); + } finally { + XmlConfig.fastMode = false; + } + } + + @Test + void benchmark() { + long now = System.currentTimeMillis(); + int loops = 10000; + for (int i = 0; i < loops; i++) { + testFromXML(); + } + System.out.println(" reflect mode:\t" + (System.currentTimeMillis() - now) + " (ms) "); + + now = System.currentTimeMillis(); + for (int i = 0; i < loops; i++) { + testFromXMLFastMode(); + } + System.out.println(" fast mode:\t" + (System.currentTimeMillis() - now) + " (ms) "); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResultTest.java index 6ca4192a71..e9e8e80bf7 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResultTest.java @@ -7,10 +7,16 @@ /** *
      * Created by Binary Wang on 2016-12-29.
    + *  
    + * * @author binarywang(Binary Wang) - *
    */ public class WxPayRefundQueryResultTest { + /** + * Compose refund records. + * + * @throws Exception the exception + */ @Test public void composeRefundRecords() throws Exception { /* diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResultTest.java new file mode 100644 index 0000000000..3df177f2c9 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResultTest.java @@ -0,0 +1,149 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.github.binarywang.wxpay.util.XmlConfig; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
    + *  Created by BinaryWang on 2018/4/22.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayRefundResultTest { + @Test + public void testFromXML() { + /* + 该xml字符串来自于官方文档示例,稍加改造,加上代金卷 + refund_channel 是个什么鬼,官方文档只字不提 + */ + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " 1\n" + + " 123\n" + + " 1\n" + + " \n" + + " 2 \n" + + ""; + + WxPayRefundResult result = BaseWxPayResult.fromXML(xmlString, WxPayRefundResult.class); + result.composeRefundCoupons(); + assertThat(result.getRefundCoupons()).isNotEmpty(); + assertThat(result.getRefundCoupons().get(0).getCouponRefundId()).isEqualTo("123"); + assertThat(result.getRefundCoupons().get(0).getCouponType()).isEqualTo("CASH"); + assertThat(result.getRefundCoupons().get(0).getCouponRefundFee()).isEqualTo(1); + } + + @Test + public void testFromXML_danpin() { + //样例来自:https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_103&index=3 + String xmlString = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "1\n" + + "1\n" + + "1\n" + + "1\n" + + "{\"promotion_detail\":[{\"promotion_id\":\"109519\",\"scope\":\"SINGLE\",\"type\":\"DISCOUNT\",\"refund_amount\":5,\"goods_detail\":[{\"goods_id\":\"a_goods1\",\"refund_quantity\":7,\"price\":1,\"refund_amount\":4},{\"goods_id\":\"a_goods2\",\"refund_quantity\":1,\"price\":2,\"refund_amount\":1}]}]}\n" + + ""; + + WxPayRefundResult result = BaseWxPayResult.fromXML(xmlString, WxPayRefundResult.class); + result.composePromotionDetails(); + assertThat(result.getPromotionDetails()).isNotEmpty(); + assertThat(result.getPromotionDetails().get(0).getPromotionId()).isEqualTo("109519"); + assertThat(result.getPromotionDetails().get(0).getRefundAmount()).isEqualTo(5); + assertThat(result.getPromotionDetails().get(0).getScope()).isEqualTo("SINGLE"); + assertThat(result.getPromotionDetails().get(0).getType()).isEqualTo("DISCOUNT"); + + assertThat(result.getPromotionDetails().get(0).getGoodsDetails()).isNotEmpty(); + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(0).getGoodsId()).isEqualTo("a_goods1"); + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(0).getRefundQuantity()).isEqualTo(7); + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(0).getRefundAmount()).isEqualTo(4); + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(0).getPrice()).isEqualTo(1); + + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(1).getGoodsId()).isEqualTo("a_goods2"); + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(1).getRefundQuantity()).isEqualTo(1); + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(1).getRefundAmount()).isEqualTo(1); + assertThat(result.getPromotionDetails().get(0).getGoodsDetails().get(1).getPrice()).isEqualTo(2); + } + + @Test + public void testFromXMLFastMode() { + /* + 该xml字符串来自于官方文档示例,稍加改造,加上代金卷 + refund_channel 是个什么鬼,官方文档只字不提 + */ + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " 1\n" + + " 123\n" + + " 1\n" + + " \n" + + " 2 \n" + + ""; + XmlConfig.fastMode = true; + try { + WxPayRefundResult result = BaseWxPayResult.fromXML(xmlString, WxPayRefundResult.class); + result.composeRefundCoupons(); + assertThat(result.getRefundCoupons()).isNotEmpty(); + assertThat(result.getRefundCoupons().get(0).getCouponRefundId()).isEqualTo("123"); + assertThat(result.getRefundCoupons().get(0).getCouponType()).isEqualTo("CASH"); + assertThat(result.getRefundCoupons().get(0).getCouponRefundFee()).isEqualTo(1); + } finally { + XmlConfig.fastMode = false; + } + } + + @Test + void benchmark() { + long now = System.currentTimeMillis(); + int loops = 10000; + for (int i = 0; i < loops; i++) { + testFromXML(); + } + System.out.println(" reflect mode:\t" + (System.currentTimeMillis() - now) + " (ms) "); + + now = System.currentTimeMillis(); + for (int i = 0; i < loops; i++) { + testFromXMLFastMode(); + } + System.out.println(" fast mode:\t" + (System.currentTimeMillis() - now) + " (ms) "); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResultTest.java index 1985c924d0..8ad4c246af 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResultTest.java @@ -2,19 +2,29 @@ import com.thoughtworks.xstream.XStream; import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; +/** + * The type Wx pay send redpack result test. + */ public class WxPaySendRedpackResultTest { private XStream xstream; + /** + * Sets . + */ @BeforeTest public void setup() { this.xstream = XStreamInitializer.getInstance(); this.xstream.processAnnotations(WxPaySendRedpackResult.class); } + /** + * Load success result. + */ @Test public void loadSuccessResult() { final String successSample = "\n" + @@ -37,6 +47,9 @@ public void loadSuccessResult() { Assert.assertEquals("20150520102602", wxMpRedpackResult.getSendTime()); } + /** + * Load failure result. + */ @Test public void loadFailureResult() { final String failureSample = "\n" + @@ -55,6 +68,6 @@ public void loadFailureResult() { Assert.assertEquals("FAIL", wxMpRedpackResult.getReturnCode()); Assert.assertEquals("FAIL", wxMpRedpackResult.getResultCode()); Assert.assertEquals("onqOjjmM1tad-3ROpncN-yUfa6uI", wxMpRedpackResult.getReOpenid()); - Assert.assertEquals(1, wxMpRedpackResult.getTotalAmount()); + Assert.assertEquals(1, wxMpRedpackResult.getTotalAmount().intValue()); } } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/config/WxPayConfigTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/config/WxPayConfigTest.java new file mode 100644 index 0000000000..fb46c58a4d --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/config/WxPayConfigTest.java @@ -0,0 +1,34 @@ +package com.github.binarywang.wxpay.config; + +import org.testng.annotations.Test; + +/** + *
    + *  Created by BinaryWang on 2017/6/18.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayConfigTest { + private final WxPayConfig payConfig = new WxPayConfig(); + + @Test + public void testInitSSLContext_classpath() throws Exception { + payConfig.setMchId("123"); + payConfig.setKeyPath("classpath:/dlt.p12"); + payConfig.initSSLContext(); + } + + @Test + public void testInitSSLContext_http() throws Exception { + payConfig.setMchId("123"); + payConfig.setKeyPath("https://www.baidu.com"); + payConfig.initSSLContext(); + } + + @Test + public void testInitSSLContext() throws Exception { + this.testInitSSLContext_classpath(); + this.testInitSSLContext_http(); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/Applyment4SubServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/Applyment4SubServiceImplTest.java new file mode 100644 index 0000000000..da268ce9e8 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/Applyment4SubServiceImplTest.java @@ -0,0 +1,87 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.applyment.ModifySettlementRequest; +import com.github.binarywang.wxpay.bean.applyment.WxPayApplyment4SubCreateRequest; +import com.github.binarywang.wxpay.bean.applyment.WxPayApplymentCreateResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.Applyment4SubService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +@Slf4j +@Test +@Guice(modules = ApiTestModule.class) +public class Applyment4SubServiceImplTest { + @Inject + private WxPayService wxPayService; + + private static final Gson GSON = new GsonBuilder().create(); + + @Test + public void testCreateApply() throws WxPayException { + Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService); + String requestParamStr="{}"; + /* + {"business_code":"1596785690732","contact_info":{"contact_name":"张三","contact_id_number":"110110202001011234","mobile_phone":"13112345678","contact_email":"abc@qq.com"},"subject_info":{"subject_type":"SUBJECT_TYPE_ENTERPRISE","business_license_info":{"license_copy":"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI","license_number":"123456789012345678","merchant_name":"腾讯科技有限公司","legal_person":"张三"},"identity_info":{"id_doc_type":"IDENTIFICATION_TYPE_IDCARD","id_card_info":{"id_card_copy":"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI","id_card_national":"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI","id_card_name":"张三","id_card_number":"110110202001011234","card_period_begin":"2016-06-06","card_period_end":"2026-06-06"},"owner":false},"ubo_info":{"id_type":"IDENTIFICATION_TYPE_IDCARD","id_card_copy":"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI","id_card_national":"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI","id_doc_copy":"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI","name":"张三","id_number":"110110202001011234","id_period_begin":"2016-06-06","id_period_end":"2026-06-06"}},"business_info":{"merchant_shortname":"商户简称","service_phone":"13212345678","sales_info":{"sales_scenes_type":["SALES_SCENES_MINI_PROGRAM"],"mini_program_info":{"mini_program_appid":"wxe5f52902cf4de896"}}},"settlement_info":{"settlement_id":"716","qualification_type":"餐饮"}} + */ + requestParamStr="{\"business_code\":\"1596785690732\",\"contact_info\":{\"contact_name\":\"张三\",\"contact_id_number\":\"110110202001011234\",\"mobile_phone\":\"13112345678\",\"contact_email\":\"abc@qq.com\"},\"subject_info\":{\"subject_type\":\"SUBJECT_TYPE_ENTERPRISE\",\"business_license_info\":{\"license_copy\":\"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI\",\"license_number\":\"123456789012345678\",\"merchant_name\":\"腾讯科技有限公司\",\"legal_person\":\"张三\"},\"identity_info\":{\"id_doc_type\":\"IDENTIFICATION_TYPE_IDCARD\",\"id_card_info\":{\"id_card_copy\":\"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI\",\"id_card_national\":\"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI\",\"id_card_name\":\"张三\",\"id_card_number\":\"110110202001011234\",\"card_period_begin\":\"2016-06-06\",\"card_period_end\":\"2026-06-06\"},\"owner\":false},\"ubo_info\":{\"id_type\":\"IDENTIFICATION_TYPE_IDCARD\",\"id_card_copy\":\"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI\",\"id_card_national\":\"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI\",\"id_doc_copy\":\"mxX07DyfM-bJyGJYCTyW-4wrXpJ5fq_bgYfWkIZZgjenf6Ct1gKV_FpkzgyQrf5ETVEyOWhC_0cbhOATODuLBAkxGl6Cvj31lh6OFAIHnwI\",\"name\":\"张三\",\"id_number\":\"110110202001011234\",\"id_period_begin\":\"2016-06-06\",\"id_period_end\":\"2026-06-06\"}},\"business_info\":{\"merchant_shortname\":\"商户简称\",\"service_phone\":\"13212345678\",\"sales_info\":{\"sales_scenes_type\":[\"SALES_SCENES_MINI_PROGRAM\"],\"mini_program_info\":{\"mini_program_appid\":\"wxe5f52902cf4de896\"}}},\"settlement_info\":{\"settlement_id\":\"716\",\"qualification_type\":\"餐饮\"}}"; + + WxPayApplyment4SubCreateRequest request=GSON.fromJson(requestParamStr,WxPayApplyment4SubCreateRequest.class); + String businessCode = String.valueOf(System.currentTimeMillis()); + request.setBusinessCode(businessCode); + + WxPayApplymentCreateResult apply = applyment4SubService.createApply(request); + String applymentId = apply.getApplymentId(); + log.info("businessCode:[{}],applymentId:[{}]",businessCode,applymentId); + + } + + @Test + public void testQueryApplyStatusByBusinessCode() throws WxPayException { + Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService); + String businessCode="businessCode"; + + applyment4SubService.queryApplyStatusByBusinessCode(businessCode); + + + } + + @Test + public void testQueryApplyStatusByApplymentId() throws WxPayException { + Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService); + String applymentId="applymentId"; + + applyment4SubService.queryApplyStatusByApplymentId(applymentId); + + } + + @Test + public void testQuerySettlementBySubMchid() throws WxPayException { + Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService); + String subMchid="subMchid"; + + applyment4SubService.querySettlementBySubMchid(subMchid); + + } + + @Test + public void testModifySettlement() throws WxPayException { + Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService); + String subMchid="subMchid"; + ModifySettlementRequest modifySettlementRequest = new ModifySettlementRequest(); + + applyment4SubService.modifySettlement(subMchid,modifySettlementRequest); + } + + + + + + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java new file mode 100644 index 0000000000..779916162c --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java @@ -0,0 +1,707 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.github.binarywang.wxpay.bean.coupon.*; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResultTest; +import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; +import com.github.binarywang.wxpay.bean.request.*; +import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.constant.WxPayConstants.AccountType; +import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; +import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; +import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.github.binarywang.wxpay.testbase.XmlWxPayConfig; +import com.github.binarywang.wxpay.util.XmlConfig; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Calendar; +import java.util.Date; + +import static com.github.binarywang.wxpay.constant.WxPayConstants.TarType; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.*; + +/** + * 测试支付相关接口 + * Created by Binary Wang on 2016/7/28. + * + * @author Binary Wang + */ +@Slf4j +@Test +@Guice(modules = ApiTestModule.class) +public class BaseWxPayServiceImplTest { + + @Inject + private WxPayService payService; + + /** + * Test method for {@link WxPayService#unifiedOrder(WxPayUnifiedOrderRequest)}. + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testUnifiedOrder() throws WxPayException { + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() + .body("我去") + .totalFee(1) + .spbillCreateIp("11.1.11.1") + .notifyUrl("111111") + .tradeType(TradeType.JSAPI) + .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) + .outTradeNo("111111826") + .attach("#*#{\"pn\":\"粤B87965\",\"aid\":\"wx123\"}#*#") + .build(); + request.setSignType(SignType.HMAC_SHA256); + WxPayUnifiedOrderResult result = this.payService.unifiedOrder(request); + log.info(result.toString()); + log.warn(this.payService.getWxApiData().toString()); + } + + /** + * Test create order. + * + * @throws Exception the exception + */ + @Test + public void testCreateOrder() throws Exception { + //see other tests with method name starting with 'testCreateOrder_' + } + + /** + * Test create order jsapi. + * + * @throws Exception the exception + */ + @Test + public void testCreateOrder_jsapi() throws Exception { + WxPayMpOrderResult result = this.payService + .createOrder(WxPayUnifiedOrderRequest.newBuilder() + .body("我去") + .totalFee(1) + .spbillCreateIp("11.1.11.1") + .notifyUrl("111111") + .tradeType(TradeType.JSAPI) + .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) + .outTradeNo("1111112") + .build()); + log.info(result.toString()); + log.warn(this.payService.getWxApiData().toString()); + } + + /** + * Test create order app. + * + * @throws Exception the exception + */ + @Test + public void testCreateOrder_app() throws Exception { + WxPayAppOrderResult result = this.payService + .createOrder(WxPayUnifiedOrderRequest.newBuilder() + .body("我去") + .totalFee(1) + .spbillCreateIp("11.1.11.1") + .notifyUrl("111111") + .tradeType(TradeType.APP) + .outTradeNo("1111112") + .build()); + log.info(result.toString()); + log.warn(this.payService.getWxApiData().toString()); + } + + /** + * Test create order native. + * + * @throws Exception the exception + */ + @Test + public void testCreateOrder_native() throws Exception { + WxPayNativeOrderResult result = this.payService + .createOrder(WxPayUnifiedOrderRequest.newBuilder() + .body("我去") + .totalFee(1) + .productId("aaa") + .spbillCreateIp("11.1.11.1") + .notifyUrl("111111") + .tradeType(TradeType.NATIVE) + .outTradeNo("111111290") + .build()); + log.info(result.toString()); + log.warn(this.payService.getWxApiData().toString()); + } + + @Test + public void testCreateOrderSpecific() throws Exception { + // Won't compile + // WxPayMpOrderResult result = payService.createOrder(TradeType.Specific.APP, new WxPayUnifiedOrderRequest()); + payService.createOrder( + TradeType.Specific.JSAPI, + WxPayUnifiedOrderRequest.newBuilder() + .body("我去") + .totalFee(1) + .productId("aaa") + .spbillCreateIp("11.1.11.1") + .notifyUrl("111111") + .outTradeNo("111111290") + .build() + ) + .getAppId(); + } + + /** + * Test get pay info. + * + * @throws Exception the exception + */ + @Test + public void testGetPayInfo() throws Exception { + //please use createOrder instead + } + + /** + * Test method for {@link WxPayService#queryOrder(String, String)} . + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testQueryOrder() throws WxPayException { + log.info(this.payService.queryOrder("11212121", null).toString()); + log.info(this.payService.queryOrder(null, "11111").toString()); + } + + /** + * Test method for {@link WxPayService#closeOrder(String)} . + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testCloseOrder() throws WxPayException { + log.info(this.payService.closeOrder("11212121").toString()); + } + + /** + * Billing data object [ ] [ ]. + * + * @return the object [ ] [ ] + */ + @DataProvider + public Object[][] billingData() { + return new Object[][]{ + {"20170831", BillType.ALL, TarType.GZIP, "deviceInfo"}, + {"20170831", BillType.RECHARGE_REFUND, TarType.GZIP, "deviceInfo"}, + {"20170831", BillType.REFUND, TarType.GZIP, "deviceInfo"}, + {"20170831", BillType.SUCCESS, TarType.GZIP, "deviceInfo"}, + {"20170831", BillType.ALL, null, "deviceInfo"}, + {"20170831", BillType.RECHARGE_REFUND, null, "deviceInfo"}, + {"20170831", BillType.REFUND, null, "deviceInfo"}, + {"20170831", BillType.SUCCESS, null, "deviceInfo"} + }; + } + + /** + * Test download bill. + * + * @param billDate the bill date + * @param billType the bill type + * @param tarType the tar type + * @param deviceInfo the device info + * @throws Exception the exception + */ + @Test(dataProvider = "billingData") + public void testDownloadBill(String billDate, String billType, + String tarType, String deviceInfo) throws Exception { + WxPayBillResult billResult = this.payService.downloadBill(billDate, billType, tarType, deviceInfo); + assertThat(billResult).isNotNull(); + log.info(billResult.toString()); + } + + /** + * Test download bill with no params. + * + * @throws Exception the exception + */ + @Test(expectedExceptions = WxPayException.class) + public void testDownloadBill_withNoParams() throws Exception { + //必填字段为空时,抛出异常 + this.payService.downloadBill("", "", "", null); + } + + /** + * Fund flow data object [ ] [ ]. + * + * @return the object [ ] [ ] + */ + @DataProvider + public Object[][] fundFlowData() { + return new Object[][]{ + {"20180819", AccountType.BASIC, TarType.GZIP}, + {"20180819", AccountType.OPERATION, TarType.GZIP}, + {"20180819", AccountType.FEES, TarType.GZIP}, + {"20180819", AccountType.BASIC, null}, + {"20180819", AccountType.OPERATION, null}, + {"20180819", AccountType.FEES, null} + }; + } + + /** + * Test download fund flow. + * + * @param billDate the bill date + * @param accountType the account type + * @param tarType the tar type + * @throws Exception the exception + */ + @Test(dataProvider = "fundFlowData") + public void testDownloadFundFlow(String billDate, String accountType, String tarType) throws Exception { + WxPayFundFlowResult fundFlowResult = this.payService.downloadFundFlow(billDate, accountType, tarType); + assertThat(fundFlowResult).isNotNull(); + log.info(fundFlowResult.toString()); + } + + /** + * Test download fund flow with no params. + * + * @throws Exception the exception + */ + @Test(expectedExceptions = WxPayException.class) + public void testDownloadFundFlow_withNoParams() throws Exception { + //必填字段为空时,抛出异常 + this.payService.downloadFundFlow("", "", null); + } + + /** + * Test report. + * + * @throws Exception the exception + */ + @Test + public void testReport() throws Exception { + WxPayReportRequest request = new WxPayReportRequest(); + request.setInterfaceUrl("hahahah"); + request.setSignType(SignType.HMAC_SHA256);//貌似接口未校验此字段 + request.setExecuteTime(1000); + request.setReturnCode("aaa"); + request.setResultCode("aaa"); + request.setUserIp("8.8.8"); + this.payService.report(request); + } + + /** + * Test method for {@link WxPayService#refund(WxPayRefundRequest)} . + * + * @throws Exception the exception + */ + @Test + public void testRefund() throws Exception { + WxPayRefundResult result = this.payService.refund( + WxPayRefundRequest.newBuilder() + .outRefundNo("aaa") + .outTradeNo("1111") + .totalFee(1222) + .refundFee(111) + .build()); + log.info(result.toString()); + } + + @Test + public void testRefundV2() throws WxPayException { + WxPayRefundResult result = this.payService.refundV2( + WxPayRefundRequest.newBuilder() + .outRefundNo("aaa") + .outTradeNo("1111") + .totalFee(1222) + .refundFee(111) + .build()); + log.info(result.toString()); + } + + /** + * Test method for {@link WxPayService#refundQuery(String, String, String, String)} . + * + * @throws Exception the exception + */ + @Test + public void testRefundQuery() throws Exception { + WxPayRefundQueryResult result; + + result = this.payService.refundQuery("1", "", "", ""); + log.info(result.toString()); + + result = this.payService.refundQuery("", "2", "", ""); + log.info(result.toString()); + + result = this.payService.refundQuery("", "", "3", ""); + log.info(result.toString()); + + result = this.payService.refundQuery("", "", "", "4"); + log.info(result.toString()); + + //测试四个参数都填的情况,应该报异常的 + result = this.payService.refundQuery("1", "2", "3", "4"); + log.info(result.toString()); + } + + @Test + public void testRefundQueryV2() throws WxPayException { + this.payService.refundQueryV2(WxPayRefundQueryRequest.newBuilder().outRefundNo("1").build()); + } + + /** + * Test parse refund notify result. + * + * @throws Exception the exception + */ + @Test + public void testParseRefundNotifyResult() throws Exception { + // 请参考com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResultTest里的单元测试 + } + + /** + * Test create scan pay qrcode mode 1. + * + * @throws Exception the exception + */ + @Test + public void testCreateScanPayQrcodeMode1() throws Exception { + String productId = "abc"; + byte[] bytes = this.payService.createScanPayQrcodeMode1(productId, null, null); + Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); + Files.write(qrcodeFilePath, bytes); + String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()); + log.info(qrcodeContent); + + assertTrue(qrcodeContent.startsWith("weixin://wxpay/bizpayurl?")); + assertTrue(qrcodeContent.contains("product_id=" + productId)); + } + + /** + * Test create scan pay qrcode mode 2. + * + * @throws Exception the exception + */ + @Test + public void testCreateScanPayQrcodeMode2() throws Exception { + String qrcodeContent = "abc"; + byte[] bytes = this.payService.createScanPayQrcodeMode2(qrcodeContent, null, null); + Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); + Files.write(qrcodeFilePath, bytes); + assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); + } + + /** + * Test micropay. + * + * @throws Exception the exception + */ + @Test + public void testMicropay() throws Exception { + WxPayMicropayResult result = this.payService.micropay( + WxPayMicropayRequest + .newBuilder() + .body("body") + .outTradeNo("aaaaa") + .totalFee(123) + .spbillCreateIp("127.0.0.1") + .authCode("aaa") + .build()); + log.info(result.toString()); + } + + /** + * Test get config. + * + * @throws Exception the exception + */ + @Test + public void testGetConfig() throws Exception { + // no need to test + } + + /** + * Test set config. + * + * @throws Exception the exception + */ + @Test + public void testSetConfig() throws Exception { + // no need to test + } + + /** + * Test reverse order. + * + * @throws Exception the exception + */ + @Test + public void testReverseOrder() throws Exception { + WxPayOrderReverseResult result = this.payService.reverseOrder( + WxPayOrderReverseRequest.newBuilder() + .outTradeNo("1111") + .build()); + assertNotNull(result); + log.info(result.toString()); + } + + /** + * Test shorturl. + * + * @throws Exception the exception + */ + @Test + public void testShorturl() throws Exception { + String longUrl = "weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX"; + + String result = this.payService.shorturl(new WxPayShorturlRequest(longUrl)); + assertNotNull(result); + log.info(result); + + result = this.payService.shorturl(longUrl); + assertNotNull(result); + log.info(result); + } + + /** + * Test authcode 2 openid. + * + * @throws Exception the exception + */ + @Test + public void testAuthcode2Openid() throws Exception { + String authCode = "11111"; + + String result = this.payService.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); + assertNotNull(result); + log.info(result); + + result = this.payService.authcode2Openid(authCode); + assertNotNull(result); + log.info(result); + } + + /** + * Test get sandbox sign key. + * + * @throws Exception the exception + */ + @Test + public void testGetSandboxSignKey() throws Exception { + final String signKey = this.payService.getSandboxSignKey(); + assertNotNull(signKey); + log.info(signKey); + } + + /** + * Test send coupon. + * + * @throws Exception the exception + */ + @Test + public void testSendCoupon() throws Exception { + WxPayCouponSendResult result = this.payService.sendCoupon(WxPayCouponSendRequest.newBuilder() + .couponStockId("123") + .openid("122") + .partnerTradeNo("1212") + .openidCount(1) + .build()); + log.info(result.toString()); + } + + /** + * Test query coupon stock. + * + * @throws Exception the exception + */ + @Test + public void testQueryCouponStock() throws Exception { + WxPayCouponStockQueryResult result = this.payService.queryCouponStock( + WxPayCouponStockQueryRequest.newBuilder() + .couponStockId("123") + .build()); + log.info(result.toString()); + } + + /** + * Test query coupon info. + * + * @throws Exception the exception + */ + @Test + public void testQueryCouponInfo() throws Exception { + WxPayCouponInfoQueryResult result = this.payService.queryCouponInfo( + WxPayCouponInfoQueryRequest.newBuilder() + .openid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4") + .couponId("11") + .stockId("1121") + .build()); + log.info(result.toString()); + } + + /** + * 只支持拉取90天内的评论数据 + * + * @throws Exception the exception + */ + @Test + public void testQueryComment() throws Exception { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, -1); + Date endDate = calendar.getTime(); + calendar.add(Calendar.DAY_OF_MONTH, -88); + Date beginDate = calendar.getTime(); + String result = this.payService.queryComment(beginDate, endDate, 0, 1); + log.info(result); + } + + /** + * Test parse order notify result. + * + * @throws Exception the exception + * @see WxPayOrderNotifyResultTest#testFromXML() WxPayOrderNotifyResultTest#testFromXML() + */ + @Test + public void testParseOrderNotifyResult() throws Exception { + // 请参考com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResultTest 里的单元测试 + + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " \n" + + " \n" + + " 2\n" + + " \n" + + " 10000\n" + + " 100\n" + + " \n" + + " 10001\n" + + " 200\n" + + ""; + + XmlConfig.fastMode = true; + WxPayOrderNotifyResult result; + try { + result = BaseWxPayResult.fromXML(xmlString, WxPayOrderNotifyResult.class); + System.out.println(result); + } finally { + XmlConfig.fastMode = false; + } + + result = this.payService.parseOrderNotifyResult(xmlString); + System.out.println(result); + + } + + /** + * Test get wx api data. + * + * @throws Exception the exception + */ + @Test + public void testGetWxApiData() throws Exception { + //see test in testUnifiedOrder() + } + + @Test + public void testDownloadRawBill() { + } + + @Test + public void testTestDownloadRawBill() { + } + + @Test + public void testGetWxPayFaceAuthInfo() throws WxPayException { + XmlConfig.fastMode = true; + final WxPayFaceAuthInfoRequest request = new WxPayFaceAuthInfoRequest() + .setStoreId("1").setRawdata("111").setNow("111").setVersion("111").setStoreName("2222").setDeviceId("111"); + request.setSignType("MD5"); + this.payService.getWxPayFaceAuthInfo(request); + } + + @Test + public void testFacepay() throws WxPayException { + final WxPayFacepayResult result = this.payService.facepay(WxPayFacepayRequest.newBuilder().build()); + } + + @Test + public void testGetEntPayService() { + // no need to test + } + + @Test + public void testGetProfitSharingService() { + // no need to test + } + + @Test + public void testGetRedpackService() { + // no need to test + } + + @Test + public void testSetEntPayService() { + // no need to test + } + + @Test + public void testGetPayBaseUrl() { + // no need to test + } + + @Test + public void testParseScanPayNotifyResult() { + } + + @Test + public void testSendMiniProgramRedpack() { + } + + @Test + public void testSendRedpack() { + } + + @Test + public void testQueryRedpack() { + } + + @Test + public void testTestQueryRedpack() { + } + + @Test + public void testGetPayScoreService() { + // no need to test + } + + @Test + public void testQueryExchangeRate() throws WxPayException { + final WxPayQueryExchangeRateResult result = this.payService.queryExchangeRate("USD", "20200425"); + assertThat(result).isNotNull(); + System.out.println(result); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImplTest.java new file mode 100644 index 0000000000..142bbbc734 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImplTest.java @@ -0,0 +1,151 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.entpay.*; +import com.github.binarywang.wxpay.constant.WxPayConstants.CheckNameOption; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.concurrent.TimeUnit; + +/** + *
    + *  企业付款测试类.
    + *  Created by BinaryWang on 2017/12/19.
    + * 
    + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class EntPayServiceImplTest { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Inject + private WxPayService payService; + + /** + * Test ent pay. + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testEntPay() throws WxPayException { + EntPayRequest request = EntPayRequest.newBuilder() + .partnerTradeNo("Eb6Aep7uVTdbkJqrP4") + .openid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP5") + .amount(100) + .spbillCreateIp("10.10.10.10") + .checkName(CheckNameOption.NO_CHECK) + .description("描述信息") + .build(); + + this.logger.info(this.payService.getEntPayService().entPay(request).toString()); + } + + /** + * Test query ent pay. + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testQueryEntPay() throws WxPayException { + this.logger.info(this.payService.getEntPayService().queryEntPay("11212121").toString()); + } + + /** + * Test get public key. + * + * @throws Exception the exception + */ + @Test + public void testGetPublicKey() throws Exception { + this.logger.info(this.payService.getEntPayService().getPublicKey()); + } + + /** + * Test pay bank. + * + * @throws Exception the exception + */ + @Test + public void testPayBank() throws Exception { + EntPayBankResult result = this.payService.getEntPayService().payBank(EntPayBankRequest.builder() + .bankCode("aa") + .amount(1) + .encBankNo("1") + .encTrueName("2") + .partnerTradeNo("3") + .description("11") + .build()); + this.logger.info(result.toString()); + } + + /** + * Test query pay bank. + * + * @throws Exception the exception + */ + @Test + public void testQueryPayBank() throws Exception { + this.logger.info(this.payService.getEntPayService().queryPayBank("123").toString()); + } + + + + /** + * 发送企业红包 + * @throws Exception the exception + */ + @Test + public void testSendEnterpriseRedpack() { + EntPayRedpackRequest request = new EntPayRedpackRequest(); + request.setMchId("1"); + //商户单号 + request.setMchBillNo(request.getMchId()+"20191202"+"1"); + //企业微信corpid即为此appId + request.setWxAppId("1"); +// request.setSenderName("1"); +// request.setSenderHeaderMediaId("2"); + request.setAgentId("1"); + request.setReOpenid("1"); + //目前企业微信api红包最低1块钱 + request.setTotalAmount(1000); + request.setWishing("1"); + request.setActName("1"); + request.setRemark("1"); + + EntPayRedpackResult redpackResult = null; + try { + redpackResult = this.payService.getEntPayService().sendEnterpriseRedpack(request); + } catch (WxPayException e) { + } + this.logger.info(redpackResult.toString()); + } + + /** + * 查询企业红包 + * @throws Exception + */ + @Test + public void testQueryEnterpriseRedpack() throws Exception { + while (true) { + EntPayRedpackQueryRequest request = new EntPayRedpackQueryRequest(); + request.setAppid("1"); + request.setMchId("1"); + request.setMchBillNo("1"); + + try { + EntPayRedpackQueryResult result = this.payService.getEntPayService().queryEnterpriseRedpack(request); + this.logger.info(result.toString()); + } catch (Exception e) { + } + TimeUnit.SECONDS.sleep(3); + } + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MerchantMediaServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MerchantMediaServiceImplTest.java new file mode 100644 index 0000000000..c8dd069b44 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MerchantMediaServiceImplTest.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.media.ImageUploadResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.MerchantMediaService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; + +/** + *
    + *  媒体文件上传测试类
    + * 
    + * + * @author zhouyongshen + */ +@Slf4j +@Test +@Guice(modules = ApiTestModule.class) +public class MerchantMediaServiceImplTest { + + @Inject + private WxPayService wxPayService; + + @Test + public void testImageUploadV3() throws WxPayException, IOException { + + MerchantMediaService merchantMediaService=new MerchantMediaServiceImpl(wxPayService); + + String filePath="你的图片文件的路径地址"; +// String filePath="WxJava/images/banners/wiki.jpg"; + + File file=new File(filePath); + + ImageUploadResult imageUploadResult = merchantMediaService.imageUploadV3(file); + String mediaId = imageUploadResult.getMediaId(); + + log.info("mediaId1:[{}]",mediaId); + + File file2=new File(filePath); + + ImageUploadResult imageUploadResult2 = merchantMediaService.imageUploadV3(file2); + String mediaId2 = imageUploadResult2.getMediaId(); + + log.info("mediaId2:[{}]",mediaId2); + + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImplTest.java new file mode 100644 index 0000000000..425cf99c6c --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImplTest.java @@ -0,0 +1,116 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.payscore.WxPayScoreRequest; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.net.URISyntaxException; + +/** + * 测试代码,待补充完善. + * + * @author Binary Wang + * @date 2020-05-19 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class PayScoreServiceImplTest { + @Inject + private WxPayService payService; + + @Test + + public void testCreateServiceOrder() throws WxPayException { + //测试数据 +/* { + "out_order_no":"QLS202005201058000201", + "appid":"", + "service_id":"", + "service_introduction":"租借服务", + "time_range":{ + "start_time":"OnAccept", + "end_time":"20200520225840" + }, + "location":{ + "start_location":"山", + "end_location":"山" + }, + "risk_fund":{ + "name":"DEPOSIT", + "amount":200, + "description":"丢失偿还费用2元/台" + }, + "attach":"", + "notify_url":"/pay/notify/payScore", + "openid":"", + "need_user_confirm":true, + "profit_sharing":false, + "post_payments":[ + { + "name":"租借服务", + "amount":100, + "description":"服务费:1元/台", + "count":1 + } + ], + "total_amount":0 + }*/ + + this.payService.getPayScoreService().createServiceOrder(WxPayScoreRequest.builder().build()); + } + + @Test + public void testQueryServiceOrder() throws WxPayException { + //两个参数选填一个 + this.payService.getPayScoreService().queryServiceOrder("11", ""); + } + + @Test + public void testCancelServiceOrder() throws WxPayException { + this.payService.getPayScoreService().cancelServiceOrder("11", "测试取消"); + } + + @Test + public void testModifyServiceOrder() { + } + + @Test + public void testCompleteServiceOrder() throws WxPayException { +/* { + "appid":"", + "service_id":"", + "time_range":{ + "end_time":"20200520111702" + }, + "need_user_confirm":false, + "profit_sharing":false, + "post_payments":[ + { + "name":"租借服务", + "amount":100, + "description":"服务费:1.0000元/台", + "count":1 + } + ], + "total_amount":100 + } +*/ + this.payService.getPayScoreService().completeServiceOrder(WxPayScoreRequest.builder().build()); + } + + @Test + public void testPayServiceOrder() { + } + + @Test + public void testSyncServiceOrder() { + } + + @Test + public void testDecryptNotifyData() { + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java new file mode 100644 index 0000000000..13f94d4015 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java @@ -0,0 +1,127 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.profitsharing.*; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +@Test +@Guice(modules = ApiTestModule.class) +public class ProfitSharingServiceImplTest { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Inject + private WxPayService payService; + + @Test + public void testProfitSharing() throws WxPayException { + ReceiverList instance = ReceiverList.getInstance(); + instance.add(new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID, + "oyOUE5ql4TtzrBg5cVOwxq6tbjOs", + 20, + "***")); + //30000002922019102310811092093 + ProfitSharingRequest request = ProfitSharingRequest + .newBuilder() + .outOrderNo("20191023112023031060677") + .transactionId("4200000431201910234736634272") + .receivers(instance.toJSONString()) + .build(); + this.logger.info(this.payService.getProfitSharingService().profitSharing(request).toString()); + } + + @Test + public void testMultiProfitSharing() throws WxPayException { + ReceiverList instance = ReceiverList.getInstance(); + instance.add(new Receiver(WxPayConstants.ReceiverType.MERCHANT_ID, + "86693852", + 1, + "***")); + ProfitSharingRequest request = ProfitSharingRequest + .newBuilder() + .outOrderNo("20191023154723316420060") + .transactionId("4200000448201910238249687345")//order_id=30000102922019102310821824010 + .receivers(instance.toJSONString()) + .build(); + this.logger.info(this.payService.getProfitSharingService().multiProfitSharing(request).toString()); + } + + @Test + public void testProfitSharingFinish() throws WxPayException { + ProfitSharingFinishRequest request = ProfitSharingFinishRequest + .newBuilder() + .outOrderNo("20191023103251431856285") + .transactionId("4200000441201910238267278073") + .description("分账完成") + .build(); + this.logger.info(this.payService.getProfitSharingService().profitSharingFinish(request).toString()); + } + + @Test + public void testAddReceiver() throws WxPayException { + Receiver receiver = new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID, + "oyOUE5ql4TtzrBg5cVOwxq6tbjOs", + "***", + "STORE_OWNER", + null); + ProfitSharingReceiverRequest request = ProfitSharingReceiverRequest + .newBuilder() + .receiver(receiver.toJSONString()) + .build(); + this.logger.info(this.payService.getProfitSharingService().addReceiver(request).toString()); + } + + @Test + public void testRemoveReceiver() throws WxPayException { + Receiver receiver = new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID, + "oyOUE5ql4TtzrBg5cVOwxq6tbjOs"); + ProfitSharingReceiverRequest request = ProfitSharingReceiverRequest + .newBuilder() + .receiver(receiver.toJSONString()) + .build(); + this.logger.info(this.payService.getProfitSharingService().removeReceiver(request).toString()); + } + + @Test + public void testProfitSharingQuery() throws WxPayException { + ProfitSharingQueryRequest request = ProfitSharingQueryRequest + .newBuilder() + .outOrderNo("20191023112023031060677") + .transactionId("4200000431201910234736634272") + .build(); + ProfitSharingQueryResult result = this.payService.getProfitSharingService().profitSharingQuery(request); + this.logger.info(result.formatReceivers().toString()); + this.logger.info(result.toString()); + } + + @Test + public void testProfitSharingReturn() throws WxPayException { + ProfitSharingReturnRequest request = ProfitSharingReturnRequest + .newBuilder() + .outOrderNo("20191023154723316420060") + .outReturnNo("R2019102315") + .returnAccountType("MERCHANT_ID") + .returnAccount("86693852") + .returnAmount(2) + .description("用户退款") + .build(); + this.logger.info(this.payService.getProfitSharingService().profitSharingReturn(request).toString()); + } + + @Test + public void testProfitSharingReturnQuery() throws WxPayException { + ProfitSharingReturnQueryRequest request = ProfitSharingReturnQueryRequest + .newBuilder() + .outOrderNo("20191023154723316420060") + .outReturnNo("R2019102315") + .build(); + this.logger.info(this.payService.getProfitSharingService().profitSharingReturnQuery(request).toString()); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImplTest.java new file mode 100644 index 0000000000..161135b51f --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImplTest.java @@ -0,0 +1,63 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.request.WxPaySendMiniProgramRedpackRequest; +import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest; +import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendMiniProgramRedpackResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.github.binarywang.wxpay.testbase.XmlWxPayConfig; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2019-12-26 + */ +@Slf4j +@Test +@Guice(modules = ApiTestModule.class) +public class RedpackServiceImplTest { + @Inject + private WxPayService payService; + + @Test + public void testSendRedpack() throws Exception { + WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); + request.setActName("abc"); + request.setClientIp("aaa"); + request.setMchBillNo("aaaa"); + request.setWishing("what"); + request.setSendName("111"); + request.setTotalAmount(1); + request.setTotalNum(1); + request.setReOpenid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()); + WxPaySendRedpackResult redpackResult = this.payService.getRedpackService().sendRedpack(request); + log.info(redpackResult.toString()); + } + + @Test + public void testQueryRedpack() throws Exception { + WxPayRedpackQueryResult redpackResult = this.payService.getRedpackService().queryRedpack("aaaa"); + log.info(redpackResult.toString()); + } + + @Test + public void testSendMiniProgramRedpack() throws WxPayException { + final WxPaySendMiniProgramRedpackResult result = this.payService.getRedpackService() + .sendMiniProgramRedpack(new WxPaySendMiniProgramRedpackRequest() + .setReOpenid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4") + .setWishing("haha") + .setMchBillNo("123") + .setActName("11") + .setSendName("111") + .setTotalAmount(1)); + System.out.println(result); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java deleted file mode 100644 index 93b0b8b552..0000000000 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java +++ /dev/null @@ -1,267 +0,0 @@ -package com.github.binarywang.wxpay.service.impl; - -import com.github.binarywang.utils.qrcode.QrcodeUtils; -import com.github.binarywang.wxpay.bean.request.*; -import com.github.binarywang.wxpay.bean.result.*; -import com.github.binarywang.wxpay.service.WxPayService; -import com.github.binarywang.wxpay.testbase.ApiTestModule; -import com.github.binarywang.wxpay.testbase.XmlWxPayConfig; -import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.*; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; - -import static org.testng.Assert.*; - -/** - * 测试支付相关接口 - * Created by Binary Wang on 2016/7/28. - * - * @author binarywang (https://github.com/binarywang) - */ -@Test -@Guice(modules = ApiTestModule.class) -public class WxPayServiceImplTest { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - @Inject - private WxPayService payService; - - @Test - public void testGetPayInfo() throws Exception { - Map payInfo = this.payService.getPayInfo(WxPayUnifiedOrderRequest.builder() - .body("我去") - .totalFee(1) - .spbillCreateIp("111111") - .notifyURL("111111") - .tradeType("JSAPI") - .outTradeNo("111111") - .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) - .build()); - this.logger.info(payInfo.toString()); - } - - @Test - public void testDownloadBill() throws Exception { - File file = this.payService.downloadBill("20170101", "ALL", "GZIP", "1111111"); - assertNotNull(file); - //必填字段为空时,抛出异常 - this.payService.downloadBill("", "", "", null); - } - - @Test - public void testReport() throws Exception { - WxPayReportRequest request = new WxPayReportRequest(); - request.setInterfaceUrl("hahahah"); - request.setSignType("HMAC-SHA256");//貌似接口未校验此字段 - request.setExecuteTime(1000); - request.setReturnCode("aaa"); - request.setResultCode("aaa"); - request.setUserIp("8.8.8"); - this.payService.report(request); - } - - /** - * Test method for {@link WxPayService#refund(WxPayRefundRequest)} . - */ - @Test - public void testRefund() throws Exception { - WxPayRefundResult result = this.payService.refund( - WxPayRefundRequest.newBuilder() - .outRefundNo("aaa") - .outTradeNo("1111") - .totalFee(1222) - .refundFee(111) - .build()); - this.logger.info(result.toString()); - } - - /** - * Test method for {@link WxPayService#refundQuery(java.lang.String, java.lang.String, java.lang.String, java.lang.String)} . - */ - @Test - public void testRefundQuery() throws Exception { - WxPayRefundQueryResult result; - - result = this.payService.refundQuery("1", "", "", ""); - this.logger.info(result.toString()); - - result = this.payService.refundQuery("", "2", "", ""); - this.logger.info(result.toString()); - - result = this.payService.refundQuery("", "", "3", ""); - this.logger.info(result.toString()); - - result = this.payService.refundQuery("", "", "", "4"); - this.logger.info(result.toString()); - - //测试四个参数都填的情况,应该报异常的 - result = this.payService.refundQuery("1", "2", "3", "4"); - this.logger.info(result.toString()); - } - - /** - * Test method for {@link WxPayService#sendRedpack(WxPaySendRedpackRequest)} . - */ - @Test - public void testSendRedpack() throws Exception { - WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); - request.setActName("abc"); - request.setClientIp("aaa"); - request.setMchBillNo("aaaa"); - request.setReOpenid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()); - WxPaySendRedpackResult redpackResult = this.payService.sendRedpack(request); - this.logger.info(redpackResult.toString()); - } - - /** - * Test method for {@link WxPayService#queryRedpack(java.lang.String)}. - */ - @Test - public void testQueryRedpack() throws Exception { - WxPayRedpackQueryResult redpackResult = this.payService.queryRedpack("aaaa"); - this.logger.info(redpackResult.toString()); - } - - /** - * Test method for {@link WxPayService#unifiedOrder(WxPayUnifiedOrderRequest)}. - */ - @Test - public void testUnifiedOrder() throws WxErrorException { - WxPayUnifiedOrderResult result = this.payService - .unifiedOrder(WxPayUnifiedOrderRequest.builder() - .body("我去") - .totalFee(1) - .spbillCreateIp("111111") - .notifyURL("111111") - .tradeType("JSAPI") - .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) - .outTradeNo("111111") - .build()); - this.logger.info(result.toString()); - } - - /** - * Test method for {@link WxPayService#queryOrder(java.lang.String, java.lang.String)} . - */ - @Test - public void testQueryOrder() throws WxErrorException { - this.logger.info(this.payService.queryOrder("11212121", null).toString()); - this.logger.info(this.payService.queryOrder(null, "11111").toString()); - } - - /** - * Test method for {@link WxPayService#closeOrder(java.lang.String)} . - */ - @Test - public void testCloseOrder() throws WxErrorException { - this.logger.info(this.payService.closeOrder("11212121").toString()); - } - - /** - * Test method for {@link WxPayService#entPay(WxEntPayRequest)}. - */ - @Test - public void testEntPay() throws WxErrorException { - WxEntPayRequest request = new WxEntPayRequest(); - this.logger.info(this.payService.entPay(request).toString()); - } - - /** - * Test method for {@link WxPayService#queryEntPay(java.lang.String)}. - */ - @Test - public void testQueryEntPay() throws WxErrorException { - this.logger.info(this.payService.queryEntPay("11212121").toString()); - } - - @Test - public void testCreateScanPayQrcodeMode1() throws Exception { - String productId = "abc"; - byte[] bytes = this.payService.createScanPayQrcodeMode1(productId, null, null); - Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); - Files.write(qrcodeFilePath, bytes); - String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()); - this.logger.info(qrcodeContent); - - assertTrue(qrcodeContent.startsWith("weixin://wxpay/bizpayurl?")); - assertTrue(qrcodeContent.contains("product_id=" + productId)); - } - - @Test - public void testCreateScanPayQrcodeMode2() throws Exception { - String qrcodeContent = "abc"; - byte[] bytes = this.payService.createScanPayQrcodeMode2(qrcodeContent, null, null); - Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); - Files.write(qrcodeFilePath, bytes); - assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); - } - - @Test - public void testGetOrderNotifyResult() throws Exception { - } - - @Test - public void testMicropay() throws Exception { - WxPayMicropayResult result = this.payService.micropay(WxPayMicropayRequest.newBuilder() - .body("body") - .outTradeNo("aaaaa") - .totalFee(123) - .spbillCreateIp("127.0.0.1") - .authCode("aaa") - .build()); - this.logger.info(result.toString()); - } - - @Test - public void testGetConfig() throws Exception { - // no need to test - } - - @Test - public void testSetConfig() throws Exception { - // no need to test - } - - @Test - public void testReverseOrder() throws Exception { - WxPayOrderReverseResult result = this.payService.reverseOrder(WxPayOrderReverseRequest.newBuilder() - .outTradeNo("1111") - .build()); - assertNotNull(result); - this.logger.info(result.toString()); - } - - @Test - public void testShorturl() throws Exception { - String longUrl = "weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX"; - - String result = this.payService.shorturl(new WxPayShorturlRequest(longUrl)); - assertNotNull(result); - this.logger.info(result); - - result = this.payService.shorturl(longUrl); - assertNotNull(result); - this.logger.info(result); - } - - @Test - public void testAuthcode2Openid() throws Exception { - String authCode = "11111"; - - String result = this.payService.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); - assertNotNull(result); - this.logger.info(result); - - result = this.payService.authcode2Openid(authCode); - assertNotNull(result); - this.logger.info(result); - } - -} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/ApiTestModule.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/ApiTestModule.java index d5a028d1fb..b29b1af16c 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/ApiTestModule.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/ApiTestModule.java @@ -7,24 +7,37 @@ import com.google.inject.Module; import com.thoughtworks.xstream.XStream; import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; +/** + * The type Api test module. + */ public class ApiTestModule implements Module { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + private static final String TEST_CONFIG_XML = "test-config.xml"; @Override public void configure(Binder binder) { - try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { - XmlWxPayConfig config = this.fromXml(XmlWxPayConfig.class, is1); + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(TEST_CONFIG_XML)) { + if (inputStream == null) { + throw new RuntimeException("测试配置文件【" + TEST_CONFIG_XML + "】未找到,请参照test-config-sample.xml文件生成"); + } + + XmlWxPayConfig config = this.fromXml(XmlWxPayConfig.class, inputStream); + config.setIfSaveApiData(true); WxPayService wxService = new WxPayServiceImpl(); wxService.setConfig(config); binder.bind(WxPayService.class).toInstance(wxService); binder.bind(WxPayConfig.class).toInstance(config); } catch (IOException e) { - e.printStackTrace(); + this.log.error(e.getMessage(), e); } + } @SuppressWarnings("unchecked") diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java index a10f36f3bc..bdb394cd29 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java @@ -2,30 +2,36 @@ import com.github.binarywang.wxpay.config.WxPayConfig; import com.thoughtworks.xstream.annotations.XStreamAlias; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.http.ssl.SSLContexts; - -import javax.net.ssl.SSLContext; -import java.io.File; -import java.io.FileInputStream; -import java.security.KeyStore; +/** + * The type Xml wx pay config. + */ @XStreamAlias("xml") public class XmlWxPayConfig extends WxPayConfig { private String openid; + /** + * Gets openid. + * + * @return the openid + */ public String getOpenid() { return openid; } + /** + * Sets openid. + * + * @param openid the openid + */ public void setOpenid(String openid) { this.openid = openid; } @Override - public boolean useSandboxForWxPay() { + public boolean isUseSandboxEnv() { //沙箱环境不成熟,有问题无法使用,暂时屏蔽掉 - // return true; + //return true; return false; } } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/util/SignUtilsTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/util/SignUtilsTest.java new file mode 100644 index 0000000000..9247d852c3 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/util/SignUtilsTest.java @@ -0,0 +1,56 @@ +package com.github.binarywang.wxpay.util; + +import org.testng.annotations.*; + +import com.google.common.base.Splitter; + +import static com.github.binarywang.wxpay.constant.WxPayConstants.SignType.HMAC_SHA256; +import static org.testng.Assert.*; + +/** + *
    + * 测试中使用的测试数据参考的是官方文档,地址:
    + * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
    + *  Created by BinaryWang on 2017/9/2.
    + * 
    + * + * @author Binary Wang + */ +public class SignUtilsTest { + /** + * Test create sign. + * + * @throws Exception the exception + */ + @Test + public void testCreateSign() throws Exception { + String signKey = "192006250b4c09247ec02edce69f6a2d"; + String message = "appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA"; + assertEquals(SignUtils.createSign((Splitter.on("&").withKeyValueSeparator("=").split(message)), null, signKey, null), + "9A0A8659F005D6984697E2CA0A9CF3B7"); + } + + /** + * Test create sign hmacsha 256. + * + * @throws Exception the exception + */ + @Test + public void testCreateSign_HMACSHA256() throws Exception { + String signKey = "192006250b4c09247ec02edce69f6a2d"; + final String message = "appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA"; + String sign = SignUtils.createSign(Splitter.on("&").withKeyValueSeparator("=").split(message), + HMAC_SHA256, signKey, null); + assertEquals(sign, "6A9AE1657590FD6257D693A078E1C3E4BB6BA4DC30B23E0EE2496E54170DACD6"); + } + + /** + * Test check sign. + * + * @throws Exception the exception + */ + @Test + public void testCheckSign() throws Exception { + } + +} diff --git a/weixin-java-pay/src/test/resources/.gitignore b/weixin-java-pay/src/test/resources/.gitignore new file mode 100644 index 0000000000..edd25bc776 --- /dev/null +++ b/weixin-java-pay/src/test/resources/.gitignore @@ -0,0 +1 @@ +*.p12 diff --git a/weixin-java-pay/src/test/resources/logback-test.xml b/weixin-java-pay/src/test/resources/logback-test.xml index 75de28c04c..35deb2ed28 100644 --- a/weixin-java-pay/src/test/resources/logback-test.xml +++ b/weixin-java-pay/src/test/resources/logback-test.xml @@ -12,5 +12,4 @@ - diff --git a/weixin-java-pay/src/test/resources/test-config.sample.xml b/weixin-java-pay/src/test/resources/test-config.sample.xml index 1e64cbe40c..25ec4be056 100644 --- a/weixin-java-pay/src/test/resources/test-config.sample.xml +++ b/weixin-java-pay/src/test/resources/test-config.sample.xml @@ -10,4 +10,12 @@ --> 商户平台的证书文件地址 某个openId + +