Commit 9528698a5f875eabee5ab4c16935d24d97c7b6cd
1 parent
60911c9f
短信提交 2021-03-31,重构返回的结果
Showing
12 changed files
with
68 additions
and
121 deletions
src/main/java/cn/iocoder/dashboard/common/exception/GlobalException.java deleted
| 1 | -package cn.iocoder.dashboard.common.exception; | |
| 2 | - | |
| 3 | -import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 4 | -import lombok.Data; | |
| 5 | -import lombok.EqualsAndHashCode; | |
| 6 | - | |
| 7 | -/** | |
| 8 | - * 全局异常 Exception | |
| 9 | - */ | |
| 10 | -@Data | |
| 11 | -@EqualsAndHashCode(callSuper = true) | |
| 12 | -public class GlobalException extends RuntimeException { | |
| 13 | - | |
| 14 | - /** | |
| 15 | - * 全局错误码 | |
| 16 | - * | |
| 17 | - * @see GlobalErrorCodeConstants | |
| 18 | - */ | |
| 19 | - private Integer code; | |
| 20 | - /** | |
| 21 | - * 错误提示 | |
| 22 | - */ | |
| 23 | - private String message; | |
| 24 | - | |
| 25 | - /** | |
| 26 | - * 空构造方法,避免反序列化问题 | |
| 27 | - */ | |
| 28 | - public GlobalException() { | |
| 29 | - } | |
| 30 | - | |
| 31 | - public GlobalException(ErrorCode errorCode) { | |
| 32 | - this.code = errorCode.getCode(); | |
| 33 | - this.message = errorCode.getMessage(); | |
| 34 | - } | |
| 35 | - | |
| 36 | - public GlobalException(Integer code, String message) { | |
| 37 | - this.code = code; | |
| 38 | - this.message = message; | |
| 39 | - } | |
| 40 | - | |
| 41 | -} |
src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
| 1 | 1 | package cn.iocoder.dashboard.common.pojo; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.common.exception.ErrorCode; |
| 4 | -import cn.iocoder.dashboard.common.exception.GlobalException; | |
| 5 | 4 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 6 | 5 | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; |
| 7 | 6 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| ... | ... | @@ -16,7 +15,7 @@ import java.io.Serializable; |
| 16 | 15 | * @param <T> 数据泛型 |
| 17 | 16 | */ |
| 18 | 17 | @Data |
| 19 | -public final class CommonResult<T> implements Serializable { | |
| 18 | +public class CommonResult<T> implements Serializable { | |
| 20 | 19 | |
| 21 | 20 | /** |
| 22 | 21 | * 错误码 |
| ... | ... | @@ -81,16 +80,12 @@ public final class CommonResult<T> implements Serializable { |
| 81 | 80 | // ========= 和 Exception 异常体系集成 ========= |
| 82 | 81 | |
| 83 | 82 | /** |
| 84 | - * 判断是否有异常。如果有,则抛出 {@link GlobalException} 或 {@link ServiceException} 异常 | |
| 83 | + * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常 | |
| 85 | 84 | */ |
| 86 | - public void checkError() throws GlobalException, ServiceException { | |
| 85 | + public void checkError() throws ServiceException { | |
| 87 | 86 | if (isSuccess()) { |
| 88 | 87 | return; |
| 89 | 88 | } |
| 90 | - // 全局异常 | |
| 91 | - if (GlobalErrorCodeConstants.isMatch(code)) { | |
| 92 | - throw new GlobalException(code, msg); | |
| 93 | - } | |
| 94 | 89 | // 业务异常 |
| 95 | 90 | throw new ServiceException(code, msg); |
| 96 | 91 | } |
| ... | ... | @@ -99,8 +94,4 @@ public final class CommonResult<T> implements Serializable { |
| 99 | 94 | return error(serviceException.getCode(), serviceException.getMessage()); |
| 100 | 95 | } |
| 101 | 96 | |
| 102 | - public static <T> CommonResult<T> error(GlobalException globalException) { | |
| 103 | - return error(globalException.getCode(), globalException.getMessage()); | |
| 104 | - } | |
| 105 | - | |
| 106 | 97 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
| 1 | 1 | package cn.iocoder.dashboard.framework.sms.core.client; |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 4 | 3 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 5 | 5 | |
| 6 | 6 | import javax.servlet.ServletRequest; |
| 7 | 7 | import java.util.Map; |
| ... | ... | @@ -30,7 +30,7 @@ public interface SmsClient { |
| 30 | 30 | * @param templateParams 短信模板参数 |
| 31 | 31 | * @return 短信发送结果 |
| 32 | 32 | */ |
| 33 | - SmsResult send(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams); | |
| 33 | + SmsCommonResult<SmsSendRespDTO> send(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams); | |
| 34 | 34 | |
| 35 | 35 | // TODO FROM 芋艿 to ZZF:是不是可以改成意图更明确的解析返回结果,例如说 parseXXXX |
| 36 | 36 | /** | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsCommonResult.java
| 1 | -package cn.iocoder.dashboard.framework.sms.core; | |
| 1 | +package cn.iocoder.dashboard.framework.sms.core.client; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.exceptions.ExceptionUtil; |
| 4 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 4 | 5 | import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; |
| 5 | 6 | import lombok.Data; |
| 6 | - | |
| 7 | -import java.io.Serializable; | |
| 7 | +import lombok.EqualsAndHashCode; | |
| 8 | +import lombok.ToString; | |
| 8 | 9 | |
| 9 | 10 | /** |
| 10 | - * 消息内容实体类 | |
| 11 | + * 短信的 CommonResult 拓展类 | |
| 12 | + * | |
| 13 | + * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段 | |
| 14 | + * | |
| 15 | + * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段 | |
| 16 | + * | |
| 17 | + * @author 芋道源码 | |
| 11 | 18 | */ |
| 12 | 19 | @Data |
| 13 | -public class SmsResult implements Serializable { | |
| 14 | - | |
| 15 | - /** | |
| 16 | - * 是否成功 | |
| 17 | - * | |
| 18 | - * 注意,是调用 API 短信平台的请求是否成功 | |
| 19 | - */ | |
| 20 | - private Boolean success; | |
| 21 | - /** | |
| 22 | - * 发送失败的类型 | |
| 23 | - * | |
| 24 | - * 枚举 {@link SmsSendFailureTypeEnum#getType()} | |
| 25 | - */ | |
| 26 | - private Integer sendFailureType; | |
| 27 | - /** | |
| 28 | - * 发送失败的提示 | |
| 29 | - * | |
| 30 | - * 一般情况下,使用 {@link SmsSendFailureTypeEnum#getMsg()} | |
| 31 | - * 异常情况下,通过格式化 Exception 的提示存储 | |
| 32 | - */ | |
| 33 | - private String sendFailureMsg; | |
| 20 | +@EqualsAndHashCode(callSuper = true) | |
| 21 | +@ToString(callSuper = true) | |
| 22 | +public class SmsCommonResult<T> extends CommonResult<T> { | |
| 34 | 23 | |
| 35 | 24 | /** |
| 36 | - * 短信 API 发送的错误码 | |
| 25 | + * API 返回错误码 | |
| 37 | 26 | * |
| 38 | 27 | * 由于第三方的错误码可能是字符串,所以使用 String 类型 |
| 39 | 28 | */ |
| 40 | - private String apiSendCode; | |
| 29 | + private String apiCode; | |
| 41 | 30 | /** |
| 42 | - * 短信 API 发送的提示 | |
| 31 | + * API 返回提示 | |
| 43 | 32 | */ |
| 44 | - private String apiSendMsg; | |
| 33 | + private String apiMsg; | |
| 34 | + | |
| 45 | 35 | /** |
| 46 | - * 短信 API 发送返回的唯一请求 ID | |
| 47 | - * | |
| 48 | - * 用于和短信 API 进行定位于排错 | |
| 36 | + * API 请求编号 | |
| 49 | 37 | */ |
| 50 | 38 | private String apiRequestId; |
| 51 | - /** | |
| 52 | - * 短信 API 发送返回的序号 | |
| 53 | - * | |
| 54 | - * 用于和短信 API 平台的发送记录关联 | |
| 55 | - */ | |
| 56 | - private String apiSerialNo; | |
| 57 | 39 | |
| 58 | - private SmsResult() { | |
| 40 | + private SmsCommonResult() { | |
| 59 | 41 | } |
| 60 | 42 | |
| 61 | - public static SmsResult success(SmsSendFailureTypeEnum sendFailureType, | |
| 62 | - String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) { | |
| 63 | - SmsResult result = new SmsResult().setSuccess(true).setApiSendCode(apiSendCode).setApiSendMsg(apiSendMsg) | |
| 43 | + public static SmsCommonResult success(SmsSendFailureTypeEnum sendFailureType, | |
| 44 | + String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) { | |
| 45 | + SmsCommonResult result = new SmsCommonResult().setSuccess(true).setApiSendCode(apiSendCode).setApiSendMsg(apiSendMsg) | |
| 64 | 46 | .setApiRequestId(apiRequestId).setApiSerialNo(apiSerialNo); |
| 65 | 47 | if (sendFailureType != null) { |
| 66 | 48 | result.setSendFailureType(sendFailureType.getType()).setSendFailureMsg(sendFailureType.getMsg()); |
| ... | ... | @@ -68,8 +50,8 @@ public class SmsResult implements Serializable { |
| 68 | 50 | return result; |
| 69 | 51 | } |
| 70 | 52 | |
| 71 | - public static SmsResult error(Throwable ex) { | |
| 72 | - return new SmsResult().setSuccess(false) | |
| 53 | + public static SmsCommonResult error(Throwable ex) { | |
| 54 | + return new SmsCommonResult().setSuccess(false) | |
| 73 | 55 | .setSendFailureType(SmsSendFailureTypeEnum.SMS_SEND_EXCEPTION.getType()) |
| 74 | 56 | .setSendFailureMsg(ExceptionUtil.getRootCauseMessage(ex)); |
| 75 | 57 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java
0 → 100644
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java
| 1 | 1 | package cn.iocoder.dashboard.framework.sms.core.client.impl; |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 5 | 6 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; |
| 6 | 7 | import lombok.extern.slf4j.Slf4j; |
| 7 | 8 | |
| ... | ... | @@ -60,8 +61,10 @@ public abstract class AbstractSmsClient implements SmsClient { |
| 60 | 61 | } |
| 61 | 62 | |
| 62 | 63 | @Override |
| 63 | - public final SmsResult send(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) { | |
| 64 | - SmsResult result; | |
| 64 | + public final SmsCommonResult<SmsSendRespDTO> send(Long sendLogId, String mobile, | |
| 65 | + String apiTemplateId, Map<String, Object> templateParams) { | |
| 66 | + // 执行短信发送 | |
| 67 | + SmsCommonResult<SmsSendRespDTO> result; | |
| 65 | 68 | try { |
| 66 | 69 | result = doSend(sendLogId, mobile, apiTemplateId, templateParams); |
| 67 | 70 | } catch (Throwable ex) { |
| ... | ... | @@ -69,7 +72,7 @@ public abstract class AbstractSmsClient implements SmsClient { |
| 69 | 72 | log.error("[send][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]", |
| 70 | 73 | sendLogId, mobile, apiTemplateId, templateParams, ex); |
| 71 | 74 | // 封装返回 |
| 72 | - return SmsResult.error(ex); | |
| 75 | + return SmsCommonResult.error(ex); | |
| 73 | 76 | } |
| 74 | 77 | return result; |
| 75 | 78 | } |
| ... | ... | @@ -83,7 +86,7 @@ public abstract class AbstractSmsClient implements SmsClient { |
| 83 | 86 | * @param templateParams 短信模板参数 |
| 84 | 87 | * @return 短信发送结果 |
| 85 | 88 | */ |
| 86 | - protected abstract SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) | |
| 87 | - throws Throwable; | |
| 89 | + protected abstract SmsCommonResult<SmsSendRespDTO> doSend(Long sendLogId, String mobile, | |
| 90 | + String apiTemplateId, Map<String, Object> templateParams) throws Throwable; | |
| 88 | 91 | |
| 89 | 92 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
| ... | ... | @@ -3,7 +3,7 @@ package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; |
| 3 | 3 | import cn.hutool.core.collection.CollectionUtil; |
| 4 | 4 | import cn.hutool.core.date.DateUtil; |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 7 | 7 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; |
| 8 | 8 | import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; |
| 9 | 9 | import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; |
| ... | ... | @@ -59,7 +59,7 @@ public class AliyunSmsClient extends AbstractSmsClient { |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | 61 | @Override |
| 62 | - protected SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) throws Exception { | |
| 62 | + protected SmsCommonResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) throws Exception { | |
| 63 | 63 | // 构建参数 |
| 64 | 64 | SendSmsRequest request = new SendSmsRequest(); |
| 65 | 65 | request.setSysMethod(MethodType.POST); |
| ... | ... | @@ -73,10 +73,10 @@ public class AliyunSmsClient extends AbstractSmsClient { |
| 73 | 73 | // 执行发送 |
| 74 | 74 | SendSmsResponse sendResult = acsClient.getAcsResponse(request); |
| 75 | 75 | // 解析结果 |
| 76 | - return SmsResult.success(parseSendFailureType(sendResult.getCode()), // 将 API 短信平台,解析成统一的错误码 | |
| 76 | + return SmsCommonResult.success(parseSendFailureType(sendResult.getCode()), // 将 API 短信平台,解析成统一的错误码 | |
| 77 | 77 | sendResult.getCode(), sendResult.getMessage(), sendResult.getRequestId(), sendResult.getBizId()); |
| 78 | 78 | } catch (ClientException ex) { |
| 79 | - return SmsResult.success(parseSendFailureType(ex.getErrCode()), // 将 API 短信平台,解析成统一的错误码 | |
| 79 | + return SmsCommonResult.success(parseSendFailureType(ex.getErrCode()), // 将 API 短信平台,解析成统一的错误码 | |
| 80 | 80 | ex.getErrCode(), formatResultMsg(ex), ex.getRequestId(), null); |
| 81 | 81 | } |
| 82 | 82 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
| ... | ... | @@ -7,7 +7,7 @@ import cn.hutool.core.util.CharsetUtil; |
| 7 | 7 | import cn.hutool.core.util.StrUtil; |
| 8 | 8 | import cn.hutool.core.util.URLUtil; |
| 9 | 9 | import cn.iocoder.dashboard.framework.sms.core.SmsConstants; |
| 10 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 10 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 11 | 11 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; |
| 12 | 12 | import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; |
| 13 | 13 | import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; |
| ... | ... | @@ -66,7 +66,7 @@ public class YunpianSmsClient extends AbstractSmsClient { |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | @Override |
| 69 | - protected SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) throws Throwable { | |
| 69 | + protected SmsCommonResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) throws Throwable { | |
| 70 | 70 | // 构建参数 |
| 71 | 71 | Map<String, String> request = new HashMap<>(); |
| 72 | 72 | request.put(YunpianConstant.APIKEY, properties.getApiKey()); |
| ... | ... | @@ -82,7 +82,7 @@ public class YunpianSmsClient extends AbstractSmsClient { |
| 82 | 82 | throw sendResult.getThrowable(); |
| 83 | 83 | } |
| 84 | 84 | // 解析结果 |
| 85 | - return SmsResult.success(parseSendFailureType(sendResult), // 将 API 短信平台,解析成统一的错误码 | |
| 85 | + return SmsCommonResult.success(parseSendFailureType(sendResult), // 将 API 短信平台,解析成统一的错误码 | |
| 86 | 86 | String.valueOf(sendResult.getCode()), formatResultMsg(sendResult), null, getApiSerialNo(sendResult)); |
| 87 | 87 | } |
| 88 | 88 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
| ... | ... | @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.iocoder.dashboard.common.enums.CommonStatusEnum; |
| 6 | 6 | import cn.iocoder.dashboard.common.enums.UserTypeEnum; |
| 7 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 8 | 8 | import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; |
| 9 | 9 | import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; |
| 10 | 10 | import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; |
| ... | ... | @@ -137,7 +137,7 @@ public class SysSmsServiceImpl implements SysSmsService { |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | 139 | // 发送短信 |
| 140 | - SmsResult sendResult = smsClient.send(message.getSendLogId(), message.getMobile(), | |
| 140 | + SmsCommonResult sendResult = smsClient.send(message.getSendLogId(), message.getMobile(), | |
| 141 | 141 | message.getApiTemplateId(), message.getTemplateParams()); |
| 142 | 142 | } |
| 143 | 143 | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
| 1 | 1 | package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; |
| 5 | 5 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; |
| 6 | 6 | import org.junit.jupiter.api.Test; |
| ... | ... | @@ -30,7 +30,7 @@ public class AliyunSmsClientTest { |
| 30 | 30 | templateParams.put("code", "1024"); |
| 31 | 31 | // templateParams.put("operation", "嘿嘿"); |
| 32 | 32 | // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); |
| 33 | - SmsResult result = smsClient.send(1L, "15601691399", "SMS_207945135", templateParams); | |
| 33 | + SmsCommonResult result = smsClient.send(1L, "15601691399", "SMS_207945135", templateParams); | |
| 34 | 34 | System.out.println(result); |
| 35 | 35 | } |
| 36 | 36 | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java
| 1 | 1 | package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult; | |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; |
| 5 | 5 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; |
| 6 | 6 | import org.junit.jupiter.api.Test; |
| ... | ... | @@ -29,7 +29,7 @@ public class YunpianSmsClientIntegrationTest { |
| 29 | 29 | templateParams.put("code", "1024"); |
| 30 | 30 | templateParams.put("operation", "嘿嘿"); |
| 31 | 31 | // SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); |
| 32 | - SmsResult result = smsClient.send(1L, "15601691399", "4383920", templateParams); | |
| 32 | + SmsCommonResult result = smsClient.send(1L, "15601691399", "4383920", templateParams); | |
| 33 | 33 | System.out.println(result); |
| 34 | 34 | } |
| 35 | 35 | ... | ... |