Skip to content

Commit 1246de1

Browse files
authored
🆕 #4102 【视频号】 支持微信小店电子面单服务
1 parent 7795387 commit 1246de1

32 files changed

Lines changed: 782 additions & 3 deletions
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package me.chanjar.weixin.channel.api;
2+
3+
import java.util.List;
4+
import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
5+
import me.chanjar.weixin.channel.bean.ewaybill.AccountInfoResponse;
6+
import me.chanjar.weixin.channel.bean.ewaybill.AddSubOrderRequest;
7+
import me.chanjar.weixin.channel.bean.ewaybill.CreateOrderRequest;
8+
import me.chanjar.weixin.channel.bean.ewaybill.CreateOrderResponse;
9+
import me.chanjar.weixin.channel.bean.ewaybill.DeliveryListResponse;
10+
import me.chanjar.weixin.channel.bean.ewaybill.PrintOrderRequest;
11+
import me.chanjar.weixin.channel.bean.ewaybill.BatchPrintOrderRequest;
12+
import me.chanjar.weixin.channel.bean.ewaybill.OrderDetailResponse;
13+
import me.chanjar.weixin.channel.bean.ewaybill.PreCreateRequest;
14+
import me.chanjar.weixin.channel.bean.ewaybill.PreCreateResponse;
15+
import me.chanjar.weixin.channel.bean.ewaybill.PrintContentResponse;
16+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateConfigResponse;
17+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateCreateRequest;
18+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateIdResponse;
19+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateInfoResponse;
20+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateUpdateRequest;
21+
import me.chanjar.weixin.common.error.WxErrorException;
22+
23+
/**
24+
* 视频号小店电子面单服务接口
25+
*
26+
* @author GitHub Copilot
27+
*/
28+
public interface WxChannelEwaybillService {
29+
30+
/** 获取可用的标准面单模板。 @return 模板配置 @throws WxErrorException 微信接口调用失败 */
31+
TemplateConfigResponse getTemplateConfig() throws WxErrorException;
32+
33+
/** 创建商家面单模板。 @param req 官方模板创建字段 @return 新模板 ID @throws WxErrorException 调用失败 */
34+
TemplateIdResponse createTemplate(TemplateCreateRequest req) throws WxErrorException;
35+
36+
/** 删除商家面单模板。 @param templateId 模板 ID @return 操作结果 @throws WxErrorException 调用失败 */
37+
WxChannelBaseResponse deleteTemplate(String templateId) throws WxErrorException;
38+
39+
/** 更新商家面单模板。 @param req 官方模板更新字段 @return 操作结果 @throws WxErrorException 调用失败 */
40+
WxChannelBaseResponse updateTemplate(TemplateUpdateRequest req) throws WxErrorException;
41+
42+
/** 查询标准模板信息。 @param templateCode 标准模板编码 @return 模板详情 @throws WxErrorException 调用失败 */
43+
TemplateInfoResponse getTemplate(String templateCode) throws WxErrorException;
44+
45+
/** 按模板 ID 查询商家模板。 @param templateId 模板 ID @return 模板详情 @throws WxErrorException 调用失败 */
46+
TemplateInfoResponse getTemplateById(String templateId) throws WxErrorException;
47+
48+
/** 查询已开通电子面单的网点和账号。 @return 账号信息 @throws WxErrorException 调用失败 */
49+
AccountInfoResponse getAccount() throws WxErrorException;
50+
51+
/** 查询已开通电子面单的快递公司。 @return 快递公司列表 @throws WxErrorException 调用失败 */
52+
DeliveryListResponse getDeliveryList() throws WxErrorException;
53+
54+
/** 预取电子面单号。 @param req 官方预取号字段 @return 预取号结果 @throws WxErrorException 调用失败 */
55+
PreCreateResponse preCreateOrder(PreCreateRequest req) throws WxErrorException;
56+
57+
/** 获取电子面单号。 @param req 官方取号字段,含收寄件信息 @return 面单号结果 @throws WxErrorException 调用失败 */
58+
CreateOrderResponse createOrder(CreateOrderRequest req) throws WxErrorException;
59+
60+
/** 追加电子面单子件。 @param req 官方子件字段 @return 操作结果 @throws WxErrorException 调用失败 */
61+
WxChannelBaseResponse addSubOrder(AddSubOrderRequest req) throws WxErrorException;
62+
63+
/** 取消电子面单下单。 @param waybillId 运单 ID @return 操作结果 @throws WxErrorException 调用失败 */
64+
WxChannelBaseResponse cancelOrder(PrintOrderRequest req) throws WxErrorException;
65+
66+
/** 查询电子面单详情。 @param waybillId 运单 ID @return 面单详情 @throws WxErrorException 调用失败 */
67+
OrderDetailResponse getOrder(String ewaybillOrderId) throws WxErrorException;
68+
69+
/** 获取打印报文。 @param waybillIds 运单 ID 列表 @param templateId 可选模板 ID @return 打印内容 @throws WxErrorException 调用失败 */
70+
PrintContentResponse getPrintContent(String ewaybillOrderId, String templateId)
71+
throws WxErrorException;
72+
73+
/** 通知单个运单打印成功。 @param waybillId 运单 ID @return 操作结果 @throws WxErrorException 调用失败 */
74+
WxChannelBaseResponse printOrder(PrintOrderRequest req) throws WxErrorException;
75+
76+
/** 批量通知运单打印成功。 @param waybillIds 运单 ID 列表 @return 操作结果 @throws WxErrorException 调用失败 */
77+
WxChannelBaseResponse batchPrintOrder(BatchPrintOrderRequest req) throws WxErrorException;
78+
}

