103 Commits

Author SHA1 Message Date
Binary Wang
cfe1f6dc95 🔖 发布 4.7.8.B 测试版本
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-09-24 21:55:26 +08:00
Copilot
c0edd9fd16 🎨 #3698 【微信支付】 修复字符编码导致的解析报文异常问题 2025-09-24 21:43:30 +08:00
Copilot
1788d9071e 🎨 #3715 企业微信回调消息中的MsgId改为字符串类型 2025-09-24 21:40:27 +08:00
Copilot
e6a844ab70 🐛 #3631 修复tryLockunlock中针对Key的序列化方式不一致导致无法unlock的问题
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-09-23 10:53:09 +08:00
Copilot
6fc17201e1 🎨 #3697 【企业微信】修复自定义工作台enable_webview_click等参数设置无效的问题 2025-09-23 10:40:53 +08:00
Binary Wang
ad19f653c7 🎨 优化代码,修复过期方法引用 2025-09-23 10:31:35 +08:00
wangshuai
7018dceb1c :art:【企业微信】企微客户群回调事件添加LastMemVer、CurMemVer字段
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-09-22 16:59:32 +00:00
ihao
399f3648c0 :art:【微信支付】公众号签约缺失执行签名方法,增加非必填参数判断;微信信h5纯签约接口,增加非必填字段判断 2025-09-23 00:58:11 +08:00
graalvm-samples
88bdd4a36e 🆕 #3703 【企业微信】第三方应用基础接口实现 2025-09-23 00:25:39 +08:00
imyzt
213cf6fd67 🐛 #3631 修复tryLockunlock中针对Key的序列化方式不一致导致无法unlock的问题 2025-09-23 00:24:02 +08:00
hb0730
c8d84da2ab 🎨 #3702 【小程序】多小程序spring starter增加Redis Template支持 2025-09-15 09:45:15 +08:00
dengfengfeng
118839a49d 🐛 #3692【小程序】【公众号】修复图片智能裁剪接口请求类型错误的问题
Co-authored-by: dengfeng <dengfeng@huaxincem.com>
2025-09-04 09:19:20 +08:00
水依寒
8447caa75f 🎨 #3694 统一各模块微信接口请求的基础地址字段命名为apiHostUrl,并完善Spring Boot starter配置支持 2025-09-04 09:17:36 +08:00
LiYaoheng
1eefd11e03 🎨 修复文档错误,关于channel-springboot-starter的依赖 2025-09-04 09:13:27 +08:00
tryking123
781798345e 🆕 #3681 【小程序】增加企微客服增删查3个接口 2025-08-31 21:44:24 +08:00
Copilot
0424d75882 🎨 #3323 【企业微信】获取企业所有打卡规则接口增加官方新添加的late_rule等字段 2025-08-08 18:21:59 +08:00
Copilot
e61ad44d27 📝 Add comprehensive GitHub Copilot instructions for WxJava SDK development 2025-08-08 18:18:39 +08:00
Copilot
dc46d9de75 🎨 #3640 【微信支付】使用HttpClient发送http请求时调整为使用连接池的形式 2025-08-08 17:47:20 +08:00
Copilot
bc6fb7b58e 🎨 #3656 【微信支付】 修复平台收付通 - 合单支付 - 关闭订单接口,增加缺少的两个参数
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-08-08 17:40:28 +08:00
Copilot
983e4f1e66 🎨 #3659 【微信支付】新增BaseWxPayV3Result基类,提供rawJsonString字段保存原始API响应 2025-08-08 17:25:40 +08:00
痴货
7ffc1b3719 :new:【开放平台】添加获取换绑管理员 URL 的接口 2025-08-08 06:14:25 +00:00
Binary Wang
8bb4a21598 🎨 #3662 【企业微信】修复获取审批模板详情接口异常的问题:Expected BEGIN_OBJECT but was BEGIN_ARRAY
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-07-30 23:29:59 +08:00
Copilot
a881de59a5 🎨 #3567 【企业微信】获取企业所有打卡规则接口调整 2025-07-30 23:10:30 +08:00
Copilot
14f8c8ebc2 🎨 #3655 通过支持现代 TLS 版本修复 SSL 握手失败问题
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-07-25 11:41:06 +08:00
Winnie
875c35e745 🎨 #3660【企业微信】获取会议详情接口响应类补充官方新加的若干字段 2025-07-25 11:35:46 +08:00
Copilot
262a02b606 📝 为微信支付新增商户转账接口添加全面的文档和示例 2025-07-24 16:43:23 +08:00
Copilot
3a1d8075fc 🎨 Fix StackOverflowError in WxCpRedissonConfigImpl and WxCpRedisTemplateConfigImpl toString() methods 2025-07-24 16:39:56 +08:00
Binary Wang
2e952c76c8 🎨 修复错误的变量命名
Some checks are pending
Publish to Maven Central / build-and-publish (push) Waiting to run
2025-07-24 15:49:15 +08:00
Farahani
e5ef34553d 🆕 #3645【小程序】增加虚拟支付的更多接口 2025-07-24 15:29:25 +08:00
匠心
4cdd056a3e 🎨 修复代码缩进格式 2025-07-24 15:18:42 +08:00
痴货
57a6ef1179 🆕 【小程序】增加小程序基本信息通知事件相关的属性
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-07-16 15:06:45 +00:00
痴货
4754a562ca 🎨 调整部分starter模块接口连接的 soTimeout 默认值为 5000毫秒 2025-07-16 15:05:00 +00:00
Binary Wang
62f89987c0 🔖 发布 4.7.7.B 测试版本 2025-07-16 20:05:09 +08:00
痴货
990dbba06b 🎨 【开放平台】重构小程序认证及备案相关接口 2025-07-16 11:47:11 +00:00
痴货
d85e04a72a 🎨 starter模块添加 HttpClient 超时配置 2025-07-16 11:46:38 +00:00
Binary Wang
aacfa947be 🎨 统一优化gson adapter类,抽取常量,优化重复代码
Some checks are pending
Publish to Maven Central / build-and-publish (push) Waiting to run
2025-07-16 13:50:55 +08:00
Ryn
3e4ee57e1c 🎨 #3639 【小程序】修复多账号starter不支持设置数据格式,导致消息解析出错的问题 2025-07-16 13:44:50 +08:00
benben
d4cf48d059 🎨 【公众号】删除已下线的新增删除永久图文素材的接口
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-07-12 08:59:11 +00:00
ihao
696ce8621e 🆕 #3636【微信支付】增加微信签约、解约结果通知解析的方法 2025-07-12 16:57:50 +08:00
dependabot[bot]
8c8139533d ⬆️ Bump org.apache.commons:commons-lang3 from 3.10 to 3.18.0 2025-07-12 16:49:40 +08:00
zhfkt
84c69d4900 🎨 Add null check for textJsonElement in WxCpUserGsonAdapter 2025-07-04 15:40:29 +08:00
xuxiancan
d52e225626 🎨 #3633【微信支付】创建支付分订单接口请求参数里增加设备信息字段 2025-07-04 15:39:15 +08:00
ihao
686dff6436 🎨 优化微信h5纯签约接口,增加非必填字段判断 2025-07-03 01:55:33 +00:00
Style-lo
92431aabd9 🎨 getConfigKey方法改成public的 2025-07-03 09:53:30 +08:00
Binary Wang
29bc950f92 🎨 为小程序短剧管理服务接口增加接口 2025-07-02 10:35:54 +08:00
Binary Wang
5c74c1d930 🎨 为微信小程序物流退货组件接口增加注释文档 2025-07-02 10:35:54 +08:00
Binary Wang
52d0b2fc1c 🎨 为小程序虚拟支付接口类增加注释文档 2025-07-02 10:35:54 +08:00
Binary Wang
5b816242a4 🎨 优化小程序数据分析接口类的注释文档 2025-07-02 10:35:54 +08:00
Binary Wang
a45f0ac048 🎨 优化小程序云开发接口类的注释文档 2025-07-02 10:35:54 +08:00
Binary Wang
4731479b93 🎨 优化小程序接口注释文档 2025-07-02 10:35:54 +08:00
benben
cc344f105e 🎨 #3624 【企业微信】企微微盘API相关接口去掉废弃的userId参数 2025-07-02 10:33:56 +08:00
Well
172b96444f 🎨 #3626 【微信支付】创建代金券批次接口新增字段,包括背景色、卡包跳转目标、小程序 appid 与 path等 2025-07-02 10:31:26 +08:00
jinceon
b3b8c273c7 🎨 【企业微信】增加“审批申请状态变化回调通知”事件类型 2025-06-28 23:07:36 +08:00
大粽子
b6d489e9c8 📝 修改赞助商信息 2025-06-28 23:07:36 +08:00
zhoushi1
72266c3aeb 🎨 #3627 【开放平台】小程序三方平台代上传代码提交额外信息对象新增requiredPrivateInfos信息
Co-authored-by: zhoushi1 <qiannanyou9@gmail.com>
2025-06-28 20:57:37 +08:00
altusea
ccbfa98864 🎨 添加 Apache HttpComponents Client 5.x 为可选的 http client
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-06-09 14:29:35 +08:00
buaazyl
bf35797b91 :art:【小程序】发订阅消息的“43101用户未订阅消息”异常日志调整为debug 2025-06-09 14:28:56 +08:00
buaazyl
41081561b5 :art:【小程序】不再打印发订阅消息的“43101用户未订阅消息”警告日志 2025-06-09 11:19:37 +08:00
Binary Wang
8878f06c1e 🎨 修复单元测试代码
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-06-06 17:07:52 +08:00
Zeyes Lee
22d288f6cd 🎨 #3609 【视频号】微信小店获取售后单接口返回新增换货相关的数据 2025-06-06 16:55:46 +08:00
Binary Wang
b0af2c704e 🔖 发布 4.7.6.B 测试版本
Some checks are pending
Publish to Maven Central / build-and-publish (push) Waiting to run
2025-06-05 23:47:01 +08:00
Binary Wang
320f05e6d6 📝 赞助商表格优化 2025-06-03 12:15:47 +08:00
Binary Wang
4d7fd68fe5 📝 调整两个logo的位置
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-06-03 12:06:48 +08:00
Molzx
388188b694 🎨 【微信支付】调整逻辑:当完全使用公钥模式时,也可从p12证书中读取证书私钥等 2025-06-03 11:53:45 +08:00
altusea
ecce9292b2 🎨 初步引入 Apache HttpClient 5.x 2025-06-03 11:52:39 +08:00
msgpo
96a5cc995b 🎨 #3603【企业微信】获取审批申请详情接口 增加说明文字控件的支持 2025-05-29 11:18:12 +08:00
Binary Wang
4d66dd09ca 🎨 修改查看最新版本的地址 2025-05-28 16:22:20 +08:00
阿杆
08e2107667 🎨 #3571【公众号】草稿箱相关接口支持图片文章类型 2025-05-28 15:59:08 +08:00
Binary Wang
f71ac2185c 🎨 workflow 里增加 concurrency 设置并发限制 2025-05-22 15:21:39 +08:00
Zeyes Lee
5bde717f8f 🎨 #3594【视频号】微信小店更新售后、订单详情等返回参数,并修改获取售后单列表和获取快递公司列表等接口的参数
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-05-22 15:07:25 +08:00
altusea
8bacc9425e 🎨 修复CloseableHttpClient相关的误用代码 2025-05-22 15:03:27 +08:00
mengyou
3e1a38a696 🎨 #3591【微信支付】修复只设置privateKeyString或者privateKeyContent时私钥初始化失败的问题
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-05-16 20:19:19 +08:00
altusea
fefe737803 🎨 #3588 【小程序】短剧媒资列表接口增加新字段media_name_fuzzy 2025-05-15 14:43:07 +08:00
SynchPj
d0b7a526f6 🎨 #3587【微信支付】支持完全公钥模式,新增fullPublicKeyModel字段来控制,默认关闭,关闭时走老逻辑,开启时,只加载公钥所需相关配置,避免下载平台证书使灰度切换无法达到100%覆盖 2025-05-14 16:27:29 +08:00
SynchPj
47051bdf31 🎨 #3586 【微信支付】修复资源上传接口使用POST请求Content-Type不正确导致签名错误的问题 2025-05-14 11:54:30 +08:00
Binary Wang
f5dd32de86 📝 优化badge展示 2025-05-14 10:44:59 +08:00
altusea
d776792bae 🎨 #3575 【视频号】微信小店api官方将要废除finder_id,以promoter_id代替
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
2025-05-13 22:57:07 +08:00
altusea
1a74e3e3a8 🎨 #3562 【企业微信】发送应用消息接口返回增加unlicenseduser字段 2025-05-13 22:54:28 +08:00
altusea
2762a98279 🎨 #3569 【视频号】微信小店-订单详情查询接口增加参数 2025-05-13 22:52:24 +08:00
altusea
04c162f03a 🎨 #3574 【微信支付】修复商家券available_day_time字段定义错误 2025-05-13 22:50:35 +08:00
Binary Wang
5c2a36c538 🎨 add serialVersionUID for some class 2025-05-13 22:48:25 +08:00
altusea
2c4b5ea128 🎨 #3525 【企业微信】第三方应用配置类提供setProviderSecret 方法 2025-05-13 22:41:15 +08:00
altusea
9a6db34b6a 🎨 #3577 【微信支付】微信分账回退请求接口新增字段:微信订单号transaction_id 2025-05-13 16:18:00 +08:00
Binary Wang
77b90626f3 ⬆️ Bump org.eclipse.jetty:jetty-server 2025-05-13 16:16:17 +08:00
altusea
063fbb7f19 🎨 升级部分依赖版本,优化代码,部分代码增加泛型参数 2025-05-13 15:56:14 +08:00
Binary Wang
a7b007f853 📝 修正版本说明文档 2025-05-13 13:08:24 +08:00
SynchPj
c91a5a8e22 🎨 【微信支付】修复连接池关闭异常问题 2025-05-13 12:49:46 +08:00
Binary Wang
a051587d3e 👷 自动构建迁移到maven central portal 2025-05-13 12:43:51 +08:00
Binary Wang
245cf72104 🎨 增加github action来自动发布版本到maven中央仓库 2025-05-12 12:35:06 +08:00
Binary Wang
fb57af7409 🔖 发布 4.7.5.B 测试版本 2025-05-08 23:04:32 +08:00
jimson
88a5bc4f43 🎨 企微客户群回调事件添加MemChangeList字段 2025-05-08 22:15:06 +08:00
Binary Wang
dc674ce024 🎨 优化部分javadoc 2025-04-29 12:12:34 +08:00
Binary Wang
854b50bd51 🎨 修复日志代码报错问题 2025-04-29 11:33:58 +08:00
imyzt
2d8d1df00e 🆕 #3320【企业微信】增加异步上传临时素材相关接口 2025-04-29 11:29:09 +08:00
Binary Wang
2279105fef :art: 优化微信支付请求代码,抽取合并重复代码 2025-04-26 11:18:56 +08:00
SynchPj
cbb3b24577 🎨 #3553【微信支付】v3请求统一加上Wechatpay-Serial请求头 2025-04-22 11:08:50 +08:00
giveup
ddfbee25e1 🐛 #3557 【企业微信】修复agentId数据类型不一致导致的WxCpTpMessageRouterRule.test()方法匹配失败的问题 2025-04-21 10:00:39 +08:00
友人A
6a4ed91ae2 🎨 #3558 【企业微信】修复会话内容存档接口获取解密的聊天数据时文件信息转换出错的问题 2025-04-21 09:58:51 +08:00
西东
59f5a99fda 🆕 添加wx-java-channel-solon-plugin README.md 2025-04-21 09:55:13 +08:00
Henry Heng
3718b499a0 🎨 #3554【企业微信】修复审批通知节点获取不到用户ID的问题 2025-04-16 10:43:55 +08:00
Henry Heng
bb76db052d 🎨 #3554【企业微信】修复审批通知节点获取不到用户ID的问题 2025-04-15 19:32:54 +08:00
HerveyHall
e16e0e9373 🎨 #3548【微信支付】修复公钥模式下V3接口未设置Wechatpay-Serial请求头导致的验签失败 2025-04-15 15:14:12 +08:00
Holy
89280abd00 🎨 #3547【微信支付】修复验证器未正确初始化导致的v3请求构造异常问题 2025-04-14 11:32:34 +08:00
629 changed files with 15588 additions and 3425 deletions

