🆕 #3077【小程序】增加openApi管理的接口支持

This commit is contained in:
水依寒 2023-07-12 11:04:59 +08:00 committed by GitHub
parent 9bd7940195
commit 831aac31ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 362 additions and 4 deletions

View File

@ -70,11 +70,10 @@ public enum WxMaErrorMsgEnum {
* appid不正确或者不符合绑定关系要求.
* 对应操作<code>sendUniformMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuota.html
* </pre>
*/
CODE_40013(40013, "appid不正确,或者不符合绑定关系要求"),
CODE_40013(40013, "appid不正确/不合法(避免异常字符,注意大小写),或者不符合绑定关系要求"),
/**
* <pre>
* template_id 不正确.
@ -267,6 +266,50 @@ public enum WxMaErrorMsgEnum {
* activity_id 过期.
*/
CODE_47504(47504, "activity_id 过期"),
/**
* api 禁止清零调用次数因为清零次数达到上限
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuota.html">参考文档</a>
*/
CODE_48006(48006, "api 禁止清零调用次数,因为清零次数达到上限"),
/**
* rid不存在
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getRidInfo.html">参考文档</a>
*/
CODE_76001(76001, "rid不存在"),
/**
* rid为空或者格式错误
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getRidInfo.html">参考文档</a>
*/
CODE_76002(76002, "rid为空或者格式错误"),
/**
* 当前账号无权查询该rid该rid属于其他账号调用所产生
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getRidInfo.html">参考文档</a>
*/
CODE_76003(76003, "当前账号无权查询该rid该rid属于其他账号调用所产生"),
/**
* rid过期
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getRidInfo.html">参考文档</a>
*/
CODE_76004(76004, "rid过期仅支持持续7天内的rid"),
/**
* cgi_path填错了
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getApiQuota.html">参考文档</a>
*/
CODE_76021(76021, "cgi_path填错了"),
/**
* 当前调用接口使用的token与api所属账号不符
*
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getApiQuota.html">参考文档</a>
*/
CODE_76022(76022, "当前调用接口使用的token与api所属账号不符详情可看注意事项的说明"),
/**
* 没有绑定开放平台帐号.
*/
@ -343,6 +386,17 @@ public enum WxMaErrorMsgEnum {
CODE_91017(91017, "+号规则 不同类型关联名主体不一致"),
CODE_40097(40097, "参数错误"),
/**
* 缺少 appid 参数
* <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuotaByAppSecret.html">参考文档</a>
*/
CODE_41002(41002, "缺少 appid 参数"),
/**
* 缺少 secret 参数
* <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuotaByAppSecret.html">参考文档</a>
*/
CODE_41004(41004, "缺少 secret 参数"),
CODE_41006(41006, "media_id 不能为空"),

View File

@ -0,0 +1,65 @@
package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetApiQuotaResult;
import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetRidInfoResult;
import me.chanjar.weixin.common.error.WxErrorException;
/**
* openApi管理
*
* @author shuiyihan12
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuota.html">openApi管理 微信文档</a>
* @since 2023/7/7 17:07
*/
public interface WxMaOpenApiService {
/**
* 本接口用于清空公众号/小程序/第三方平台等接口的每日调用接口次数
* HTTP调用https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN
*
* @return 是否成功
* @throws WxErrorException the wx error exception
* @apiNote !!! 单小程序直接调用该方法 , 如多个appid调用此方法前请调用 {@link cn.binarywang.wx.miniapp.api.WxMaService#switchoverTo} 切换appid !!!
* @code wxMaService.getWxMaOpenApiService().clearQuota() //单个
* @code wxMaService.switchoverTo(" appid ").getWxMaOpenApiService().clearQuota() //多个
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuota.html">注意事项参考微信文档</a>
*/
boolean clearQuota() throws WxErrorException;
/**
* 查询API调用额度
* HTTP调用https://api.weixin.qq.com/cgi-bin/openapi/quota/get?access_token=ACCESS_TOKEN
*
* @param cgiPath api的请求地址例如"/cgi-bin/message/custom/send";不要前缀https://api.weixin.qq.com 也不要漏了"/",否则都会76003的报错
* @return 额度详情
* @throws WxErrorException 微信异常
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getApiQuota.html">注意事项参考微信文档</a>
*/
WxMiniGetApiQuotaResult getApiQuota(String cgiPath) throws WxErrorException;
/**
* 查询rid信息
* HTTP调用https://api.weixin.qq.com/cgi-bin/openapi/rid/get?access_token=ACCESS_TOKEN
*
* @param rid 调用接口报错返回的rid
* @return 该rid对应的请求详情
* @throws WxErrorException 微信异常
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/getRidInfo.html">注意事项参考微信文档</a>
*/
WxMiniGetRidInfoResult getRidInfo(String rid) throws WxErrorException;
/**
* 使用AppSecret重置 API 调用次数
* HTTP调用https://api.weixin.qq.com/cgi-bin/clear_quota/v2
*
* @return 是否成功
* @throws WxErrorException 微信异常
* @apiNote !!! 单小程序直接调用该方法 , 如多个appid调用此方法前请调用 {@link cn.binarywang.wx.miniapp.api.WxMaService#switchoverTo} 切换appid!!!
* 参考示例
* @code wxMaService.getWxMaOpenApiService().clearQuotaByAppSecret() //单个
* @code wxMaService.switchoverTo(" appid ").getWxMaOpenApiService().clearQuotaByAppSecret() //多个
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuotaByAppSecret.html">注意事项参考微信文档</a>
*/
boolean clearQuotaByAppSecret() throws WxErrorException;
}

View File

@ -523,4 +523,10 @@ public interface WxMaService extends WxService {
* @return getWxMaShopPayService
*/
WxMaShopPayService getWxMaShopPayService();
/**
* 小程序openApi管理
* @return getWxMaOpenApiService
*/
WxMaOpenApiService getWxMaOpenApiService();
}

View File

@ -85,6 +85,7 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
private final WxMaProductOrderService productOrderService = new WxMaProductOrderServiceImpl(this);
private final WxMaShopCouponService wxMaShopCouponService = new WxMaShopCouponServiceImpl(this);
private final WxMaShopPayService wxMaShopPayService = new WxMaShopPayServiceImpl(this);
private final WxMaOpenApiService wxMaOpenApiService = new WxMaOpenApiServiceImpl(this);
private Map<String, WxMaConfig> configMap;
private int retrySleepMillis = 1000;
private int maxRetryTimes = 5;
@ -634,4 +635,9 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
public WxMaShopPayService getWxMaShopPayService() {
return this.wxMaShopPayService;
}
@Override
public WxMaOpenApiService getWxMaOpenApiService() {
return this.wxMaOpenApiService;
}
}

View File

@ -0,0 +1,81 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaOpenApiService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetApiQuotaResult;
import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetRidInfoResult;
import cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants;
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonParser;
import static me.chanjar.weixin.common.api.WxConsts.ERR_CODE;
/**
* @author shuiyihan12
* @since 2023/7/7 17:08
*/
@RequiredArgsConstructor
public class WxMaOpenApiServiceImpl implements WxMaOpenApiService {
private final WxMaService wxMaService;
private static final String QUOTA = "quota";
private static final String REQUEST = "request";
@Override
public boolean clearQuota() throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("appid", this.wxMaService.getWxMaConfig().getAppid());
String responseContent = this.wxMaService.post(WxMaApiUrlConstants.OpenApi.CLEAR_QUOTA, params.toString());
parseErrorResponse(responseContent);
return true;
}
@Override
public WxMiniGetApiQuotaResult getApiQuota(String cgiPath) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("cgi_path", cgiPath);
String responseContent = this.wxMaService.post(WxMaApiUrlConstants.OpenApi.GET_API_QUOTA, params.toString());
parseErrorResponse(responseContent);
JsonObject response = GsonParser.parse(responseContent);
if (response.has(QUOTA)) {
return WxMaGsonBuilder.create().fromJson(response.getAsJsonObject(QUOTA), WxMiniGetApiQuotaResult.class);
}
return null;
}
@Override
public WxMiniGetRidInfoResult getRidInfo(String rid) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("rid", rid);
String responseContent = this.wxMaService.post(WxMaApiUrlConstants.OpenApi.GET_RID_INFO, params.toString());
parseErrorResponse(responseContent);
JsonObject response = GsonParser.parse(responseContent);
if (response.has(REQUEST)) {
return WxMaGsonBuilder.create().fromJson(response.getAsJsonObject(QUOTA), WxMiniGetRidInfoResult.class);
}
return null;
}
@Override
public boolean clearQuotaByAppSecret() throws WxErrorException {
String url = String.format(WxMaApiUrlConstants.OpenApi.CLEAR_QUOTA_BY_APP_SECRET, this.wxMaService.getWxMaConfig().getAppid(), this.wxMaService.getWxMaConfig().getSecret());
String responseContent = this.wxMaService.post(url, "");
parseErrorResponse(responseContent);
return true;
}
private void parseErrorResponse(String response) throws WxErrorException {
JsonObject jsonObject = GsonParser.parse(response);
if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
throw new WxErrorException(WxError.fromJson(response, WxType.MiniApp));
}
}
}

