Skip to content

Commit da31b5d

Browse files
committed
feat: 拆分微信小店独立模块
1 parent ce51e03 commit da31b5d

1,242 files changed

Lines changed: 34827 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# 微信小店模块迁移指南
2+
3+
从本版本开始,微信小店能力由独立的 `weixin-java-store` 模块提供。原有
4+
`weixin-java-channel` 小店 API 仍可继续使用,且行为不变;它们已标记为
5+
`@Deprecated`,以便在后续版本中引导迁移。
6+
7+
## 依赖替换
8+
9+
将:
10+
11+
```xml
12+
<dependency>
13+
<groupId>com.github.binarywang</groupId>
14+
<artifactId>weixin-java-channel</artifactId>
15+
</dependency>
16+
```
17+
18+
替换或并行增加为:
19+
20+
```xml
21+
<dependency>
22+
<groupId>com.github.binarywang</groupId>
23+
<artifactId>weixin-java-store</artifactId>
24+
</dependency>
25+
```
26+
27+
新模块不依赖 `weixin-java-channel`;两者可以同时引入,适合按业务逐步迁移。
28+
29+
## 包名与入口替换
30+
31+
| 旧入口 | 新入口 |
32+
| --- | --- |
33+
| `me.chanjar.weixin.channel.api.WxChannelService` | `com.binarywang.wxjava.store.api.WxStoreService` |
34+
| `WxChannelProductService` | `WxStoreProductService` |
35+
| `WxChannelOrderService` | `WxStoreOrderService` |
36+
| `WxChannelAfterSaleService` | `WxStoreAfterSaleService` |
37+
| `WxChannelFundService` | `WxStoreFundService` |
38+
| `WxChannelWarehouseService` | `WxStoreWarehouseService` |
39+
40+
所有新模型均位于 `com.binarywang.wxjava.store.bean.*`。旧模型不会改包或删除;
41+
由于新旧模型是独立类型,业务层应在迁移边界显式完成类型转换。
42+
43+
## 框架集成
44+
45+
微信小店提供独立的 Spring Boot 与 Solon 集成模块,并统一使用 `wx.store`
46+
作为配置前缀:
47+
48+
| 场景 | 模块 |
49+
| --- | --- |
50+
| Spring Boot 单账号 | `wx-java-store-spring-boot-starter` |
51+
| Spring Boot 多账号 | `wx-java-store-multi-spring-boot-starter` |
52+
| Solon 单账号 | `wx-java-store-solon-plugin` |
53+
| Solon 多账号 | `wx-java-store-multi-solon-plugin` |
54+
55+
原有 Channel Starter/Plugin 及其 `wx.channel` 配置保持不变,可与新的
56+
Store Starter/Plugin 并存。
57+
58+
## 不属于 store 的能力
59+
60+
视频号直播、Finder、联盟分销、留资组件与达人罗盘继续使用
61+
`weixin-java-channel`,不会被弃用,也不会由 `WxStoreService` 暴露。

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
<module>weixin-java-qidian</module>
127127
<module>weixin-java-aispeech</module>
128128
<module>weixin-java-channel</module>
129+
<module>weixin-java-store</module>
129130
<module>spring-boot-starters</module>
130131
<module>solon-plugins</module>
131132
<module>wx-java-bom</module>

solon-plugins/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
<module>wx-java-cp-solon-plugin</module>
3030
<module>wx-java-channel-solon-plugin</module>
3131
<module>wx-java-channel-multi-solon-plugin</module>
32+
<module>wx-java-store-solon-plugin</module>
33+
<module>wx-java-store-multi-solon-plugin</module>
3234
</modules>
3335