‎weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelService.java‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,13 @@ public interface WxChannelService extends BaseWxChannelService {
210210
*/
211211
WxChannelFavoriteService getFavoriteService();
212212

213+
/**
214+
* 电子面单服务
215+
*
216+
* @return 电子面单服务
217+
*/
218+
default WxChannelEwaybillService getEwaybillService() {
219+
throw new UnsupportedOperationException("当前 WxChannelService 实现不支持电子面单服务");
220+
}
221+
213222
}

‎weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
6464
private WxChannelQicService qicService = null;
6565
private WxTalentService talentService = null;
6666
private WxChannelFavoriteService favoriteService = null;
67+
private WxChannelEwaybillService ewaybillService = null;
6768

6869
protected WxChannelConfig config;
6970
private int retrySleepMillis = 1000;
@@ -235,7 +236,8 @@ protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E
235236

236237
try {
237238
T result = executor.execute(uriWithAccessToken, data, WxType.Channel);
238-
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog,
239+
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken,
240+
printResult ? dataForLog : "...",
239241
printResult ? result : "...");
240242
return result;
241243
} catch (WxErrorException e) {
@@ -262,12 +264,14 @@ protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E
262264
}
263265

264266
if (error.getErrorCode() != 0) {
265-
log.warn("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error);
267+
log.warn("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken,
268+
printResult ? dataForLog : "...", error);
266269
throw new WxErrorException(error, e);
267270
}
268271
return null;
269272
} catch (IOException e) {
270-
log.warn("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage());
273+
log.warn("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken,
274+
printResult ? dataForLog : "...", e.getMessage());
271275
throw new WxRuntimeException(e);
272276
}
273277
}
@@ -509,4 +513,12 @@ public synchronized WxChannelFavoriteService getFavoriteService() {
509513
return favoriteService;
510514
}
511515