View File

@ -0,0 +1,28 @@
package cn.binarywang.wx.miniapp.bean.openapi;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
/**
* 查询API调用额度 返回数据
*
* @author shuiyihan12
* @since 2023/7/10 16:52
*/
@Data
public class WxMiniGetApiQuotaResult {
/**
* 当天该账号可调用该接口的次数
*/
@SerializedName("daily_limit")
private Integer dailyLimit;
/**
* 当天已经调用的次数
*/
private Integer used;
/**
* 当天剩余调用次数
*/
private Integer remain;
}

View File

@ -0,0 +1,44 @@
package cn.binarywang.wx.miniapp.bean.openapi;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
/**
* 查询rid信息 返回数据
* @author shuiyihan12
* @since 2023/7/10 16:53
*/
@Data
public class WxMiniGetRidInfoResult {
/**
* 发起请求的时间戳
*/
@SerializedName("invoke_time")
private Integer invokeTime;
/**
* 请求毫秒级耗时
*/
@SerializedName("cost_in_ms")
private Integer costInMs;
/**
* 请求的URL参数
*/
@SerializedName("request_url")
private String requestUrl;
/**
* post请求的请求参数
*/
@SerializedName("request_body")
private String requestBody;
/**
* 接口请求返回参数
*/
@SerializedName("response_body")
private String responseBody;
/**
* 接口请求的客户端ip
*/
@SerializedName("client_ip")
private String clientIp;
}