198
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,198 @@
# WxJava - WeChat Java SDK Development Instructions
WxJava is a comprehensive WeChat Java SDK supporting multiple WeChat platforms including Official Accounts (公众号), Mini Programs (小程序), WeChat Pay (微信支付), Enterprise WeChat (企业微信), Open Platform (开放平台), and Channel/Video (视频号). This is a Maven multi-module project with Spring Boot and Solon framework integrations.
**ALWAYS reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the information here.**
## Working Effectively
### Prerequisites and Environment Setup
- **Java Requirements**: JDK 8+ required (project uses Java 8 as minimum target)
- **Maven**: Maven 3.6+ recommended (Maven 3.9.11 validated)
- **IDE**: IntelliJ IDEA recommended (project optimized for IDEA)
### Bootstrap, Build, and Validate
Execute these commands in sequence after cloning:
```bash
# 1. Basic compilation (NEVER CANCEL - takes 4-5 minutes)
mvn clean compile -DskipTests=true --no-transfer-progress
# Timeout: Set 8+ minutes. Actual time: ~4 minutes
# 2. Full package build (NEVER CANCEL - takes 2-3 minutes)
mvn clean package -DskipTests=true --no-transfer-progress
# Timeout: Set 5+ minutes. Actual time: ~2 minutes
# 3. Code quality validation (NEVER CANCEL - takes 45-60 seconds)
mvn checkstyle:check --no-transfer-progress
# Timeout: Set 3+ minutes. Actual time: ~50 seconds
```
**CRITICAL TIMING NOTES:**
- **NEVER CANCEL** any Maven build command
- Compilation phase takes longest (~4 minutes) due to 34 modules
- Full builds are faster on subsequent runs due to incremental compilation
- Always use `--no-transfer-progress` to reduce log noise
### Testing Structure
- **Test Framework**: TestNG (not JUnit)
- **Test Files**: 298 test files across all modules
- **Default Behavior**: Tests are DISABLED by default in pom.xml (`<skip>true</skip>`)
- **Test Configuration**: Tests require external WeChat API credentials via test-config.xml files
- **DO NOT** attempt to run tests without proper WeChat API credentials as they will fail
## Project Structure and Navigation
### Core SDK Modules (Main Development Areas)
- `weixin-java-common/` - Common utilities and base classes (most important)
- `weixin-java-mp/` - WeChat Official Account SDK (公众号)
- `weixin-java-pay/` - WeChat Pay SDK (微信支付)
- `weixin-java-miniapp/` - Mini Program SDK (小程序)
- `weixin-java-cp/` - Enterprise WeChat SDK (企业微信)
- `weixin-java-open/` - Open Platform SDK (开放平台)
- `weixin-java-channel/` - Channel/Video SDK (视频号)
- `weixin-java-qidian/` - Qidian SDK (企点)
### Framework Integration Modules
- `spring-boot-starters/` - Spring Boot auto-configuration starters
- `solon-plugins/` - Solon framework plugins
- `weixin-graal/` - GraalVM native image support
### Configuration and Quality
- `quality-checks/google_checks.xml` - Checkstyle configuration
- `.editorconfig` - Code formatting rules (2 spaces = 1 tab)
- `pom.xml` - Root Maven configuration
## Development Workflow
### Making Code Changes
1. **Always build first** to establish clean baseline:
```bash
mvn clean compile --no-transfer-progress
```
2. **Follow code style** (enforced by checkstyle):
- Use 2 spaces for indentation (not tabs)
- Follow Google Java Style Guide
- Install EditorConfig plugin in your IDE
3. **Validate changes incrementally**:
```bash
# After each change:
mvn compile --no-transfer-progress
mvn checkstyle:check --no-transfer-progress
```
### Before Submitting Changes
**ALWAYS run these validation steps in sequence:**
1. **Code Style Validation**:
```bash
mvn checkstyle:check --no-transfer-progress
# Must pass - takes ~50 seconds
```
2. **Full Clean Build**:
```bash
mvn clean package -DskipTests=true --no-transfer-progress
# Must succeed - takes ~2 minutes
```
3. **Documentation**: Update javadoc for public methods and classes
4. **Contribution Guidelines**: Follow `CONTRIBUTING.md` - PRs must target `develop` branch
## Module Dependencies and Build Order
### Core Module Dependencies (Build Order)
1. `weixin-graal` (GraalVM support)
2. `weixin-java-common` (foundation for all other modules)
3. Core SDK modules (mp, pay, miniapp, cp, open, channel, qidian)
4. Framework integrations (spring-boot-starters, solon-plugins)
### Key Relationship Patterns
- All SDK modules depend on `weixin-java-common`
- Spring Boot starters depend on corresponding SDK modules
- Solon plugins follow same pattern as Spring Boot starters
- Each module has both single and multi-account configurations
## Common Tasks and Commands
### Validate Specific Module
```bash
# Build single module (replace 'weixin-java-mp' with target module):
cd weixin-java-mp
mvn clean compile --no-transfer-progress
```
### Check Dependencies
```bash
# Analyze dependencies:
mvn dependency:tree --no-transfer-progress
# Check for dependency updates:
./others/check-dependency-updates.sh
```
### Release and Publishing
```bash
# Version check:
mvn versions:display-property-updates --no-transfer-progress
# Deploy (requires credentials):
mvn clean deploy -P release --no-transfer-progress
```
## Important Files and Locations
### Configuration Files
- `pom.xml` - Root Maven configuration with dependency management
- `quality-checks/google_checks.xml` - Checkstyle rules
- `.editorconfig` - IDE formatting configuration
- `.github/workflows/maven-publish.yml` - CI/CD pipeline
### Documentation
- `README.md` - Project overview and usage (Chinese)
- `CONTRIBUTING.md` - Development contribution guidelines
- `demo.md` - Links to demo projects and examples
- Each module has dedicated documentation and examples
### Test Resources
- `*/src/test/resources/test-config.sample.xml` - Template for test configuration
- Tests require real WeChat API credentials to run
## SDK Usage Patterns
### Maven Dependency Usage
```xml
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId> <!-- or other modules -->
<version>4.7.0</version>
</dependency>
```
### Common Development Areas
- **API Client Implementation**: Located in `*/service/impl/` directories
- **Model Classes**: Located in `*/bean/` directories
- **Configuration**: Located in `*/config/` directories
- **Utilities**: Located in `*/util/` directories in weixin-java-common
## Troubleshooting
### Build Issues
- **OutOfMemoryError**: Increase Maven memory: `export MAVEN_OPTS="-Xmx2g"`
- **Compilation Failures**: Usually dependency issues - run `mvn clean` first
- **Checkstyle Failures**: Check `.editorconfig` settings in IDE
### Common Gotchas
- **Tests Always Skip**: This is normal - tests require WeChat API credentials
- **Multi-Module Changes**: Always build from root, not individual modules
- **Branch Target**: PRs must target `develop` branch, not `master`/`release`
## Performance Notes
- **First Build**: Takes 4-5 minutes due to dependency downloads
- **Incremental Builds**: Much faster (~30-60 seconds)
- **Checkstyle**: Runs quickly (~50 seconds) and should be run frequently
- **IDE Performance**: Project uses Lombok - ensure annotation processing is enabled
Remember: This is a SDK library project, not a runnable application. Changes should focus on API functionality, not application behavior.

4
.github/workflows vendored
View File

@@ -1,4 +0,0 @@
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

94
.github/workflows/maven-publish.yml vendored Normal file
View File

@@ -0,0 +1,94 @@
name: Publish to Maven Central
on:
push:
branches:
- develop
permissions:
contents: write
concurrency:
group: maven-publish-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect and tag release version from commit message
id: version_detect
run: |
COMMIT_MSG=$(git log -1 --pretty=%B)
VERSION=""
TAG=""
IS_RELEASE="false"
if [[ "$COMMIT_MSG" =~ ^:bookmark:\ 发布\ ([0-9]+\.[0-9]+\.[0-9]+)\.B\ 测试版本 ]]; then
BASE_VER="${BASH_REMATCH[1]}"
VERSION="${BASE_VER}.B"
TAG="v${BASE_VER}"
IS_RELEASE="true"
echo "Matched release commit: VERSION=$VERSION, TAG=$TAG"
# 检查并打tag
if git tag | grep -q "^$TAG$"; then
echo "Tag $TAG already exists."
else
git config user.name "Binary Wang"
git config user.email "a@binarywang.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
echo "Tag $TAG created and pushed."
fi
fi
echo "is_release=$IS_RELEASE" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'
server-id: central
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
cache: maven
- name: Verify GPG keys
run: |
echo "Available GPG Keys:"
gpg --list-secret-keys --keyid-format LONG
- name: Generate and set version
id: set_version
run: |
if [[ "${{ steps.version_detect.outputs.is_release }}" == "true" ]]; then
VERSION="${{ steps.version_detect.outputs.version }}"
else
git describe --tags 2>/dev/null || echo "no tag"
TIMESTAMP=$(date +'%Y%m%d.%H%M%S')
GIT_DESCRIBE=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.1")
VERSION="${GIT_DESCRIBE}-${TIMESTAMP}"
fi
echo "Final version: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
mvn versions:set -DnewVersion=$VERSION --no-transfer-progress
env:
TZ: Asia/Shanghai
- name: Publish to Maven Central
run: |
mvn clean deploy -P release \
-Dmaven.test.skip=true \
-Dgpg.args="--batch --yes --pinentry-mode loopback" \
--no-transfer-progress
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}

129
NEW_TRANSFER_API_SUPPORT.md Normal file
View File