516+
@Override
517+
public synchronized WxChannelEwaybillService getEwaybillService() {
518+
if (ewaybillService == null) {
519+
ewaybillService = new WxChannelEwaybillServiceImpl(this);
520+
}
521+
return ewaybillService;
522+
}
523+
512524
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package me.chanjar.weixin.channel.api.impl;
2+
3+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.ADD_SUB_ORDER_URL;
4+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.BATCH_PRINT_ORDER_URL;
5+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.CANCEL_ORDER_URL;
6+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.CREATE_ORDER_URL;
7+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.CREATE_TEMPLATE_URL;
8+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.DELETE_TEMPLATE_URL;
9+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.GET_ACCOUNT_URL;
10+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.GET_DELIVERY_LIST_URL;
11+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.GET_ORDER_URL;
12+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.GET_PRINT_CONTENT_URL;
13+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.GET_TEMPLATE_BY_ID_URL;
14+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.GET_TEMPLATE_CONFIG_URL;
15+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.GET_TEMPLATE_URL;
16+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.PRE_CREATE_ORDER_URL;
17+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.PRINT_ORDER_URL;
18+
import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Ewaybill.UPDATE_TEMPLATE_URL;
19+
20+
import java.util.List;
21+
import me.chanjar.weixin.channel.api.WxChannelEwaybillService;
22+
import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
23+
import me.chanjar.weixin.channel.bean.ewaybill.AccountInfoResponse;
24+
import me.chanjar.weixin.channel.bean.ewaybill.AddSubOrderRequest;
25+
import me.chanjar.weixin.channel.bean.ewaybill.CreateOrderRequest;
26+
import me.chanjar.weixin.channel.bean.ewaybill.CreateOrderResponse;
27+
import me.chanjar.weixin.channel.bean.ewaybill.DeliveryListResponse;
28+
import me.chanjar.weixin.channel.bean.ewaybill.EwaybillOrderIdParam;
29+
import me.chanjar.weixin.channel.bean.ewaybill.PrintOrderRequest;
30+
import me.chanjar.weixin.channel.bean.ewaybill.BatchPrintOrderRequest;
31+
import me.chanjar.weixin.channel.bean.ewaybill.OrderDetailResponse;
32+
import me.chanjar.weixin.channel.bean.ewaybill.PreCreateRequest;
33+
import me.chanjar.weixin.channel.bean.ewaybill.PreCreateResponse;
34+
import me.chanjar.weixin.channel.bean.ewaybill.PrintContentResponse;
35+
import me.chanjar.weixin.channel.bean.ewaybill.PrintContentParam;
36+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateCodeParam;
37+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateConfigResponse;
38+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateCreateRequest;
39+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateIdParam;
40+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateIdResponse;
41+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateInfoResponse;
42+
import me.chanjar.weixin.channel.bean.ewaybill.TemplateUpdateRequest;
43+
import me.chanjar.weixin.channel.bean.ewaybill.WaybillIdParam;
44+
import me.chanjar.weixin.channel.bean.ewaybill.WaybillIdsParam;
45+
import me.chanjar.weixin.channel.util.JsonUtils;
46+
import me.chanjar.weixin.channel.util.ResponseUtils;
47+
import me.chanjar.weixin.common.error.WxErrorException;
48+
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
49+
50+
/**
51+
* 视频号小店电子面单服务实现。
52+
*
53+
* @author GitHub Copilot
54+
*/
55+
public class WxChannelEwaybillServiceImpl implements WxChannelEwaybillService {
56+
57+
/** 微信商店服务 */
58+
private final BaseWxChannelServiceImpl<?, ?> shopService;
59+
60+
public WxChannelEwaybillServiceImpl(BaseWxChannelServiceImpl<?, ?> shopService) {
61+
this.shopService = shopService;
62+
}
63+
64+
@Override
65+
public TemplateConfigResponse getTemplateConfig() throws WxErrorException {
66+
String resJson = post(GET_TEMPLATE_CONFIG_URL, "{}");
67+
return ResponseUtils.decode(resJson, TemplateConfigResponse.class);
68+
}
69+
70+
@Override
71+
public TemplateIdResponse createTemplate(TemplateCreateRequest req) throws WxErrorException {
72+
String resJson = post(CREATE_TEMPLATE_URL, req);
73+
return ResponseUtils.decode(resJson, TemplateIdResponse.class);
74+
}
75+
76+
@Override
77+
public WxChannelBaseResponse deleteTemplate(String templateId) throws WxErrorException {
78+
String resJson = post(DELETE_TEMPLATE_URL, new TemplateIdParam(templateId));
79+
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
80+
}
81+
82+
@Override
83+
public WxChannelBaseResponse updateTemplate(TemplateUpdateRequest req) throws WxErrorException {
84+
String resJson = post(UPDATE_TEMPLATE_URL, req);
85+
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
86+
}
87+
88+
@Override
89+
public TemplateInfoResponse getTemplate(String templateCode) throws WxErrorException {
90+
String resJson = post(GET_TEMPLATE_URL, new TemplateCodeParam(templateCode));
91+
return ResponseUtils.decode(resJson, TemplateInfoResponse.class);
92+
}
93+
94+
@Override
95+
public TemplateInfoResponse getTemplateById(String templateId) throws WxErrorException {
96+
String resJson = post(GET_TEMPLATE_BY_ID_URL, new TemplateIdParam(templateId));
97+
return ResponseUtils.decode(resJson, TemplateInfoResponse.class);
98+
}
99+
100+
@Override
101+
public AccountInfoResponse getAccount() throws WxErrorException {
102+
String resJson = post(GET_ACCOUNT_URL, "{}");
103+
return ResponseUtils.decode(resJson, AccountInfoResponse.class);
104+
}
105+
106+
@Override
107+
public DeliveryListResponse getDeliveryList() throws WxErrorException {
108+
String resJson = post(GET_DELIVERY_LIST_URL, "{}");
109+
return ResponseUtils.decode(resJson, DeliveryListResponse.class);
110+
}
111+
112+
@Override
113+
public PreCreateResponse preCreateOrder(PreCreateRequest req) throws WxErrorException {
114+
String resJson = post(PRE_CREATE_ORDER_URL, req);
115+
return ResponseUtils.decode(resJson, PreCreateResponse.class);
116+
}
117+
118+
@Override
119+
public CreateOrderResponse createOrder(CreateOrderRequest req) throws WxErrorException {
120+
String resJson = post(CREATE_ORDER_URL, req);
121+
return ResponseUtils.decode(resJson, CreateOrderResponse.class);
122+
}
123+
124+
@Override
125+
public WxChannelBaseResponse addSubOrder(AddSubOrderRequest req) throws WxErrorException {
126+
String resJson = post(ADD_SUB_ORDER_URL, req);
127+
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
128+
}
129+
130+
@Override
131+
public WxChannelBaseResponse cancelOrder(PrintOrderRequest req) throws WxErrorException {
132+
String resJson = post(CANCEL_ORDER_URL, req);
133+
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
134+
}
135+
136+
@Override
137+
public OrderDetailResponse getOrder(String ewaybillOrderId) throws WxErrorException {
138+
String resJson = post(GET_ORDER_URL, new EwaybillOrderIdParam(ewaybillOrderId));
139+
return ResponseUtils.decode(resJson, OrderDetailResponse.class);
140+
}
141+
142+
@Override
143+
public PrintContentResponse getPrintContent(String ewaybillOrderId, String templateId)
144+
throws WxErrorException {
145+
String resJson = post(GET_PRINT_CONTENT_URL, new PrintContentParam(ewaybillOrderId, templateId));
146+
return ResponseUtils.decode(resJson, PrintContentResponse.class);
147+
}
148+
149+
@Override
150+
public WxChannelBaseResponse printOrder(PrintOrderRequest req) throws WxErrorException {
151+
String resJson = post(PRINT_ORDER_URL, req);
152+
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
153+
}
154+
155+
@Override
156+
public WxChannelBaseResponse batchPrintOrder(BatchPrintOrderRequest req) throws WxErrorException {
157+
String resJson = post(BATCH_PRINT_ORDER_URL, req);
158+
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
159+
}
160+
161+
private String post(String url, Object request) throws WxErrorException {
162+
return shopService.executeWithoutLog(
163+
SimplePostRequestExecutor.create(shopService), url, JsonUtils.encode(request));
164+
}
165+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package me.chanjar.weixin.channel.bean.ewaybill;
2+
3+
import com.fasterxml.jackson.annotation.JsonAnyGetter;
4+
import com.fasterxml.jackson.annotation.JsonAnySetter;
5+
import com.fasterxml.jackson.annotation.JsonIgnore;
6+
import java.io.Serializable;
7+
import java.util.LinkedHashMap;
8+
import java.util.Map;
9+
import lombok.Data;
10+
import lombok.NoArgsConstructor;
11+
12+
/**
13+
* 电子面单通用请求参数容器。
14+
*
15+
* <p>字段按官方文档动态透传,避免非官方字段定义。</p>
16+
*
17+
* @author GitHub Copilot
18+
*/
19+
@Data
20+
@NoArgsConstructor
21+
public abstract class AbstractEwaybillRequest implements Serializable {
22+
23+
private static final long serialVersionUID = 4213577159985597237L;
24+
25+
@JsonIgnore
26+
private Map<String, Object> params = new LinkedHashMap<>();
27+
28+
@JsonAnySetter
29+
public void addParam(String key, Object value) {
30+
params.put(key, value);
31+
}
32+
33+
@JsonAnyGetter
34+
public Map<String, Object> anyParams() {
35+
return params;
36+
}
37+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package me.chanjar.weixin.channel.bean.ewaybill;
2+
3+
import com.fasterxml.jackson.annotation.JsonAnySetter;
4+
import com.fasterxml.jackson.annotation.JsonIgnore;
5+
import java.util.LinkedHashMap;
6+
import java.util.Map;
7+
import lombok.Data;
8+
import lombok.EqualsAndHashCode;
9+
import lombok.NoArgsConstructor;
10+
import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
11+
12+
/**
13+
* 电子面单通用响应参数容器。
14+
*
15+
* <p>未显式声明字段将保存到 extra 字段,便于兼容官方接口变更。</p>
16+
*
17+
* @author GitHub Copilot
18+
*/
19+
@Data
20+
@NoArgsConstructor
21+
@EqualsAndHashCode(callSuper = true)
22+
public abstract class AbstractEwaybillResponse extends WxChannelBaseResponse {
23+
24+
private static final long serialVersionUID = -2460196179063989718L;
25+
26+
@JsonIgnore
27+
private Map<String, Object> extra = new LinkedHashMap<>();
28+
29+
@JsonAnySetter
30+
public void addExtra(String key, Object value) {
31+
extra.put(key, value);
32+
}
33+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package me.chanjar.weixin.channel.bean.ewaybill;
2+
3+
/**
4+
* 电子面单网点/账号信息响应。
5+
*
6+
* @author GitHub Copilot
7+
*/
8+
public class AccountInfoResponse extends AbstractEwaybillResponse {
9+
private static final long serialVersionUID = 5682783958522805959L;
10+
}

0 commit comments

Comments
 (0)