View File

@ -10,6 +10,30 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public class WxMaApiUrlConstants {
/**
* openApi管理
*/
public interface OpenApi {
/**
* 重置API调用次数
*/
String CLEAR_QUOTA = "https://api.weixin.qq.com/cgi-bin/clear_quota";
/**
* 查询API调用额度
*/
String GET_API_QUOTA = "https://api.weixin.qq.com/cgi-bin/openapi/quota/get";
/**
* 查询rid信息
*/
String GET_RID_INFO = "https://api.weixin.qq.com/cgi-bin/openapi/rid/get";
/**
* 使用AppSecret重置 API 调用次数
*/
String CLEAR_QUOTA_BY_APP_SECRET = "https://api.weixin.qq.com/cgi-bin/clear_quota/v2?appid=%s&appsecret=%s";
}
public interface Analysis {
String GET_DAILY_SUMMARY_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailysummarytrend";
String GET_DAILY_VISIT_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailyvisittrend";

View File

@ -0,0 +1,48 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetApiQuotaResult;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import com.google.gson.Gson;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import static org.testng.Assert.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;
/**
* openApi管理测试
*
* @author shuiyihan12
* @since 2023/7/7 17:08
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaOpenApiServiceImplTest {
@Inject
private WxMaService wxMaService;
@Test
public void clearQuota() throws WxErrorException {
final boolean result = wxMaService.getWxMaOpenApiService().clearQuota();
assertTrue(result);
}
@Test
public void getApiQuota() throws WxErrorException {
String cgiPath = "/cgi-bin/openapi/quota/get";
final WxMiniGetApiQuotaResult apiQuota = wxMaService.getWxMaOpenApiService().getApiQuota(cgiPath);
assertNotNull(apiQuota);
System.out.println(new Gson().toJson(apiQuota));
}
@Test
public void clearQuotaByAppSecret() throws WxErrorException {
final boolean result = wxMaService.getWxMaOpenApiService().clearQuotaByAppSecret();
assertTrue(result);
}
}

View File

@ -209,9 +209,11 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
@Override
public String getPayBaseUrl() {
if (this.getConfig().isUseSandboxEnv()) {
if (StringUtils.isNotBlank(this.getConfig().getApiV3Key())) {
throw new WxRuntimeException("微信支付V3 目前不支持沙箱模式!");
}
return this.getConfig().getPayBaseUrl() + "/xdc/apiv2sandbox";
}
return this.getConfig().getPayBaseUrl();
}