@@ -0,0 +1,129 @@
# 微信支付新版商户转账API支持
## 问题解答
**问题**: 新开通的商户号只能使用最新版本的商户转账接口WxJava是否支持
**答案**: **WxJava 已经完整支持新版商户转账API** 从2025年1月15日开始生效的新版转账API已在WxJava中实现。
## 新版转账API特性
### 1. API接口对比
| 特性 | 传统转账API | 新版转账API (2025.1.15+) |
|------|-------------|-------------------------|
| **服务类** | `MerchantTransferService` | `TransferService` |
| **API路径** | `/v3/transfer/batches` | `/v3/fund-app/mch-transfer/transfer-bills` |
| **转账方式** | 批量转账 | 单笔转账 |
| **场景支持** | 基础场景 | 丰富场景(如佣金报酬等) |
| **撤销功能** | ❌ 不支持 | ✅ 支持 |
| **适用范围** | 所有商户 | **新开通商户必须使用** |
### 2. 新版API功能列表
**发起转账** - `transferBills()`
**查询转账** - `getBillsByOutBillNo()` / `getBillsByTransferBillNo()`
**撤销转账** - `transformBillsCancel()`
**回调通知** - `parseTransferBillsNotifyResult()`
**RSA加密** - 自动处理用户姓名加密
**场景支持** - 支持多种转账场景ID
## 快速开始
### 1. 获取服务实例
```java
// 获取WxPayService实例
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(config);
// 获取新版转账服务 - 这就是新开通商户需要使用的服务!
TransferService transferService = wxPayService.getTransferService();
```
### 2. 发起转账新版API
```java
// 构建转账请求
TransferBillsRequest request = TransferBillsRequest.newBuilder()
.appid("your_appid") // 应用ID
.outBillNo("T" + System.currentTimeMillis()) // 商户转账单号
.transferSceneId("1005") // 转账场景ID佣金报酬
.openid("user_openid") // 用户openid
.userName("张三") // 收款用户姓名(可选,自动加密)
.transferAmount(100) // 转账金额(分)
.transferRemark("佣金报酬") // 转账备注
.build();
// 发起转账
TransferBillsResult result = transferService.transferBills(request);
System.out.println("转账成功,微信转账单号:" + result.getTransferBillNo());
```
### 3. 查询转账结果
```java
// 通过商户单号查询
TransferBillsGetResult result = transferService.getBillsByOutBillNo("T1642567890123");
// 通过微信转账单号查询
TransferBillsGetResult result2 = transferService.getBillsByTransferBillNo("wx_transfer_bill_no");
System.out.println("转账状态:" + result.getState());
```
### 4. 撤销转账(新功能)
```java
// 撤销转账
TransferBillsCancelResult cancelResult = transferService.transformBillsCancel("T1642567890123");
System.out.println("撤销状态:" + cancelResult.getState());
```
## 重要说明
### 转账场景ID (transfer_scene_id)
- **1005**: 佣金报酬(常用场景)
- 其他场景需要在微信商户平台申请
### 转账状态说明
- **ACCEPTED**: 转账已受理
- **PROCESSING**: 转账处理中
- **SUCCESS**: 转账成功
- **FAIL**: 转账失败
- **CANCELLED**: 转账撤销完成
### 新开通商户使用建议
1. **优先使用** `TransferService` 新版API
2. **不要使用** `MerchantTransferService` (可能不支持)
3. **必须设置** 转账场景ID (`transfer_scene_id`)
4. **建议开启** 回调通知以实时获取转账结果
## 完整示例代码
详细的使用示例请参考:
- 📄 [NEW_TRANSFER_API_USAGE.md](./NEW_TRANSFER_API_USAGE.md) - 详细使用指南
- 💻 [NewTransferApiExample.java](./weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/NewTransferApiExample.java) - 完整代码示例
## 常见问题
**Q: 我是新开通的商户,应该使用哪个服务?**
A: 使用 `TransferService`这是专为新版API设计的服务。
**Q: 新版API和旧版API有什么区别**
A: 新版API使用单笔转账模式支持更丰富的转账场景并且支持撤销功能。
**Q: 如何设置转账场景ID**
A: 在商户平台申请相应场景常用的佣金报酬场景ID是"1005"。
**Q: 用户姓名需要加密吗?**
A: WxJava会自动处理RSA加密您只需要传入明文姓名即可。
## 版本要求
- WxJava 版本4.7.0+
- 支持时间2025年1月15日+
- 适用商户:所有商户(新开通商户强制使用)
通过以上说明新开通的微信支付商户可以放心使用WxJava进行商户转账操作

148
NEW_TRANSFER_API_USAGE.md Normal file
View File

@@ -0,0 +1,148 @@
# 微信支付新版商户转账API使用指南
## 概述
从2025年1月15日开始微信支付推出了新版的商户转账API。新开通的商户号只能使用最新版本的商户转账接口。WxJava 已经完整支持新版转账API。
## API对比
### 传统转账API (仍然支持)
- **服务类**: `MerchantTransferService`
- **API前缀**: `/v3/transfer/batches`
- **特点**: 支持批量转账,一次可以转账给多个用户
### 新版转账API (2025.1.15+)
- **服务类**: `TransferService`
- **API前缀**: `/v3/fund-app/mch-transfer/transfer-bills`
- **特点**: 单笔转账,支持更丰富的转账场景
## 使用新版转账API
### 1. 获取服务实例
```java
// 获取WxPayService实例
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(config);
// 获取新版转账服务
TransferService transferService = wxPayService.getTransferService();
```
### 2. 发起转账
```java
// 构建转账请求
TransferBillsRequest request = TransferBillsRequest.newBuilder()
.appid("your_appid") // 应用ID
.outBillNo("T" + System.currentTimeMillis()) // 商户转账单号
.transferSceneId("1005") // 转账场景ID佣金报酬
.openid("user_openid") // 用户openid
.userName("张三") // 收款用户姓名(可选,需要加密)
.transferAmount(100) // 转账金额(分)
.transferRemark("佣金报酬") // 转账备注
.notifyUrl("https://your-domain.com/notify") // 回调地址(可选)
.userRecvPerception("Y") // 用户收款感知(可选)
.build();
try {
TransferBillsResult result = transferService.transferBills(request);
System.out.println("转账成功,微信转账单号:" + result.getTransferBillNo());
System.out.println("状态:" + result.getState());
} catch (WxPayException e) {
System.err.println("转账失败:" + e.getMessage());
}
```
### 3. 查询转账结果
```java
// 通过商户单号查询
String outBillNo = "T1642567890123";
TransferBillsGetResult result = transferService.getBillsByOutBillNo(outBillNo);
// 通过微信转账单号查询
String transferBillNo = "1000000000000000000000000001";
TransferBillsGetResult result2 = transferService.getBillsByTransferBillNo(transferBillNo);
System.out.println("转账状态:" + result.getState());
System.out.println("转账金额:" + result.getTransferAmount());
```
### 4. 撤销转账
```java
// 撤销转账(仅在特定状态下可撤销)
String outBillNo = "T1642567890123";
TransferBillsCancelResult cancelResult = transferService.transformBillsCancel(outBillNo);
System.out.println("撤销结果:" + cancelResult.getState());
```
### 5. 处理回调通知
```java
// 在回调接口中处理通知
@PostMapping("/transfer/notify")
public String handleTransferNotify(HttpServletRequest request) throws Exception {
String notifyData = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
// 构建签名头
SignatureHeader header = new SignatureHeader();
header.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
header.setNonce(request.getHeader("Wechatpay-Nonce"));
header.setSignature(request.getHeader("Wechatpay-Signature"));
header.setSerial(request.getHeader("Wechatpay-Serial"));
try {
TransferBillsNotifyResult notifyResult = transferService.parseTransferBillsNotifyResult(notifyData, header);
// 处理业务逻辑
String outBillNo = notifyResult.getOutBillNo();
String state = notifyResult.getState();
System.out.println("转账单号:" + outBillNo + ",状态:" + state);
return "SUCCESS";
} catch (WxPayException e) {
System.err.println("验签失败:" + e.getMessage());
return "FAIL";
}
}
```
## 重要参数说明
### 转账场景ID (transfer_scene_id)
- **1005**: 佣金报酬(常用)
- 其他场景ID需要在商户平台申请
### 转账状态
- **PROCESSING**: 转账中
- **SUCCESS**: 转账成功
- **FAILED**: 转账失败
- **REFUNDED**: 已退款
### 用户收款感知 (user_recv_perception)
- **Y**: 用户会收到微信转账通知
- **N**: 用户不会收到微信转账通知
## 新旧API对比总结
| 特性 | 传统API (MerchantTransferService) | 新版API (TransferService) |
|------|----------------------------------|---------------------------|
| 发起方式 | 批量转账 | 单笔转账 |
| API路径 | `/v3/transfer/batches` | `/v3/fund-app/mch-transfer/transfer-bills` |
| 场景支持 | 基础转账场景 | 丰富的转账场景 |
| 回调通知 | 支持 | 支持 |
| 撤销功能 | 不支持 | 支持 |
| 适用商户 | 所有商户 | 新开通商户必须使用 |
## 注意事项
1. **新开通的商户号**: 必须使用新版API (`TransferService`)
2. **转账场景ID**: 需要在商户平台申请相应的转账场景
3. **用户姓名加密**: 如果传入用户姓名会自动进行RSA加密
4. **回调验签**: 建议开启回调验签以确保安全性
5. **错误处理**: 妥善处理各种异常情况
通过以上指南您可以轻松使用WxJava的新版商户转账API功能。

121
README.md
View File