3436
<dependencies>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# wx-java-store-multi-solon-plugin
2+
3+
## 快速开始
4+
5+
1. 引入依赖
6+
```xml
7+
<dependencies>
8+
<dependency>
9+
<groupId>com.github.binarywang</groupId>
10+
<artifactId>wx-java-store-multi-solon-plugin</artifactId>
11+
<version>${version}</version>
12+
</dependency>
13+
14+
<!-- 配置存储方式为jedis 则引入jedis -->
15+
<dependency>
16+
<groupId>redis.clients</groupId>
17+
<artifactId>jedis</artifactId>
18+
<version>${jedis.version}</version>
19+
</dependency>
20+
21+
<!-- 配置存储方式为redisson 则引入redisson -->
22+
<dependency>
23+
<groupId>org.redisson</groupId>
24+
<artifactId>redisson</artifactId>
25+
<version>${redisson.version}</version>
26+
</dependency>
27+
</dependencies>
28+
```
29+
2. 添加配置(app.properties)
30+
```properties
31+
# 视频号配置
32+
## 应用 1 配置(必填)
33+
wx.store.apps.tenantId1.app-id=@appId
34+
wx.store.apps.tenantId1.secret=@secret
35+
## 选填
36+
wx.store.apps.tenantId1.use-stable-access-token=false
37+
wx.store.apps.tenantId1.token=
38+
wx.store.apps.tenantId1.aes-key=
39+
## 应用 2 配置(必填)
40+
wx.store.apps.tenantId2.app-id=@appId
41+
wx.store.apps.tenantId2.secret=@secret
42+
## 选填
43+
wx.store.apps.tenantId2.use-stable-access-token=false
44+
wx.store.apps.tenantId2.token=
45+
wx.store.apps.tenantId2.aes-key=
46+
47+
# ConfigStorage 配置(选填)
48+
## 配置类型: memory(默认), jedis, redisson, redis_template
49+
wx.store.config-storage.type=memory
50+
## 相关redis前缀配置: wx:store:multi(默认)
51+
wx.store.config-storage.key-prefix=wx:store:multi
52+
wx.store.config-storage.redis.host=127.0.0.1
53+
wx.store.config-storage.redis.port=6379
54+
wx.store.config-storage.redis.password=123456
55+
56+
# http 客户端配置(选填)
57+
## # http客户端类型: http_client(默认)
58+
wx.store.config-storage.http-client-type=http_client
59+
wx.store.config-storage.http-proxy-host=
60+
wx.store.config-storage.http-proxy-port=
61+
wx.store.config-storage.http-proxy-username=
62+
wx.store.config-storage.http-proxy-password=
63+
## 最大重试次数,默认:5 次,如果小于 0,则为 0
64+
wx.store.config-storage.max-retry-times=5
65+
## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
66+
wx.store.config-storage.retry-sleep-millis=1000
67+
```
68+
3. 自动注入的类型:`WxStoreMultiServices`
69+
70+
4. 使用样例
71+
72+
```java
73+
import com.binarywang.solon.wxjava.store.service.WxStoreMultiServices;
74+
import com.binarywang.wxjava.store.api.WxStoreService;
75+
import com.binarywang.wxjava.store.api.WxFinderLiveService;
76+
import com.binarywang.wxjava.store.bean.lead.component.response.FinderAttrResponse;
77+
import me.chanjar.weixin.common.error.WxErrorException;
78+
import org.noear.solon.annotation.Component;
79+
import org.noear.solon.annotation.Inject;
80+
81+
@Component
82+
public class DemoService {
83+
@Inject
84+
private WxStoreMultiServices wxStoreMultiServices;
85+
86+
public void test() throws WxErrorException {
87+
// 应用 1 的 WxStoreService
88+
WxStoreService wxStoreService1 = wxStoreMultiServices.getWxStoreService("tenantId1");
89+
WxFinderLiveService finderLiveService = wxStoreService1.getFinderLiveService();
90+
FinderAttrResponse response1 = finderLiveService.getFinderAttrByAppid();
91+
// todo ...
92+
93+
// 应用 2 的 WxStoreService
94+
WxStoreService wxStoreService2 = wxStoreMultiServices.getWxStoreService("tenantId2");
95+
WxFinderLiveService finderLiveService2 = wxStoreService2.getFinderLiveService();
96+
FinderAttrResponse response2 = finderLiveService2.getFinderAttrByAppid();
97+
// todo ...
98+
99+
// 应用 3 的 WxStoreService
100+
WxStoreService wxStoreService3 = wxStoreMultiServices.getWxStoreService("tenantId3");
101+
// 判断是否为空
102+
if (wxStoreService3 == null) {
103+
// todo wxStoreService3 为空,请先配置 tenantId3 微信小店应用参数
104+
return;
105+
}
106+
WxFinderLiveService finderLiveService3 = wxStoreService3.getFinderLiveService();
107+
FinderAttrResponse response3 = finderLiveService3.getFinderAttrByAppid();
108+
// todo ...
109+
}
110+
}
111+
```
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>wx-java-solon-plugins</artifactId>
7+
<groupId>com.github.binarywang</groupId>
8+
<version>4.8.6.B</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>wx-java-store-multi-solon-plugin</artifactId>
13+
<name>WxJava - Solon Plugin for Store::支持多账号配置</name>
14+
<description>微信小店开发的 Solon Plugin::支持多账号配置</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>com.github.binarywang</groupId>
19+
<artifactId>weixin-java-store</artifactId>
20+
<version>${project.version}</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>redis.clients</groupId>
24+
<artifactId>jedis</artifactId>
25+
<scope>provided</scope>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.redisson</groupId>
29+
<artifactId>redisson</artifactId>
30+
<scope>provided</scope>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.jodd</groupId>
34+
<artifactId>jodd-http</artifactId>
35+
<scope>provided</scope>
36+
</dependency>
37+
<dependency>
38+
<groupId>com.squareup.okhttp3</groupId>
39+
<artifactId>okhttp</artifactId>
40+
<scope>provided</scope>
41+
</dependency>
42+
</dependencies>
43+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.binarywang.solon.wxjava.store.configuration.services;
2+
3+
import com.binarywang.solon.wxjava.store.enums.HttpClientType;
4+
import com.binarywang.solon.wxjava.store.properties.WxStoreMultiProperties;
5+
import com.binarywang.solon.wxjava.store.properties.WxStoreSingleProperties;
6+
import com.binarywang.solon.wxjava.store.service.WxStoreMultiServices;
7+
import com.binarywang.solon.wxjava.store.service.WxStoreMultiServicesImpl;
8+
import lombok.RequiredArgsConstructor;
9+
import lombok.extern.slf4j.Slf4j;
10+
import com.binarywang.wxjava.store.api.WxStoreService;
11+
import com.binarywang.wxjava.store.api.impl.WxStoreServiceHttpComponentsImpl;
12+
import com.binarywang.wxjava.store.api.impl.WxStoreServiceHttpClientImpl;
13+
import com.binarywang.wxjava.store.api.impl.WxStoreServiceImpl;
14+
import com.binarywang.wxjava.store.config.WxStoreConfig;
15+
import com.binarywang.wxjava.store.config.impl.WxStoreDefaultConfigImpl;
16+
import org.apache.commons.lang3.StringUtils;
17+
18+
import java.util.Collection;
19+
import java.util.Map;
20+
import java.util.Set;
21+
import java.util.stream.Collectors;
22+
23+
/**
24+
* WxStoreConfigStorage 抽象配置类
25+
*
26+
* @author <a href="https://github.com/Winnie-by996">Winnie</a> 2024/9/13
27+
* @author noear
28+
*/
29+
@RequiredArgsConstructor
30+
@Slf4j
31+
public abstract class AbstractWxStoreConfiguration {
32+
protected WxStoreMultiServices wxStoreMultiServices(WxStoreMultiProperties wxStoreMultiProperties) {
33+
Map<String, WxStoreSingleProperties> appsMap = wxStoreMultiProperties.getApps();
34+
if (appsMap == null || appsMap.isEmpty()) {
35+
log.warn("微信小店应用参数未配置,通过 WxStoreMultiServices#getWxStoreService(\"tenantId\")获取实例将返回空");
36+
return new WxStoreMultiServicesImpl();
37+
}
38+
/**
39+
* 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
40+
*
41+
* 查看 {@link com.binarywang.wxjava.store.config.impl.WxStoreRedisConfigImpl#setAppid(String)}
42+
*/
43+
Collection<WxStoreSingleProperties> apps = appsMap.values();
44+
if (apps.size() > 1) {
45+
// 校验 appId 是否唯一
46+
boolean multi = apps.stream()
47+
// 没有 appId,如果不判断是否为空,这里会报 NPE 异常
48+
.collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
49+
.entrySet().stream().anyMatch(e -> e.getValue() > 1);
50+
if (multi) {
51+
throw new RuntimeException("请确保微信小店配置 appId 的唯一性");
52+
}
53+
}
54+
WxStoreMultiServicesImpl services = new WxStoreMultiServicesImpl();
55+
56+
Set<Map.Entry<String, WxStoreSingleProperties>> entries = appsMap.entrySet();
57+
for (Map.Entry<String, WxStoreSingleProperties> entry : entries) {
58+
String tenantId = entry.getKey();
59+
WxStoreSingleProperties wxStoreSingleProperties = entry.getValue();
60+
WxStoreDefaultConfigImpl storage = this.wxStoreConfigStorage(wxStoreMultiProperties);
61+
this.configApp(storage, wxStoreSingleProperties);
62+
this.configHttp(storage, wxStoreMultiProperties.getConfigStorage());
63+
WxStoreService wxStoreService = this.wxStoreService(storage, wxStoreMultiProperties);
64+
services.addWxStoreService(tenantId, wxStoreService);
65+
}
66+
return services;
67+
}
68+
69+
/**
70+
* 配置 WxStoreDefaultConfigImpl
71+
*
72+
* @param wxStoreMultiProperties 参数
73+
* @return WxStoreDefaultConfigImpl
74+
*/
75+
protected abstract WxStoreDefaultConfigImpl wxStoreConfigStorage(WxStoreMultiProperties wxStoreMultiProperties);
76+
77+
public WxStoreService wxStoreService(WxStoreConfig wxStoreConfig, WxStoreMultiProperties wxStoreMultiProperties) {
78+
WxStoreMultiProperties.ConfigStorage storage = wxStoreMultiProperties.getConfigStorage();
79+
HttpClientType httpClientType = storage.getHttpClientType();
80+
WxStoreService wxStoreService;
81+
switch (httpClientType) {
82+
// case OK_HTTP:
83+
// wxStoreService = new WxStoreServiceOkHttpImpl(false, false);
84+
// break;
85+
case HTTP_CLIENT:
86+
wxStoreService = new WxStoreServiceHttpClientImpl();
87+
break;
88+
case HTTP_COMPONENTS:
89+
wxStoreService = new WxStoreServiceHttpComponentsImpl();
90+
break;
91+
default:
92+
wxStoreService = new WxStoreServiceImpl();
93+
break;
94+
}
95+
96+
wxStoreService.setConfig(wxStoreConfig);
97+
int maxRetryTimes = storage.getMaxRetryTimes();
98+
if (maxRetryTimes < 0) {
99+
maxRetryTimes = 0;
100+
}
101+
int retrySleepMillis = storage.getRetrySleepMillis();
102+
if (retrySleepMillis < 0) {
103+
retrySleepMillis = 1000;
104+
}
105+
wxStoreService.setRetrySleepMillis(retrySleepMillis);
106+
wxStoreService.setMaxRetryTimes(maxRetryTimes);
107+
return wxStoreService;
108+
}
109+
110+
private void configApp(WxStoreDefaultConfigImpl config, WxStoreSingleProperties wxStoreSingleProperties) {
111+
String appId = wxStoreSingleProperties.getAppId();
112+
String appSecret = wxStoreSingleProperties.getSecret();
113+
String token = wxStoreSingleProperties.getToken();
114+
String aesKey = wxStoreSingleProperties.getAesKey();
115+
boolean useStableAccessToken = wxStoreSingleProperties.isUseStableAccessToken();
116+
117+
config.setAppid(appId);
118+
config.setSecret(appSecret);
119+
if (StringUtils.isNotBlank(token)) {
120+
config.setToken(token);
121+
}
122+
if (StringUtils.isNotBlank(aesKey)) {
123+
config.setAesKey(aesKey);
124+
}
125+
config.setStableAccessToken(useStableAccessToken);
126+
}
127+
128+
private void configHttp(WxStoreDefaultConfigImpl config, WxStoreMultiProperties.ConfigStorage storage) {
129+
String httpProxyHost = storage.getHttpProxyHost();
130+
Integer httpProxyPort = storage.getHttpProxyPort();
131+
String httpProxyUsername = storage.getHttpProxyUsername();
132+
String httpProxyPassword = storage.getHttpProxyPassword();
133+
if (StringUtils.isNotBlank(httpProxyHost)) {
134+
config.setHttpProxyHost(httpProxyHost);
135+
if (httpProxyPort != null) {
136+
config.setHttpProxyPort(httpProxyPort);
137+
}
138+
if (StringUtils.isNotBlank(httpProxyUsername)) {
139+
config.setHttpProxyUsername(httpProxyUsername);
140+
}
141+
if (StringUtils.isNotBlank(httpProxyPassword)) {
142+
config.setHttpProxyPassword(httpProxyPassword);
143+
}
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)