@@ -1,65 +1,65 @@
## WxJava - 微信开发 Java SDK
[![Github](https://img.shields.io/github/stars/binarywang/WxJava?logo=github&style=flat&label=Stars)](https://github.com/binarywang/WxJava)
[![Gitee](https://gitee.com/binary/weixin-java-tools/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools)
[![GitCode](https://gitcode.com/binary/WxJava/star/badge.svg)](https://gitcode.com/binary/WxJava)
[![码云Gitee](https://gitee.com/binary/weixin-java-tools/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools)
[![Github](https://img.shields.io/github/stars/binarywang/WxJava?logo=github&style=flat)](https://github.com/binarywang/WxJava)
[![GitHub release](https://img.shields.io/github/release/binarywang/WxJava.svg)](https://github.com/binarywang/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://img.shields.io/circleci/project/github/binarywang/WxJava/develop.svg?sanitize=true)](https://circleci.com/gh/binarywang/WxJava/tree/develop)
[![GitHub release](https://img.shields.io/github/release/binarywang/WxJava?label=Release)](https://github.com/binarywang/WxJava/releases)
[![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java?label=Maven)](https://central.sonatype.com/artifact/com.github.binarywang/wx-java/versions)
[![Build Status](https://img.shields.io/circleci/project/github/binarywang/WxJava/develop.svg?sanitize=true&label=Build)](https://circleci.com/gh/binarywang/WxJava/tree/develop)
[![使用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)
#### 微信`Java`开发工具包,支持包括微信支付、开放平台、公众号、企业微信、视频号、小程序等微信功能模块的后端开发。
<div align="center">
<a href="https://hellogithub.com/repository/6de6147050c94db4aedfd7098d19f8d8" target="_blank">
<img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=6de6147050c94db4aedfd7098d19f8d8&claim_uid=mwKh0tILBfjlezR" alt="FeaturedHelloGitHub" width="250" height="54" />
</a>
<a href="https://trendshift.io/repositories/12152" target="_blank">
<img src="https://trendshift.io/api/badge/repositories/12152" alt="binarywang%2FWxJava | 趋势转变" width="250" height="55" />
</a>
</div>
### 微信`Java`开发工具包,支持包括微信支付、开放平台、公众号、企业微信、视频号、小程序等微信功能模块的后端开发。
<div align="center">
<b>特别赞助</b>
<table cellspacing="0" cellpadding="0" width="500">
<tr>
<td align="center" colspan="3">
<a href="http://www.ccflow.org/?from=wxjava" target="_blank">
<img height="120" src="https://ccfast.cc/AD/ccflow2.png" alt="ccflow">
</a>
</td>
</tr>
<tr>
<td align="center" colspan="2">
<a href="https://www.jeequan.com/product/jeepay.html" target="_blank">
<img height="120" src="https://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/wxjava_jeepay.png" alt="计全支付Jeepay,开源支付系统">
</a>
</td>
<td align="center">
<a href="https://www.mall4j.com/cn/?statId=9" target="_blank">
<img height="120" src="https://img.mall4j.com/mall.png" alt="Mall4j">
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="http://mp.weixin.qq.com/mp/homepage?__biz=MzI3MzAwMzk4OA==&hid=1&sn=f31af3bf562b116b061c9ab4edf70b61&scene=18#wechat_redirect" target="_blank">
<img height="120" src="https://gitee.com/binary/weixin-java-tools/raw/develop/images/qrcodes/mp.png" alt="mp qrcode">
</a>
</td>
<td align="center">
<a href="https://www.diboot.com?from=wxjava" target="_blank">
<img height="120" src="https://www.diboot.com/img/diboot_ad.png" alt="diboot低代码开发平台">
</a>
</td>
<td align="center">
<a href="https://www.crmeb.com/xiazai" target="_blank">
<img height="120" src="https://crmebjavamer.oss-cn-beijing.aliyuncs.com/crmebimage/gitee/WechatIMG202.jpg" alt="ad">
</a>
</td>
</tr>
</table>
</div>
<table align="center" cellspacing="0" cellpadding="0" width="500">
<tr>
<td align="center" valign="middle" colspan="3">
<a href="http://www.ccflow.org/?from=wxjava" target="_blank">
<img height="120" src="https://ccfast.cc/AD/ccflow2.png" alt="ccflow">
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle" colspan="2">
<a href="https://www.jeequan.com/product/jeepay.html" target="_blank">
<img height="120" src="https://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/wxjava_jeepay.png" alt="计全支付Jeepay,开源支付系统">
</a>
</td>
<td align="center" valign="middle" colspan="1">
<a href="https://www.mall4j.com/cn/?statId=9" target="_blank">
<img height="120" src="https://img.mall4j.com/mall.png" alt="Mall4j">
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="http://mp.weixin.qq.com/mp/homepage?__biz=MzI3MzAwMzk4OA==&hid=1&sn=f31af3bf562b116b061c9ab4edf70b61&scene=18#wechat_redirect" target="_blank">
<img height="120" src="https://gitee.com/binary/weixin-java-tools/raw/develop/images/qrcodes/mp.png" alt="mp qrcode">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.diboot.com?from=wxjava" target="_blank">
<img height="120" src="https://www.diboot.com/img/diboot_ad.png" alt="diboot低代码开发平台"/>
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.iyque.cn/" target="_blank">
<img height="120" src="https://iyque-1251309172.cos.ap-nanjing.myqcloud.com/advert/wxjava.jpg" alt="aliyun ad">
</a>
</td>
</tr>
<tr>
<td colspan="3" align="center">
<a href="https://hellogithub.com/repository/6de6147050c94db4aedfd7098d19f8d8" target="_blank">
<img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=6de6147050c94db4aedfd7098d19f8d8&claim_uid=mwKh0tILBfjlezR"
alt="FeaturedHelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" />
</a>
<a href="https://trendshift.io/repositories/12152" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12152" alt="binarywang%2FWxJava | 趋势转变" style="width: 250px; height: 55px;" width="250" height="55"/>
</a>
</td>
</tr>
</table>
### 重要信息
1. [`WxJava` 荣获 `GitCode` 2024年度十大开源社区奖项](https://mp.weixin.qq.com/s/wM_UlMsDm3IZ1CPPDvcvQw)。
@@ -89,7 +89,7 @@
---------------------------------
### 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),以下为最新正式版。
注意:最新版本(包括测试版)为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](https://central.sonatype.com/artifact/com.github.binarywang/wx-java/versions),以下为最新正式版。
```xml
<dependency>
@@ -113,10 +113,13 @@
<details>
<summary>点此展开查看</summary>
1. 本项目定为大约每两个月发布一次正式版(同时 `develop` 分支代码合并进入 `release` 分支),版本号格式为 `X.X.0`(如`2.1.0``2.2.0`等)遇到重大问题需修复会及时提交新版本欢迎大家随时提交Pull Request
2. BUG修复和新特性一般会先发布成小版本作为临时测试版本`3.6.8.B`即尾号不为0并添加B以区别于正式版代码仅存在于 `develop` 分支中;
3. 目前最新版本号为 [![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)
分别查看所有最新的版本。
1. 本项目定为大约每半年左右发布一次正式版,遇到重大问题需修复会及时提交新版本,欢迎大家随时提交 `Pull Request`
2. 每次代码更新都会自动构建出新版本方便及时尝鲜,版本号格式为 `x.x.x-时间戳`;
3. 发布正式版时,`develop` 分支代码合并进入 `release` 分支),版本号格式为 `X.X.0`(如`2.1.0``2.2.0`等);
4. 每隔一段时间后,会发布测试版本(如`3.6.8.B`即尾号不为0并添加B以区别于正式版代码仅存在于 `develop` 分支中;
5. 目前最新版本号为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java) ,也可以通过访问以下链接分别查看各个模块最新的版本:
[【微信支付】](https://central.sonatype.com/artifact/com.github.binarywang/weixin-java-pay/versions) 、[【小程序】](https://central.sonatype.com/artifact/com.github.binarywang/weixin-java-miniapp/versions) 、[【公众号】](https://central.sonatype.com/artifact/com.github.binarywang/weixin-java-mp/versions) 、[【企业微信】](https://central.sonatype.com/artifact/com.github.binarywang/weixin-java-cp/versions)、[【开放平台】](https://central.sonatype.com/artifact/com.github.binarywang/weixin-java-open/versions)、[【视频号】](https://central.sonatype.com/artifact/com.github.binarywang/weixin-java-channel/versions)
</details>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

0
others/mvnw vendored Normal file → Executable file
View File

68
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
<packaging>pom</packaging>
<name>WxJava - Weixin/Wechat Java SDK</name>
<description>微信开发Java SDK</description>
@@ -136,10 +136,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<httpclient.version>4.5.13</httpclient.version>
<jetty.version>9.4.56.v20240826</jetty.version>
<!-- 这个不能用10以上的版本不支持jdk8-->
<jetty.version>9.4.57.v20241219</jetty.version> <!-- 这个不能用10以上的版本不支持jdk8-->
</properties>
<dependencyManagement>
<dependencies>
<dependency>
@@ -156,7 +154,13 @@
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.5.0</version>
<version>4.12.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.5</version>
<scope>provided</scope>
</dependency>
@@ -183,7 +187,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
@@ -198,17 +202,19 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
<version>33.3.1-jre</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.15.2</version>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.18.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 测试所用依赖 -->
@@ -327,22 +333,11 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.78.1</version>
<version>1.80</version>
</dependency>
</dependencies>
</dependencyManagement>
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<profiles>
<profile>
<id>doclint-java8-disable</id>
@@ -432,14 +427,14 @@
<plugins>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.3</version>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.7.0</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
<deploymentName>Release WxJava:${project.version}</deploymentName>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
</configuration>
</plugin>
<plugin>
@@ -473,6 +468,21 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version> <!-- 建议使用最新版本 -->
<executions>
<execution>
<id>attach-sources</id>
<!-- 绑定到 verify 阶段,在 install 之前执行 -->
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal> <!-- 使用 jar-no-fork 效率更高 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<packaging>pom</packaging>
<artifactId>wx-java-solon-plugins</artifactId>
@@ -14,7 +14,7 @@
<description>WxJava 各个模块的 Solon Plugin</description>
<properties>
<solon.version>3.0.1</solon.version>
<solon.version>3.2.0</solon.version>
</properties>
<modules>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -8,6 +8,7 @@ import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServicesImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.channel.api.WxChannelService;
import me.chanjar.weixin.channel.api.impl.WxChannelServiceHttpComponentsImpl;
import me.chanjar.weixin.channel.api.impl.WxChannelServiceHttpClientImpl;
import me.chanjar.weixin.channel.api.impl.WxChannelServiceImpl;
import me.chanjar.weixin.channel.config.WxChannelConfig;
@@ -84,6 +85,9 @@ public abstract class AbstractWxChannelConfiguration {
case HTTP_CLIENT:
wxChannelService = new WxChannelServiceHttpClientImpl();
break;
case HTTP_COMPONENTS:
wxChannelService = new WxChannelServiceHttpComponentsImpl();
break;
default:
wxChannelService = new WxChannelServiceImpl();
break;

View File

@@ -11,6 +11,10 @@ public enum HttpClientType {
* HttpClient
*/
HTTP_CLIENT,
/**
* HttpComponents
*/
HTTP_COMPONENTS
// WxChannelServiceOkHttpImpl 实现经测试无法正常完成业务固暂不支持OK_HTTP方式
// /**
// * OkHttp.

View File

@@ -0,0 +1,92 @@
# wx-java-channel-solon-plugin
## 快速开始
1. 引入依赖
```xml
<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-channel-solon-plugin</artifactId>
<version>${version}</version>
</dependency>
<!-- 配置存储方式为jedis 则引入jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<!-- 配置存储方式为redisson 则引入redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
</dependencies>
```
2. 添加配置(app.properties)
```properties
# 视频号配置(必填)
## 视频号小店的appId和secret
wx.channel.app-id=@appId
wx.channel.secret=@secret
# 视频号配置 选填
## 设置视频号小店消息服务器配置的token
wx.channel.token=@token
## 设置视频号小店消息服务器配置的EncodingAESKey
wx.channel.aes-key=
## 支持JSON或者XML格式默认JSON
wx.channel.msg-data-format=JSON
## 是否使用稳定版 Access Token
wx.channel.use-stable-access-token=false
# ConfigStorage 配置(选填)
## 配置类型: memory(默认), jedis, redisson, redis_template
wx.channel.config-storage.type=memory
## 相关redis前缀配置: wx:channel(默认)
wx.channel.config-storage.key-prefix=wx:channel
wx.channel.config-storage.redis.host=127.0.0.1
wx.channel.config-storage.redis.port=6379
wx.channel.config-storage.redis.password=123456
# http 客户端配置(选填)
## # http客户端类型: http_client(默认)
wx.channel.config-storage.http-client-type=http_client
wx.channel.config-storage.http-proxy-host=
wx.channel.config-storage.http-proxy-port=
wx.channel.config-storage.http-proxy-username=
wx.channel.config-storage.http-proxy-password=
## 最大重试次数默认5 次,如果小于 0则为 0
wx.channel.config-storage.max-retry-times=5
## 重试时间间隔步进默认1000 毫秒,如果小于 0则为 1000
wx.channel.config-storage.retry-sleep-millis=1000
```
3. 自动注入的类型
- `WxChannelService`
- `WxChannelConfig`
4. 使用样例
```java
import me.chanjar.weixin.channel.api.WxChannelService;
import me.chanjar.weixin.channel.bean.shop.ShopInfoResponse;
import me.chanjar.weixin.channel.util.JsonUtils;
import me.chanjar.weixin.common.error.WxErrorException;
import org.noear.solon.annotation.Inject;
@Component
public class DemoService {
@Inject
private WxChannelService wxChannelService;
public String getShopInfo() throws WxErrorException {
// 获取店铺基本信息
ShopInfoResponse response = wxChannelService.getBasicService().getShopInfo();
// 此处为演示如果要返回response的结果建议自己封装一个VO避免直接返回response
return JsonUtils.encode(response);
}
}
```

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -7,10 +7,7 @@ import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServicesImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceApacheHttpClientImpl;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.api.impl.WxCpServiceJoddHttpImpl;
import me.chanjar.weixin.cp.api.impl.WxCpServiceOkHttpImpl;
import me.chanjar.weixin.cp.api.impl.*;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import org.apache.commons.lang3.StringUtils;
@@ -96,6 +93,9 @@ public abstract class AbstractWxCpConfiguration {
case HTTP_CLIENT:
wxCpService = new WxCpServiceApacheHttpClientImpl();
break;
case HTTP_COMPONENTS:
wxCpService = new WxCpServiceHttpComponentsImpl();
break;
default:
wxCpService = new WxCpServiceImpl();
break;

View File

@@ -117,6 +117,10 @@ public class WxCpMultiProperties implements Serializable {
* HttpClient
*/
HTTP_CLIENT,
/**
* HttpComponents
*/
HTTP_COMPONENTS,
/**
* OkHttp
*/

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -18,11 +18,13 @@ import org.noear.solon.annotation.Condition;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.AppContext;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.util.Pool;
import java.time.Duration;
import java.util.Set;
/**
@@ -75,7 +77,7 @@ public class WxQidianStorageAutoConfiguration {
}
private WxQidianConfigStorage jedisConfigStorage() {
Pool jedisPool;
Pool<Jedis> jedisPool;
if (StringUtils.isNotEmpty(redisHost) || StringUtils.isNotEmpty(redisHost2)) {
jedisPool = getJedisPool();
} else {
@@ -104,7 +106,7 @@ public class WxQidianStorageAutoConfiguration {
}
}
private Pool getJedisPool() {
private Pool<Jedis> getJedisPool() {
WxQidianProperties.ConfigStorage storage = wxQidianProperties.getConfigStorage();
RedisProperties redis = storage.getRedis();
@@ -116,7 +118,7 @@ public class WxQidianStorageAutoConfiguration {
config.setMaxIdle(redis.getMaxIdle());
}
if (redis.getMaxWaitMillis() != null) {
config.setMaxWaitMillis(redis.getMaxWaitMillis());
config.setMaxWait(Duration.ofMillis(redis.getMaxWaitMillis()));
}
if (redis.getMinIdle() != null) {
config.setMinIdle(redis.getMinIdle());

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<packaging>pom</packaging>
<artifactId>wx-java-spring-boot-starters</artifactId>
@@ -26,6 +26,7 @@
<module>wx-java-open-spring-boot-starter</module>
<module>wx-java-qidian-spring-boot-starter</module>
<module>wx-java-cp-multi-spring-boot-starter</module>
<module>wx-java-cp-tp-multi-spring-boot-starter</module>
<module>wx-java-cp-spring-boot-starter</module>
<module>wx-java-channel-spring-boot-starter</module>
<module>wx-java-channel-multi-spring-boot-starter</module>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -119,6 +119,8 @@ public abstract class AbstractWxChannelConfiguration {
config.setAesKey(aesKey);
}
config.setStableAccessToken(useStableAccessToken);
config.setApiHostUrl(StringUtils.trimToNull(wxChannelSingleProperties.getApiHostUrl()));
config.setAccessTokenUrl(StringUtils.trimToNull(wxChannelSingleProperties.getAccessTokenUrl()));
}
private void configHttp(WxChannelDefaultConfigImpl config, WxChannelMultiProperties.ConfigStorage storage) {

View File

@@ -40,4 +40,16 @@ public class WxChannelSingleProperties implements Serializable {
* 是否使用稳定版 Access Token
*/
private boolean useStableAccessToken = false;
/**
* 自定义API主机地址用于替换默认的 https://api.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String apiHostUrl;
/**
* 自定义获取AccessToken地址用于向自定义统一服务获取AccessToken
* 例如http://proxy.company.com:8080/oauth/token
*/
private String accessTokenUrl;
}

View File

@@ -6,7 +6,7 @@
<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-channel-multi-spring-boot-starter</artifactId>
<artifactId>wx-java-channel-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -16,6 +16,8 @@ public abstract class AbstractWxChannelConfigStorageConfiguration {
config.setAesKey(StringUtils.trimToNull(properties.getAesKey()));
config.setMsgDataFormat(StringUtils.trimToNull(properties.getMsgDataFormat()));
config.setStableAccessToken(properties.isUseStableAccessToken());
config.setApiHostUrl(StringUtils.trimToNull(properties.getApiHostUrl()));
config.setAccessTokenUrl(StringUtils.trimToNull(properties.getAccessTokenUrl()));
WxChannelProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());

View File

@@ -46,6 +46,18 @@ public class WxChannelProperties {
*/
private boolean useStableAccessToken = false;
/**
* 自定义API主机地址用于替换默认的 https://api.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String apiHostUrl;
/**
* 自定义获取AccessToken地址用于向自定义统一服务获取AccessToken
* 例如http://proxy.company.com:8080/oauth/token
*/
private String accessTokenUrl;
/**
* 存储策略
*/

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -139,6 +139,9 @@ public abstract class AbstractWxCpConfiguration {
if (StringUtils.isNotBlank(msgAuditLibPath)) {
config.setMsgAuditLibPath(msgAuditLibPath);
}
if (StringUtils.isNotBlank(wxCpSingleProperties.getBaseApiUrl())) {
config.setBaseApiUrl(wxCpSingleProperties.getBaseApiUrl());
}
}
private void configHttp(WxCpDefaultConfigImpl config, WxCpMultiProperties.ConfigStorage storage) {

View File

@@ -43,4 +43,10 @@ public class WxCpSingleProperties implements Serializable {
* 微信企业号应用 会话存档类库路径
*/
private String msgAuditLibPath;
/**
* 自定义企业微信服务器baseUrl用于替换默认的 https://qyapi.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String baseApiUrl;
}

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -48,6 +48,12 @@ public class WxCpProperties {
*/
private String msgAuditLibPath;
/**
* 自定义企业微信服务器baseUrl用于替换默认的 https://qyapi.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String baseApiUrl;
/**
* 配置存储策略,默认内存
*/

View File

@@ -37,6 +37,9 @@ public abstract class AbstractWxCpConfigStorageConfiguration {
if (StringUtils.isNotBlank(msgAuditLibPath)) {
config.setMsgAuditLibPath(msgAuditLibPath);
}
if (StringUtils.isNotBlank(properties.getBaseApiUrl())) {
config.setBaseApiUrl(properties.getBaseApiUrl());
}
WxCpProperties.ConfigStorage storage = properties.getConfigStorage();
String httpProxyHost = storage.getHttpProxyHost();

View File

@@ -0,0 +1,97 @@
# wx-java-cp-multi-spring-boot-starter
企业微信多账号配置
- 实现多 WxCpService 初始化。
- 未实现 WxCpTpService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。
- 未实现 WxCpCgService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。
## 快速开始
1. 引入依赖
```xml
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-cp-multi-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
```
2. 添加配置(application.properties)
```properties
# 应用 1 配置
wx.cp.corps.tenantId1.corp-id = @corp-id
wx.cp.corps.tenantId1.corp-secret = @corp-secret
## 选填
wx.cp.corps.tenantId1.agent-id = @agent-id
wx.cp.corps.tenantId1.token = @token
wx.cp.corps.tenantId1.aes-key = @aes-key
wx.cp.corps.tenantId1.msg-audit-priKey = @msg-audit-priKey
wx.cp.corps.tenantId1.msg-audit-lib-path = @msg-audit-lib-path
# 应用 2 配置
wx.cp.corps.tenantId2.corp-id = @corp-id
wx.cp.corps.tenantId2.corp-secret = @corp-secret
## 选填
wx.cp.corps.tenantId2.agent-id = @agent-id
wx.cp.corps.tenantId2.token = @token
wx.cp.corps.tenantId2.aes-key = @aes-key
wx.cp.corps.tenantId2.msg-audit-priKey = @msg-audit-priKey
wx.cp.corps.tenantId2.msg-audit-lib-path = @msg-audit-lib-path
# 公共配置
## ConfigStorage 配置(选填)
wx.cp.config-storage.type=memory # 配置类型: memory(默认), jedis, redisson, redistemplate
## http 客户端配置(选填)
## # http客户端类型: http_client(默认), ok_http, jodd_http
wx.cp.config-storage.http-client-type=http_client
wx.cp.config-storage.http-proxy-host=
wx.cp.config-storage.http-proxy-port=
wx.cp.config-storage.http-proxy-username=
wx.cp.config-storage.http-proxy-password=
## 最大重试次数默认5 次,如果小于 0则为 0
wx.cp.config-storage.max-retry-times=5
## 重试时间间隔步进默认1000 毫秒,如果小于 0则为 1000
wx.cp.config-storage.retry-sleep-millis=1000
```
3. 支持自动注入的类型: `WxCpMultiServices`
4. 使用样例
```java
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DemoService {
@Autowired
private WxCpTpMultiServices wxCpTpMultiServices;
public void test() {
// 应用 1 的 WxCpService
WxCpService wxCpService1 = wxCpMultiServices.getWxCpService("tenantId1");
WxCpUserService userService1 = wxCpService1.getUserService();
userService1.getUserId("xxx");
// todo ...
// 应用 2 的 WxCpService
WxCpService wxCpService2 = wxCpMultiServices.getWxCpService("tenantId2");
WxCpUserService userService2 = wxCpService2.getUserService();
userService2.getUserId("xxx");
// todo ...
// 应用 3 的 WxCpService
WxCpService wxCpService3 = wxCpMultiServices.getWxCpService("tenantId3");
// 判断是否为空
if (wxCpService3 == null) {
// todo wxCpService3 为空,请先配置 tenantId3 企业微信应用参数
return;
}
WxCpUserService userService3 = wxCpService3.getUserService();
userService3.getUserId("xxx");
// todo ...
}
}
```

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wx-java-cp-tp-multi-spring-boot-starter</artifactId>
<name>WxJava - Spring Boot Starter for WxCp::支持多账号配置</name>
<description>微信企业号开发的 Spring Boot Starter::支持多账号配置</description>
<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-cp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,16 @@
package com.binarywang.spring.starter.wxjava.cp.autoconfigure;
import com.binarywang.spring.starter.wxjava.cp.configuration.WxCpTpMultiServicesAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 企业微信自动注册
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@Import(WxCpTpMultiServicesAutoConfiguration.class)
public class WxCpTpMultiAutoConfiguration {
}

View File

@@ -0,0 +1,27 @@
package com.binarywang.spring.starter.wxjava.cp.configuration;
import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInJedisTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInMemoryTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInRedisTemplateTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.configuration.services.WxCpTpInRedissonTpConfiguration;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 企业微信平台相关服务自动注册
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@EnableConfigurationProperties(WxCpTpMultiProperties.class)
@Import({
WxCpTpInJedisTpConfiguration.class,
WxCpTpInMemoryTpConfiguration.class,
WxCpTpInRedissonTpConfiguration.class,
WxCpTpInRedisTemplateTpConfiguration.class
})
public class WxCpTpMultiServicesAutoConfiguration {
}

View File

@@ -0,0 +1,139 @@
package com.binarywang.spring.starter.wxjava.cp.configuration.services;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpSingleProperties;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServicesImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceApacheHttpClientImpl;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceImpl;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceJoddHttpImpl;
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceOkHttpImpl;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* WxCpConfigStorage 抽象配置类
*
* @author yl
* created on 2023/10/16
*/
@RequiredArgsConstructor
@Slf4j
public abstract class AbstractWxCpTpConfiguration {
/**
*
* @param wxCpTpMultiProperties 应用列表配置
* @param services 用于支持,应用启动之后,可以调用这个接口添加新服务对象。主要是配置是从数据库中读取的
* @return
*/
public WxCpTpMultiServices wxCpMultiServices(WxCpTpMultiProperties wxCpTpMultiProperties,WxCpTpMultiServices services) {
Map<String, WxCpTpSingleProperties> corps = wxCpTpMultiProperties.getCorps();
if (corps == null || corps.isEmpty()) {
log.warn("企业微信应用参数未配置,通过 WxCpMultiServices#getWxCpTpService(\"tenantId\")获取实例将返回空");
return new WxCpTpMultiServicesImpl();
}
if (services == null) {
services = new WxCpTpMultiServicesImpl();
}
Set<Map.Entry<String, WxCpTpSingleProperties>> entries = corps.entrySet();
for (Map.Entry<String, WxCpTpSingleProperties> entry : entries) {
String tenantId = entry.getKey();
WxCpTpSingleProperties wxCpTpSingleProperties = entry.getValue();
WxCpTpDefaultConfigImpl storage = this.wxCpTpConfigStorage(wxCpTpMultiProperties);
this.configCorp(storage, wxCpTpSingleProperties);
this.configHttp(storage, wxCpTpMultiProperties.getConfigStorage());
WxCpTpService wxCpTpService = this.wxCpTpService(storage, wxCpTpMultiProperties.getConfigStorage());
if (services.getWxCpTpService(tenantId) == null) {
// 不存在的才会添加到服务列表中
services.addWxCpTpService(tenantId, wxCpTpService);
}
}
return services;
}
/**
* 配置 WxCpDefaultConfigImpl
*
* @param wxCpTpMultiProperties 参数
* @return WxCpDefaultConfigImpl
*/
protected abstract WxCpTpDefaultConfigImpl wxCpTpConfigStorage(WxCpTpMultiProperties wxCpTpMultiProperties);
private WxCpTpService wxCpTpService(WxCpTpConfigStorage wxCpTpConfigStorage, WxCpTpMultiProperties.ConfigStorage storage) {
WxCpTpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
WxCpTpService cpTpService;
switch (httpClientType) {
case OK_HTTP:
cpTpService = new WxCpTpServiceOkHttpImpl();
break;
case JODD_HTTP:
cpTpService = new WxCpTpServiceJoddHttpImpl();
break;
case HTTP_CLIENT:
cpTpService = new WxCpTpServiceApacheHttpClientImpl();
break;
default:
cpTpService = new WxCpTpServiceImpl();
break;
}
cpTpService.setWxCpTpConfigStorage(wxCpTpConfigStorage);
int maxRetryTimes = storage.getMaxRetryTimes();
if (maxRetryTimes < 0) {
maxRetryTimes = 0;
}
int retrySleepMillis = storage.getRetrySleepMillis();
if (retrySleepMillis < 0) {
retrySleepMillis = 1000;
}
cpTpService.setRetrySleepMillis(retrySleepMillis);
cpTpService.setMaxRetryTimes(maxRetryTimes);
return cpTpService;
}
private void configCorp(WxCpTpDefaultConfigImpl config, WxCpTpSingleProperties wxCpTpSingleProperties) {
String corpId = wxCpTpSingleProperties.getCorpId();
String providerSecret = wxCpTpSingleProperties.getProviderSecret();
String suiteId = wxCpTpSingleProperties.getSuiteId();
String token = wxCpTpSingleProperties.getToken();
String suiteSecret = wxCpTpSingleProperties.getSuiteSecret();
// 企业微信,私钥,会话存档路径
config.setCorpId(corpId);
config.setProviderSecret(providerSecret);
config.setEncodingAESKey(wxCpTpSingleProperties.getEncodingAESKey());
config.setSuiteId(suiteId);
config.setToken(token);
config.setSuiteSecret(suiteSecret);
}
private void configHttp(WxCpTpDefaultConfigImpl config, WxCpTpMultiProperties.ConfigStorage storage) {
String httpProxyHost = storage.getHttpProxyHost();
Integer httpProxyPort = storage.getHttpProxyPort();
String httpProxyUsername = storage.getHttpProxyUsername();
String httpProxyPassword = storage.getHttpProxyPassword();
if (StringUtils.isNotBlank(httpProxyHost)) {
config.setHttpProxyHost(httpProxyHost);
if (httpProxyPort != null) {
config.setHttpProxyPort(httpProxyPort);
}
if (StringUtils.isNotBlank(httpProxyUsername)) {
config.setHttpProxyUsername(httpProxyUsername);
}
if (StringUtils.isNotBlank(httpProxyPassword)) {
config.setHttpProxyPassword(httpProxyPassword);
}
}
}
}

View File

@@ -0,0 +1,78 @@
package com.binarywang.spring.starter.wxjava.cp.configuration.services;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiRedisProperties;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpJedisConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpJedisConfigImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* 自动装配基于 jedis 策略配置
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@ConditionalOnProperty(
prefix = WxCpTpMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "jedis"
)
@RequiredArgsConstructor
public class WxCpTpInJedisTpConfiguration extends AbstractWxCpTpConfiguration {
private final WxCpTpMultiProperties wxCpTpMultiProperties;
private final ApplicationContext applicationContext;
@Bean
public WxCpTpMultiServices wxCpMultiServices() {
return this.wxCpMultiServices(wxCpTpMultiProperties,null);
}
@Override
protected WxCpTpDefaultConfigImpl wxCpTpConfigStorage(WxCpTpMultiProperties wxCpTpMultiProperties) {
return this.configRedis(wxCpTpMultiProperties);
}
private WxCpTpDefaultConfigImpl configRedis(WxCpTpMultiProperties wxCpTpMultiProperties) {
WxCpTpMultiRedisProperties wxCpTpMultiRedisProperties = wxCpTpMultiProperties.getConfigStorage().getRedis();
JedisPool jedisPool;
if (wxCpTpMultiRedisProperties != null && StringUtils.isNotEmpty(wxCpTpMultiRedisProperties.getHost())) {
jedisPool = getJedisPool(wxCpTpMultiProperties);
} else {
jedisPool = applicationContext.getBean(JedisPool.class);
}
return new WxCpTpJedisConfigImpl(jedisPool, wxCpTpMultiProperties.getConfigStorage().getKeyPrefix());
}
private JedisPool getJedisPool(WxCpTpMultiProperties wxCpTpMultiProperties) {
WxCpTpMultiProperties.ConfigStorage storage = wxCpTpMultiProperties.getConfigStorage();
WxCpTpMultiRedisProperties 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());
}
}

View File

@@ -0,0 +1,39 @@
package com.binarywang.spring.starter.wxjava.cp.configuration.services;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自动装配基于内存策略配置
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@ConditionalOnProperty(
prefix = WxCpTpMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "memory", matchIfMissing = true
)
@RequiredArgsConstructor
public class WxCpTpInMemoryTpConfiguration extends AbstractWxCpTpConfiguration {
private final WxCpTpMultiProperties wxCpTpMultiProperties;
@Bean
public WxCpTpMultiServices wxCpMultiServices() {
return this.wxCpMultiServices(wxCpTpMultiProperties,null);
}
@Override
protected WxCpTpDefaultConfigImpl wxCpTpConfigStorage(WxCpTpMultiProperties wxCpTpMultiProperties) {
return this.configInMemory();
}
private WxCpTpDefaultConfigImpl configInMemory() {
return new WxCpTpDefaultConfigImpl();
}
}

View File

@@ -0,0 +1,45 @@
package com.binarywang.spring.starter.wxjava.cp.configuration.services;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpRedisTemplateConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpRedisTemplateConfigImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 自动装配基于 redisTemplate 策略配置
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@ConditionalOnProperty(
prefix = WxCpTpMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redistemplate"
)
@RequiredArgsConstructor
public class WxCpTpInRedisTemplateTpConfiguration extends AbstractWxCpTpConfiguration {
private final WxCpTpMultiProperties wxCpTpMultiProperties;
private final ApplicationContext applicationContext;
@Bean
public WxCpTpMultiServices wxCpMultiServices() {
return this.wxCpMultiServices(wxCpTpMultiProperties,null);
}
@Override
protected WxCpTpDefaultConfigImpl wxCpTpConfigStorage(WxCpTpMultiProperties wxCpTpMultiProperties) {
return this.configRedisTemplate(wxCpTpMultiProperties);
}
private WxCpTpDefaultConfigImpl configRedisTemplate(WxCpTpMultiProperties wxCpTpMultiProperties) {
StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
return new WxCpTpRedisTemplateConfigImpl(redisTemplate, wxCpTpMultiProperties.getConfigStorage().getKeyPrefix());
}
}

View File

@@ -0,0 +1,68 @@
package com.binarywang.spring.starter.wxjava.cp.configuration.services;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiProperties;
import com.binarywang.spring.starter.wxjava.cp.properties.WxCpTpMultiRedisProperties;
import com.binarywang.spring.starter.wxjava.cp.service.WxCpTpMultiServices;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.cp.config.impl.WxCpTpDefaultConfigImpl;
import me.chanjar.weixin.cp.config.impl.AbstractWxCpTpInRedisConfigImpl;
import me.chanjar.weixin.cp.config.impl.WxCpTpRedissonConfigImpl;
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.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自动装配基于 redisson 策略配置
*
* @author yl
* created on 2023/10/16
*/
@Configuration
@ConditionalOnProperty(
prefix = WxCpTpMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redisson"
)
@RequiredArgsConstructor
public class WxCpTpInRedissonTpConfiguration extends AbstractWxCpTpConfiguration {
private final WxCpTpMultiProperties wxCpTpMultiProperties;
private final ApplicationContext applicationContext;
@Bean
public WxCpTpMultiServices wxCpMultiServices() {
return this.wxCpMultiServices(wxCpTpMultiProperties,null);
}
@Override
protected WxCpTpDefaultConfigImpl wxCpTpConfigStorage(WxCpTpMultiProperties wxCpTpMultiProperties) {
return this.configRedisson(wxCpTpMultiProperties);
}
private WxCpTpDefaultConfigImpl configRedisson(WxCpTpMultiProperties wxCpTpMultiProperties) {
WxCpTpMultiRedisProperties redisProperties = wxCpTpMultiProperties.getConfigStorage().getRedis();
RedissonClient redissonClient;
if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
redissonClient = getRedissonClient(wxCpTpMultiProperties);
} else {
redissonClient = applicationContext.getBean(RedissonClient.class);
}
return new WxCpTpRedissonConfigImpl(redissonClient, wxCpTpMultiProperties.getConfigStorage().getKeyPrefix());
}
private RedissonClient getRedissonClient(WxCpTpMultiProperties wxCpTpMultiProperties) {
WxCpTpMultiProperties.ConfigStorage storage = wxCpTpMultiProperties.getConfigStorage();
WxCpTpMultiRedisProperties redis = storage.getRedis();
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
.setDatabase(redis.getDatabase())
.setPassword(redis.getPassword());
config.setTransportMode(TransportMode.NIO);
return Redisson.create(config);
}
}

View File

@@ -0,0 +1,129 @@
package com.binarywang.spring.starter.wxjava.cp.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* 企业微信多企业接入相关配置属性
*
* @author yl
* created on 2023/10/16
*/
@Data
@NoArgsConstructor
@ConfigurationProperties(prefix = WxCpTpMultiProperties.PREFIX)
public class WxCpTpMultiProperties implements Serializable {
private static final long serialVersionUID = -1569510477055668503L;
public static final String PREFIX = "wx.cp.tp";
private Map<String, WxCpTpSingleProperties> corps = new HashMap<>();
/**
* 配置存储策略,默认内存
*/
private ConfigStorage configStorage = new ConfigStorage();
@Data
@NoArgsConstructor
public static class ConfigStorage implements Serializable {
private static final long serialVersionUID = 4815731027000065434L;
/**
* 存储类型
*/
private StorageType type = StorageType.memory;
/**
* 指定key前缀
*/
private String keyPrefix = "wx:cp:tp";
/**
* redis连接配置
*/
@NestedConfigurationProperty
private WxCpTpMultiRedisProperties redis = new WxCpTpMultiRedisProperties();
/**
* http客户端类型.
*/
private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
/**
* http代理主机
*/
private String httpProxyHost;
/**
* http代理端口
*/
private Integer httpProxyPort;
/**
* http代理用户名
*/
private String httpProxyUsername;
/**
* http代理密码
*/
private String httpProxyPassword;
/**
* http 请求最大重试次数
* <pre>
* {@link me.chanjar.weixin.cp.api.WxCpService#setMaxRetryTimes(int)}
* {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setMaxRetryTimes(int)}
* </pre>
*/
private int maxRetryTimes = 5;
/**
* http 请求重试间隔
* <pre>
* {@link me.chanjar.weixin.cp.api.WxCpService#setRetrySleepMillis(int)}
* {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setRetrySleepMillis(int)}
* </pre>
*/
private int retrySleepMillis = 1000;
}
public enum StorageType {
/**
* 内存
*/
memory,
/**
* jedis
*/
jedis,
/**
* redisson
*/
redisson,
/**
* redistemplate
*/
redistemplate
}
public enum HttpClientType {
/**
* HttpClient
*/
HTTP_CLIENT,
/**
* OkHttp
*/
OK_HTTP,
/**
* JoddHttp
*/
JODD_HTTP
}
}

View File

@@ -0,0 +1,48 @@
package com.binarywang.spring.starter.wxjava.cp.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* Redis配置.
*
* @author yl
* created on 2023/10/16
*/
@Data
@NoArgsConstructor
public class WxCpTpMultiRedisProperties 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;
}

View File

@@ -0,0 +1,43 @@
package com.binarywang.spring.starter.wxjava.cp.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 企业微信企业相关配置属性
*
* @author yl
* created on 2023/10/16
*/
@Data
@NoArgsConstructor
public class WxCpTpSingleProperties implements Serializable {
private static final long serialVersionUID = -7502823825007859418L;
/**
* 微信企业号 corpId
*/
private String corpId;
/**
* 微信企业号 服务商 providerSecret
*/
private String providerSecret;
/**
* 微信企业号应用 token
*/
private String token;
private String encodingAESKey;
/**
* 微信企业号 第三方 应用 ID
*/
private String suiteId;
/**
* 微信企业号应用
*/
private String suiteSecret;
}

View File

@@ -0,0 +1,29 @@
package com.binarywang.spring.starter.wxjava.cp.service;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
/**
* 企业微信 {@link WxCpTpService} 所有实例存放类.
*
* @author yl
* created on 2023/10/16
*/
public interface WxCpTpMultiServices {
/**
* 通过租户 Id 获取 WxCpTpService
*
* @param tenantId 租户 Id
* @return WxCpTpService
*/
WxCpTpService getWxCpTpService(String tenantId);
void addWxCpTpService(String tenantId, WxCpTpService wxCpService);
/**
* 根据租户 Id从列表中移除一个 WxCpTpService 实例
*
* @param tenantId 租户 Id
*/
void removeWxCpTpService(String tenantId);
}

View File

@@ -0,0 +1,44 @@
package com.binarywang.spring.starter.wxjava.cp.service;
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 企业微信 {@link WxCpTpMultiServices} 默认实现
*
* @author yl
* created on 2023/10/16
*/
public class WxCpTpMultiServicesImpl implements WxCpTpMultiServices {
private final Map<String, WxCpTpService> services = new ConcurrentHashMap<>();
/**
* 通过租户 Id 获取 WxCpTpService
*
* @param tenantId 租户 Id
* @return WxCpTpService
*/
@Override
public WxCpTpService getWxCpTpService(String tenantId) {
return this.services.get(tenantId);
}
/**
* 根据租户 Id添加一个 WxCpTpService 到列表
*
* @param tenantId 租户 Id
* @param wxCpService WxCpTpService 实例
*/
@Override
public void addWxCpTpService(String tenantId, WxCpTpService wxCpService) {
this.services.put(tenantId, wxCpService);
}
@Override
public void removeWxCpTpService(String tenantId) {
this.services.remove(tenantId);
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.binarywang.spring.starter.wxjava.cp.autoconfigure.WxCpTpMultiAutoConfiguration

View File

@@ -0,0 +1 @@
com.binarywang.spring.starter.wxjava.cp.autoconfigure.WxCpTpMultiAutoConfiguration

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -2,6 +2,7 @@ package com.binarywang.spring.starter.wxjava.miniapp.configuration;
import com.binarywang.spring.starter.wxjava.miniapp.configuration.services.WxMaInJedisConfiguration;
import com.binarywang.spring.starter.wxjava.miniapp.configuration.services.WxMaInMemoryConfiguration;
import com.binarywang.spring.starter.wxjava.miniapp.configuration.services.WxMaInRedisTemplateConfiguration;
import com.binarywang.spring.starter.wxjava.miniapp.configuration.services.WxMaInRedissonConfiguration;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -17,9 +18,10 @@ import org.springframework.context.annotation.Import;
@Configuration
@EnableConfigurationProperties(WxMaMultiProperties.class)
@Import({
WxMaInJedisConfiguration.class,
WxMaInMemoryConfiguration.class,
WxMaInRedissonConfiguration.class,
WxMaInJedisConfiguration.class,
WxMaInMemoryConfiguration.class,
WxMaInRedissonConfiguration.class,
WxMaInRedisTemplateConfiguration.class
})
public class WxMaMultiServiceConfiguration {
}

View File

@@ -108,12 +108,12 @@ public abstract class AbstractWxMaConfiguration {
return wxMaService;
}
private void configApp(WxMaDefaultConfigImpl config, WxMaSingleProperties corpProperties) {
String appId = corpProperties.getAppId();
String appSecret = corpProperties.getAppSecret();
String token = corpProperties.getToken();
String aesKey = corpProperties.getAesKey();
boolean useStableAccessToken = corpProperties.isUseStableAccessToken();
private void configApp(WxMaDefaultConfigImpl config, WxMaSingleProperties properties) {
String appId = properties.getAppId();
String appSecret = properties.getAppSecret();
String token = properties.getToken();
String aesKey = properties.getAesKey();
boolean useStableAccessToken = properties.isUseStableAccessToken();
config.setAppid(appId);
config.setSecret(appSecret);
@@ -123,7 +123,10 @@ public abstract class AbstractWxMaConfiguration {
if (StringUtils.isNotBlank(aesKey)) {
config.setAesKey(aesKey);
}
config.setMsgDataFormat(properties.getMsgDataFormat());
config.useStableAccessToken(useStableAccessToken);
config.setApiHostUrl(StringUtils.trimToNull(properties.getApiHostUrl()));
config.setAccessTokenUrl(StringUtils.trimToNull(properties.getAccessTokenUrl()));
}
private void configHttp(WxMaDefaultConfigImpl config, WxMaMultiProperties.ConfigStorage storage) {

View File

@@ -0,0 +1,43 @@
package com.binarywang.spring.starter.wxjava.miniapp.configuration.services;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiProperties;
import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 自动装配基于 redisTemplate 策略配置
*
* @author <a href="mailto:huangbing0730@gmail">hb0730</a> 2025/9/10
*/
@Configuration
@ConditionalOnProperty(prefix = WxMaMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redis_template")
@RequiredArgsConstructor
public class WxMaInRedisTemplateConfiguration extends AbstractWxMaConfiguration {
private final WxMaMultiProperties wxMaMultiProperties;
private final ApplicationContext applicationContext;
@Bean
public WxMaMultiServices wxMaMultiServices() {
return this.wxMaMultiServices(wxMaMultiProperties);
}
@Override
protected WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties) {
return this.configRedisTemplate(wxMaMultiProperties);
}
private WxMaDefaultConfigImpl configRedisTemplate(WxMaMultiProperties wxMaMultiProperties) {
StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
RedisTemplateWxRedisOps wxRedisOps = new RedisTemplateWxRedisOps(redisTemplate);
return new WxMaRedisBetterConfigImpl(wxRedisOps, wxMaMultiProperties.getConfigStorage().getKeyPrefix());
}
}

View File

@@ -33,8 +33,25 @@ public class WxMaSingleProperties implements Serializable {
*/
private String aesKey;
/**
* 消息格式XML或者JSON.
*/
private String msgDataFormat;
/**
* 是否使用稳定版 Access Token
*/
private boolean useStableAccessToken = false;
/**
* 自定义API主机地址用于替换默认的 https://api.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String apiHostUrl;
/**
* 自定义获取AccessToken地址用于向自定义统一服务获取AccessToken
* 例如http://proxy.company.com:8080/oauth/token
*/
private String accessTokenUrl;
}

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -2,6 +2,8 @@ package com.binarywang.spring.starter.wxjava.miniapp.config.storage;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import org.apache.commons.lang3.StringUtils;
/**
@@ -10,12 +12,15 @@ import org.apache.commons.lang3.StringUtils;
public abstract class AbstractWxMaConfigStorageConfiguration {
protected WxMaDefaultConfigImpl config(WxMaDefaultConfigImpl config, WxMaProperties properties) {
WxMaProperties.ConfigStorage storage = properties.getConfigStorage();
config.setAppid(StringUtils.trimToNull(properties.getAppid()));
config.setSecret(StringUtils.trimToNull(properties.getSecret()));
config.setToken(StringUtils.trimToNull(properties.getToken()));
config.setAesKey(StringUtils.trimToNull(properties.getAesKey()));
config.setMsgDataFormat(StringUtils.trimToNull(properties.getMsgDataFormat()));
config.useStableAccessToken(properties.isUseStableAccessToken());
config.setApiHostUrl(StringUtils.trimToNull(properties.getApiHostUrl()));
config.setAccessTokenUrl(StringUtils.trimToNull(properties.getAccessTokenUrl()));
WxMaProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
@@ -25,6 +30,19 @@ public abstract class AbstractWxMaConfigStorageConfiguration {
config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
}
// 设置自定义的HttpClient超时配置
ApacheHttpClientBuilder clientBuilder = config.getApacheHttpClientBuilder();
if (clientBuilder == null) {
clientBuilder = DefaultApacheHttpClientBuilder.get();
}
if (clientBuilder instanceof DefaultApacheHttpClientBuilder) {
DefaultApacheHttpClientBuilder defaultBuilder = (DefaultApacheHttpClientBuilder) clientBuilder;
defaultBuilder.setConnectionTimeout(storage.getConnectionTimeout());
defaultBuilder.setSoTimeout(storage.getSoTimeout());
defaultBuilder.setConnectionRequestTimeout(storage.getConnectionRequestTimeout());
config.setApacheHttpClientBuilder(defaultBuilder);
}
int maxRetryTimes = configStorageProperties.getMaxRetryTimes();
if (configStorageProperties.getMaxRetryTimes() < 0) {
maxRetryTimes = 0;

View File

@@ -49,6 +49,18 @@ public class WxMaProperties {
*/
private boolean useStableAccessToken = false;
/**
* 自定义API主机地址用于替换默认的 https://api.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String apiHostUrl;
/**
* 自定义获取AccessToken地址用于向自定义统一服务获取AccessToken
* 例如http://proxy.company.com:8080/oauth/token
*/
private String accessTokenUrl;
/**
* 存储策略
*/
@@ -112,6 +124,21 @@ public class WxMaProperties {
* </pre>
*/
private int maxRetryTimes = 5;
/**
* 连接超时时间,单位毫秒
*/
private int connectionTimeout = 5000;
/**
* 读数据超时时间即socketTimeout单位毫秒
*/
private int soTimeout = 5000;
/**
* 从连接池获取链接的超时时间,单位毫秒
*/
private int connectionRequestTimeout = 5000;
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -7,10 +7,7 @@ import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServicesImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
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.api.impl.*;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.WxMpHostConfig;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
@@ -91,6 +88,9 @@ public abstract class AbstractWxMpConfiguration {
case HTTP_CLIENT:
wxMpService = new WxMpServiceHttpClientImpl();
break;
case HTTP_COMPONENTS:
wxMpService = new WxMpServiceHttpComponentsImpl();
break;
default:
wxMpService = new WxMpServiceImpl();
break;

View File

@@ -142,6 +142,10 @@ public class WxMpMultiProperties implements Serializable {
* HttpClient
*/
HTTP_CLIENT,
/**
* HttpComponents
*/
HTTP_COMPONENTS,
/**
* OkHttp
*/

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -9,6 +9,8 @@ import lombok.extern.slf4j.Slf4j;
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.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.WxMpHostConfig;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
@@ -122,6 +124,19 @@ public class WxMpStorageAutoConfiguration {
config.setSecret(properties.getSecret());
config.setToken(properties.getToken());
config.setAesKey(properties.getAesKey());
WxMpProperties.ConfigStorage storage = properties.getConfigStorage();
// 设置自定义的HttpClient超时配置
ApacheHttpClientBuilder clientBuilder = config.getApacheHttpClientBuilder();
if (clientBuilder == null) {
clientBuilder = DefaultApacheHttpClientBuilder.get();
}
if (clientBuilder instanceof DefaultApacheHttpClientBuilder) {
DefaultApacheHttpClientBuilder defaultBuilder = (DefaultApacheHttpClientBuilder) clientBuilder;
defaultBuilder.setConnectionTimeout(storage.getConnectionTimeout());
defaultBuilder.setSoTimeout(storage.getSoTimeout());
defaultBuilder.setConnectionRequestTimeout(storage.getConnectionRequestTimeout());
config.setApacheHttpClientBuilder(defaultBuilder);
}
config.setUseStableAccessToken(wxMpProperties.isUseStableAccessToken());
config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());

View File

@@ -102,6 +102,21 @@ public class WxMpProperties {
*/
private String httpProxyPassword;
/**
* 连接超时时间,单位毫秒
*/
private int connectionTimeout = 5000;
/**
* 读数据超时时间即socketTimeout单位毫秒
*/
private int soTimeout = 5000;
/**
* 从连接池获取链接的超时时间,单位毫秒
*/
private int connectionRequestTimeout = 5000;
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,7 +1,10 @@
package com.binarywang.spring.starter.wxjava.open.config.storage;
import com.binarywang.spring.starter.wxjava.open.properties.WxOpenProperties;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
import org.apache.commons.lang3.StringUtils;
/**
* @author yl
@@ -28,6 +31,24 @@ public abstract class AbstractWxOpenConfigStorageConfiguration {
}
config.setRetrySleepMillis(retrySleepMillis);
config.setMaxRetryTimes(maxRetryTimes);
// 设置URL配置
config.setApiHostUrl(StringUtils.trimToNull(properties.getApiHostUrl()));
config.setAccessTokenUrl(StringUtils.trimToNull(properties.getAccessTokenUrl()));
// 设置自定义的HttpClient超时配置
ApacheHttpClientBuilder clientBuilder = config.getApacheHttpClientBuilder();
if (clientBuilder == null) {
clientBuilder = DefaultApacheHttpClientBuilder.get();
}
if (clientBuilder instanceof DefaultApacheHttpClientBuilder) {
DefaultApacheHttpClientBuilder defaultBuilder = (DefaultApacheHttpClientBuilder) clientBuilder;
defaultBuilder.setConnectionTimeout(storage.getConnectionTimeout());
defaultBuilder.setSoTimeout(storage.getSoTimeout());
defaultBuilder.setConnectionRequestTimeout(storage.getConnectionRequestTimeout());
config.setApacheHttpClientBuilder(defaultBuilder);
}
return config;
}
}

View File

@@ -40,6 +40,18 @@ public class WxOpenProperties {
*/
private String aesKey;
/**
* 自定义API主机地址用于替换默认的 https://api.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String apiHostUrl;
/**
* 自定义获取AccessToken地址用于向自定义统一服务获取AccessToken
* 例如http://proxy.company.com:8080/oauth/token
*/
private String accessTokenUrl;
/**
* 存储策略.
*/
@@ -108,6 +120,21 @@ public class WxOpenProperties {
*/
private int maxRetryTimes = 5;
/**
* 连接超时时间,单位毫秒
*/
private int connectionTimeout = 5000;
/**
* 读数据超时时间即socketTimeout单位毫秒
*/
private int soTimeout = 5000;
/**
* 从连接池获取链接的超时时间,单位毫秒
*/
private int connectionRequestTimeout = 5000;
}
public enum StorageType {

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -50,15 +50,18 @@ public class WxPayAutoConfiguration {
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
payConfig.setUseSandboxEnv(this.properties.isUseSandboxEnv());
payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl()));
//以下是apiv3以及支付分相关
payConfig.setServiceId(StringUtils.trimToNull(this.properties.getServiceId()));
payConfig.setPayScoreNotifyUrl(StringUtils.trimToNull(this.properties.getPayScoreNotifyUrl()));
payConfig.setPayScorePermissionNotifyUrl(StringUtils.trimToNull(this.properties.getPayScorePermissionNotifyUrl()));
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()));
payConfig.setPublicKeyId(StringUtils.trimToNull(this.properties.getPublicKeyId()));
payConfig.setPublicKeyPath(StringUtils.trimToNull(this.properties.getPublicKeyPath()));
payConfig.setApiHostUrl(StringUtils.trimToNull(this.properties.getApiHostUrl()));
wxPayService.setConfig(payConfig);
return wxPayService;

View File

@@ -59,11 +59,21 @@ public class WxPayProperties {
*/
private String apiv3Key;
/**
* 微信支付异步回调地址通知url必须为直接可访问的url不能携带参数
*/
private String notifyUrl;
/**
* 微信支付分回调地址
*/
private String payScoreNotifyUrl;
/**
* 微信支付分授权回调地址
*/
private String payScorePermissionNotifyUrl;
/**
* apiv3 商户apiclient_key.pem
*/
@@ -90,4 +100,10 @@ public class WxPayProperties {
*/
private boolean useSandboxEnv;
/**
* 自定义API主机地址用于替换默认的 https://api.mch.weixin.qq.com
* 例如http://proxy.company.com:8080
*/
private String apiHostUrl;
}

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -20,6 +20,7 @@ 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.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
@@ -80,7 +81,7 @@ public class WxQidianStorageAutoConfiguration {
}
private WxQidianConfigStorage jedisConfigStorage() {
Pool jedisPool;
Pool<Jedis> jedisPool;
if (StringUtils.isNotEmpty(redisHost) || StringUtils.isNotEmpty(redisHost2)) {
jedisPool = getJedisPool();
} else {
@@ -136,7 +137,7 @@ public class WxQidianStorageAutoConfiguration {
}
}
private Pool getJedisPool() {
private Pool<Jedis> getJedisPool() {
WxQidianProperties.ConfigStorage storage = wxQidianProperties.getConfigStorage();
RedisProperties redis = storage.getRedis();

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-graal</artifactId>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.4.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-channel</artifactId>
@@ -14,7 +14,7 @@
<description>微信视频号/微信小店 Java SDK</description>
<properties>
<jackson.version>2.18.1</jackson.version>
<jackson.version>2.18.4</jackson.version>
</properties>
<dependencies>
@@ -29,6 +29,11 @@
<artifactId>jodd-http</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>

View File

@@ -131,5 +131,5 @@ public interface BaseWxChannelService extends WxService {
*
* @return . request http
*/
RequestHttp getRequestHttp();
RequestHttp<?, ?> getRequestHttp();
}

View File

@@ -3,6 +3,7 @@ package me.chanjar.weixin.channel.api;
import java.util.List;
import me.chanjar.weixin.channel.bean.after.AfterSaleInfoResponse;
import me.chanjar.weixin.channel.bean.after.AfterSaleListParam;
import me.chanjar.weixin.channel.bean.after.AfterSaleListResponse;
import me.chanjar.weixin.channel.bean.after.AfterSaleReasonResponse;
import me.chanjar.weixin.channel.bean.after.AfterSaleRejectReasonResponse;
@@ -26,10 +27,22 @@ public interface WxChannelAfterSaleService {
* @return 售后单列表
*
* @throws WxErrorException 异常
* @deprecated 使用 {@link WxChannelAfterSaleService#listIds(AfterSaleListParam)}
*/
@Deprecated
AfterSaleListResponse listIds(Long beginCreateTime, Long endCreateTime, String nextKey)
throws WxErrorException;
/**
* 获取售后单列表
*
* @param param 参数
* @return 售后单列表
*
* @throws WxErrorException 异常
*/
AfterSaleListResponse listIds(AfterSaleListParam param) throws WxErrorException;
/**
* 获取售后单详情
*

View File

@@ -34,6 +34,17 @@ public interface WxChannelOrderService {
*/
OrderInfoResponse getOrder(String orderId) throws WxErrorException;
/**
* 获取订单详情
*
* @param orderId 订单id
* @param encodeSensitiveInfo 是否编码敏感信息
* @return 订单详情
*
* @throws WxErrorException 异常
*/
OrderInfoResponse getOrder(String orderId, Boolean encodeSensitiveInfo) throws WxErrorException;
/**
* 获取订单列表
*
@@ -128,7 +139,7 @@ public interface WxChannelOrderService {
WxChannelBaseResponse closeOrder(String orderId);
/**
* 获取快递公司列表
* 获取快递公司列表-旧
*
* @return 快递公司列表
*
@@ -136,6 +147,16 @@ public interface WxChannelOrderService {
*/
DeliveryCompanyResponse listDeliveryCompany() throws WxErrorException;
/**
* 获取快递公司列表
*
* @param ewaybillOnly 是否仅返回支持电子面单功能的快递公司
* @return 快递公司列表
*
* @throws WxErrorException 异常
*/
DeliveryCompanyResponse listDeliveryCompany(Boolean ewaybillOnly) throws WxErrorException;
/**
* 订单发货
*

View File

@@ -15,43 +15,84 @@ public interface WxLeaguePromoterService {
/**
* 新增达人
*
* @param finderId 视频号finder_id
* @param finderId 视频号finder_id,待废除
* @return 结果
* @deprecated 使用 {@link #addPromoterV2(String)}
*/
@Deprecated
WxChannelBaseResponse addPromoter(String finderId) throws WxErrorException;
/**
* 编辑达人
*
* @param finderId 视频号finder_id
* @param finderId 视频号finder_id,待废除
* @param type 操作 1取消邀请 2结束合作
* @return 结果
* @deprecated 使用 {@link #updatePromoterV2(String, int)}
*/
@Deprecated
WxChannelBaseResponse updatePromoter(String finderId, int type) throws WxErrorException;
/**
* 删除达人
*
* @param finderId 视频号finder_id
* @param finderId 视频号finder_id,待废除
* @return 结果
* @deprecated 使用 {@link #deletePromoterV2(String)}
*/
@Deprecated
WxChannelBaseResponse deletePromoter(String finderId) throws WxErrorException;
/**
* 获取达人详情信息
*
* @param finderId 视频号finder_id
* @param finderId 视频号finder_id,待废除
* @return 结果
* @deprecated 使用 {@link #getPromoterInfoV2(String)}
*/
@Deprecated
PromoterInfoResponse getPromoterInfo(String finderId) throws WxErrorException;
/**
* 获取达人列表
*
* @param pageIndex 页面下标下标从1开始默认为1
* @param pageSize 单页达人数不超过200
* @param status 拉取该状态下的达人列表
* @return 结果
*/
PromoterListResponse listPromoter(Integer pageIndex, Integer pageSize, Integer status) throws WxErrorException;
/**
* 新增达人
*
* @param promoterId 达人带货id
* @return 结果
*/
WxChannelBaseResponse addPromoterV2(String promoterId) throws WxErrorException;
/**
* 编辑达人
*
* @param promoterId 达人带货id
* @param type 操作 1取消邀请 2结束合作
* @return 结果
*/
WxChannelBaseResponse updatePromoterV2(String promoterId, int type) throws WxErrorException;
/**
* 删除达人
*
* @param promoterId 达人带货id
* @return 结果
*/
WxChannelBaseResponse deletePromoterV2(String promoterId) throws WxErrorException;
/**
* 获取达人详情信息
*
* @param promoterId 达人带货id
* @return 结果
*/
PromoterInfoResponse getPromoterInfoV2(String promoterId) throws WxErrorException;
/**
* 获取达人列表
*
* @param pageIndex 页面下标下标从1开始默认为1
* @param pageSize 单页达人数不超过200
* @param status 拉取该状态下的达人列表
* @return 结果
*/
PromoterListResponse listPromoter(Integer pageIndex, Integer pageSize, Integer status) throws WxErrorException;
}

View File

@@ -66,7 +66,7 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
private int maxRetryTimes = 5;
@Override
public RequestHttp getRequestHttp() {
public RequestHttp<H, P> getRequestHttp() {
return this;
}
@@ -75,7 +75,7 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
try {
return SHA1.gen(this.getConfig().getToken(), timestamp, nonce).equals(signature);
} catch (Exception e) {
log.error("Checking signature failed, and the reason is :" + e.getMessage());
log.error("Checking signature failed, and the reason is :{}", e.getMessage());
return false;
}
}
@@ -276,7 +276,7 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
* @throws WxErrorException 异常
*/
protected String extractAccessToken(String resultContent) throws WxErrorException {
log.debug("access-token response: " + resultContent);
log.debug("access-token response: {}", resultContent);
WxChannelConfig config = this.getConfig();
WxError error = WxError.fromJson(resultContent, WxType.Channel);
if (error.getErrorCode() != 0) {

View File

@@ -28,7 +28,7 @@ import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Assist
public class WxAssistantServiceImpl implements WxAssistantService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
@Override
public WxChannelBaseResponse addWindowProduct(AddWindowProductRequest req) throws WxErrorException {
String resJson = shopService.post(ADD_WINDOW_PRODUCT_URL, "{}");

View File

@@ -29,9 +29,9 @@ import me.chanjar.weixin.common.error.WxErrorException;
public class WxChannelAddressServiceImpl implements WxChannelAddressService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelAddressServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelAddressServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}

View File

@@ -23,16 +23,22 @@ import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Compla
public class WxChannelAfterSaleServiceImpl implements WxChannelAfterSaleService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelAfterSaleServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelAfterSaleServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}
@Override
public AfterSaleListResponse listIds(Long beginCreateTime, Long endCreateTime, String nextKey)
throws WxErrorException {
AfterSaleListParam param = new AfterSaleListParam(beginCreateTime, endCreateTime, nextKey);
AfterSaleListParam param = new AfterSaleListParam(beginCreateTime, endCreateTime, null, null, nextKey);
String resJson = shopService.post(AFTER_SALE_LIST_URL, param);
return ResponseUtils.decode(resJson, AfterSaleListResponse.class);
}
@Override
public AfterSaleListResponse listIds(AfterSaleListParam param) throws WxErrorException {
String resJson = shopService.post(AFTER_SALE_LIST_URL, param);
return ResponseUtils.decode(resJson, AfterSaleListResponse.class);
}

View File

@@ -31,9 +31,9 @@ import me.chanjar.weixin.common.util.http.RequestExecutor;
public class WxChannelBasicServiceImpl implements WxChannelBasicService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelBasicServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelBasicServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}
@@ -56,7 +56,7 @@ public class WxChannelBasicServiceImpl implements WxChannelBasicService {
public ChannelImageInfo uploadImg(int respType, File file, int height, int width) throws WxErrorException {
String url = IMG_UPLOAD_URL + "?upload_type=0&resp_type=" + respType + "&height=" + height + "&width=" + width;
RequestExecutor<String, File> executor = ChannelFileUploadRequestExecutor.create(shopService);
String resJson = (String) shopService.execute(executor, url, file);
String resJson = shopService.execute(executor, url, file);
UploadImageResponse response = ResponseUtils.decode(resJson, UploadImageResponse.class);
return response.getImgInfo();
}
@@ -64,19 +64,19 @@ public class WxChannelBasicServiceImpl implements WxChannelBasicService {
@Override
public QualificationFileResponse uploadQualificationFile(File file) throws WxErrorException {
RequestExecutor<String, File> executor = ChannelFileUploadRequestExecutor.create(shopService);
String resJson = (String) shopService.execute(executor, UPLOAD_QUALIFICATION_FILE, file);
String resJson = shopService.execute(executor, UPLOAD_QUALIFICATION_FILE, file);
return ResponseUtils.decode(resJson, QualificationFileResponse.class);
}
@Override
public ChannelImageResponse getImg(String mediaId) throws WxErrorException {
String appId = shopService.getConfig().getAppid();
ChannelImageResponse rs = null;
ChannelImageResponse rs;
try {
String url = GET_IMG_URL + "?media_id=" + mediaId;
RequestExecutor<ChannelImageResponse, String> executor = ChannelMediaDownloadRequestExecutor.create(shopService,
Files.createTempDirectory("wxjava-channel-" + appId).toFile());
rs = (ChannelImageResponse) shopService.execute(executor, url, null);
rs = shopService.execute(executor, url, null);
} catch (IOException e) {
throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e);
}

View File

@@ -33,9 +33,9 @@ import me.chanjar.weixin.common.error.WxErrorException;
public class WxChannelBrandServiceImpl implements WxChannelBrandService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelBrandServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelBrandServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}

View File

@@ -37,9 +37,9 @@ import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
public class WxChannelCategoryServiceImpl implements WxChannelCategoryService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelCategoryServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelCategoryServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}
@@ -56,7 +56,7 @@ public class WxChannelCategoryServiceImpl implements WxChannelCategoryService {
try {
pid = Long.parseLong(parentId);
} catch (Throwable e) {
log.error("parentId必须为数字, " + parentId, e);
log.error("parentId必须为数字, {}", parentId, e);
return Collections.emptyList();
}
String reqJson = "{\"f_cat_id\": " + pid + "}";
@@ -80,7 +80,7 @@ public class WxChannelCategoryServiceImpl implements WxChannelCategoryService {
try {
catId = Long.parseLong(id);
} catch (Throwable e) {
log.error("id必须为数字, " + id, e);
log.error("id必须为数字, {}", id, e);
return ResponseUtils.internalError(CategoryDetailResult.class);
}
String reqJson = "{\"cat_id\": " + catId + "}";

View File

@@ -20,9 +20,9 @@ public class WxChannelCompassFinderServiceImpl implements WxChannelCompassFinder
/**
* 微信商店服务
*/
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelCompassFinderServiceImpl(BaseWxChannelServiceImpl shopService) {this.shopService = shopService;}
public WxChannelCompassFinderServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {this.shopService = shopService;}
@Override
public OverallResponse getOverall(String ds) throws WxErrorException {

View File

@@ -41,9 +41,9 @@ public class WxChannelCompassShopServiceImpl implements WxChannelCompassShopServ
/**
* 微信商店服务
*/
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelCompassShopServiceImpl(BaseWxChannelServiceImpl shopService) {this.shopService = shopService;}
public WxChannelCompassShopServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {this.shopService = shopService;}
@Override
public ShopOverallResponse getShopOverall(String ds) throws WxErrorException {

View File

@@ -35,9 +35,9 @@ import me.chanjar.weixin.common.error.WxErrorException;
public class WxChannelCouponServiceImpl implements WxChannelCouponService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelCouponServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelCouponServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}

View File

@@ -24,9 +24,9 @@ import me.chanjar.weixin.common.error.WxErrorException;
@Slf4j
public class WxChannelFreightTemplateServiceImpl implements WxChannelFreightTemplateService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelFreightTemplateServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelFreightTemplateServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}

View File

@@ -54,9 +54,9 @@ public class WxChannelFundServiceImpl implements WxChannelFundService {
/** 微信商店服务 */
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
public WxChannelFundServiceImpl(BaseWxChannelServiceImpl shopService) {
public WxChannelFundServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
this.shopService = shopService;
}

View File

@@ -27,10 +27,10 @@ public class WxChannelLiveDashboardServiceImpl implements WxChannelLiveDashboard
/**
* 微信商店服务
*/
private final BaseWxChannelServiceImpl shopService;
private final BaseWxChannelServiceImpl<?, ?> shopService;
private final ObjectMapper objectMapper = new ObjectMapper();
public WxChannelLiveDashboardServiceImpl(BaseWxChannelServiceImpl shopService) {this.shopService = shopService;}
public WxChannelLiveDashboardServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {this.shopService = shopService;}
@Override
public LiveListResponse getLiveList(Long ds) throws WxErrorException {

Some files were not shown because too many files have changed in this diff Show More