欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud 搭建企業(yè)級開發(fā)框架之實現(xiàn)多租戶多平臺短信通知服務(wù)(微服務(wù)實戰(zhàn))

 更新時間:2021年11月30日 14:36:52   作者:全棧程序猿  
這篇文章主要介紹了SpringCloud 搭建企業(yè)級開發(fā)框架之實現(xiàn)多租戶多平臺短信通知服務(wù),系統(tǒng)可以支持多家云平臺提供的短信服務(wù)。這里以阿里云和騰訊云為例,集成短信通知服務(wù),需要的朋友可以參考下

目前系統(tǒng)集成短信似乎是必不可少的部分,由于各種云平臺都提供了不同的短信通道,這里我們增加多租戶多通道的短信驗證碼,并增加配置項,使系統(tǒng)可以支持多家云平臺提供的短信服務(wù)。這里以阿里云和騰訊云為例,集成短信通知服務(wù)。

1、在GitEgg-Platform中新建gitegg-platform-sms基礎(chǔ)工程,定義抽象方法和配置類
SmsSendService發(fā)送短信抽象接口:

/**
 * 短信發(fā)送接口
 */
public interface SmsSendService {

    /**
     * 發(fā)送單個短信
     * @param smsData
     * @param phoneNumber
     * @return
     */
    default SmsResponse sendSms(SmsData smsData, String phoneNumber){
        if (StrUtil.isEmpty(phoneNumber)) {
            return new SmsResponse();
        }
        return this.sendSms(smsData, Collections.singletonList(phoneNumber));
    }

    /**
     * 群發(fā)發(fā)送短信
     * @param smsData
     * @param phoneNumbers
     * @return
     */
    SmsResponse sendSms(SmsData smsData, Collection<string> phoneNumbers);
    
}

SmsResultCodeEnum定義短信發(fā)送結(jié)果

/**
 * @ClassName: ResultCodeEnum
 * @Description: 自定義返回碼枚舉
 * @author GitEgg
 * @date 2020年09月19日 下午11:49:45
 */
@Getter
@AllArgsConstructor
public enum SmsResultCodeEnum {

    /**
     * 成功
     */
    SUCCESS(200, "操作成功"),

    /**
     * 系統(tǒng)繁忙,請稍后重試
     */
    ERROR(429, "短信發(fā)送失敗,請稍后重試"),

    /**
     * 系統(tǒng)錯誤
     */
    PHONE_NUMBER_ERROR(500, "手機號錯誤");

    public int code;

    public String msg;
}

2、新建gitegg-platform-sms-aliyun工程,實現(xiàn)阿里云短信發(fā)送接口
AliyunSmsProperties配置類

@Data
@Component
@ConfigurationProperties(prefix = "sms.aliyun")
public class AliyunSmsProperties {

    /**
     * product
     */
    private String product = "Dysmsapi";

    /**
     * domain
     */
    private String domain = "dysmsapi.aliyuncs.com";

    /**
     * regionId
     */
    private String regionId = "cn-hangzhou";

    /**
     * accessKeyId
     */
    private String accessKeyId;

    /**
     * accessKeySecret
     */
    private String accessKeySecret;

    /**
     * 短信簽名
     */
    private String signName;
}

AliyunSmsSendServiceImpl阿里云短信發(fā)送接口實現(xiàn)類

/**
 * 阿里云短信發(fā)送
 */
@Slf4j
@AllArgsConstructor
public class AliyunSmsSendServiceImpl implements SmsSendService {

    private static final String successCode = "OK";

    private final AliyunSmsProperties properties;

    private final IAcsClient acsClient;

    @Override
    public SmsResponse sendSms(SmsData smsData, Collection<string> phoneNumbers) {
        SmsResponse smsResponse = new SmsResponse();
        SendSmsRequest request = new SendSmsRequest();
        request.setSysMethod(MethodType.POST);
        request.setPhoneNumbers(StrUtil.join(",", phoneNumbers));
        request.setSignName(properties.getSignName());
        request.setTemplateCode(smsData.getTemplateId());
        request.setTemplateParam(JsonUtils.mapToJson(smsData.getParams()));
        try {
            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
            if (null != sendSmsResponse && !StringUtils.isEmpty(sendSmsResponse.getCode())) {
                if (this.successCode.equals(sendSmsResponse.getCode())) {
                    smsResponse.setSuccess(true);
                } else {
                    log.error("Send Aliyun Sms Fail: [code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
                }
                smsResponse.setCode(sendSmsResponse.getCode());
                smsResponse.setMessage(sendSmsResponse.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Send Aliyun Sms Fail: {}", e);
            smsResponse.setMessage("Send Aliyun Sms Fail!");
        }
        return smsResponse;
    }

}

3、新建gitegg-platform-sms-tencent工程,實現(xiàn)騰訊云短信發(fā)送接口
TencentSmsProperties配置類

@Data
@Component
@ConfigurationProperties(prefix = "sms.tencent")
public class TencentSmsProperties {

    /* 填充請求參數(shù),這里 request 對象的成員變量即對應(yīng)接口的入?yún)?
     * 您可以通過官網(wǎng)接口文檔或跳轉(zhuǎn)到 request 對象的定義處查看請求參數(shù)的定義
     * 基本類型的設(shè)置:
     * 幫助鏈接:
     * 短信控制臺:https://console.cloud.tencent.com/smsv2
     * sms helper:https://cloud.tencent.com/document/product/382/3773 */
    /* 短信應(yīng)用 ID: 在 [短信控制臺] 添加應(yīng)用后生成的實際 SDKAppID,例如1400006666 */
    private String SmsSdkAppId;

    /* 國際/港澳臺短信 senderid: 國內(nèi)短信填空,默認未開通,如需開通請聯(lián)系 [sms helper] */
    private String senderId;

    /* 短信碼號擴展號: 默認未開通,如需開通請聯(lián)系 [sms helper] */
    private String extendCode;

    /**
     * 短信簽名
     */
    private String signName;
}

TencentSmsSendServiceImpl騰訊云短信發(fā)送接口實現(xiàn)類

/**
 * 騰訊云短信發(fā)送
 */
@Slf4j
@AllArgsConstructor
public class TencentSmsSendServiceImpl implements SmsSendService {

    private static final String successCode = "Ok";

    private final TencentSmsProperties properties;

    private final SmsClient client;

    @Override
    public SmsResponse sendSms(SmsData smsData, Collection<string> phoneNumbers) {
        SmsResponse smsResponse = new SmsResponse();

        SendSmsRequest request = new SendSmsRequest();
        request.setSmsSdkAppid(properties.getSmsSdkAppId());
        /* 短信簽名內(nèi)容: 使用 UTF-8 編碼,必須填寫已審核通過的簽名,可登錄 [短信控制臺] 查看簽名信息 */
        request.setSign(properties.getSignName());
        /* 國際/港澳臺短信 senderid: 國內(nèi)短信填空,默認未開通,如需開通請聯(lián)系 [sms helper] */
        if (!StringUtils.isEmpty(properties.getSenderId()))
        {
            request.setSenderId(properties.getSenderId());
        }
        request.setTemplateID(smsData.getTemplateId());
        /* 下發(fā)手機號碼,采用 e.164 標準,+[國家或地區(qū)碼][手機號]
         * 例如+8613711112222, 其中前面有一個+號 ,86為國家碼,13711112222為手機號,最多不要超過200個手機號*/
        String[] phoneNumbersArray = (String[]) phoneNumbers.toArray();
        request.setPhoneNumberSet(phoneNumbersArray);
        /* 模板參數(shù): 若無模板參數(shù),則設(shè)置為空*/
        String[] templateParams = new String[]{};
        if (!CollectionUtils.isEmpty(smsData.getParams())) {
            templateParams = (String[]) smsData.getParams().values().toArray();
        }
        request.setTemplateParamSet(templateParams);
        try {
            /* 通過 client 對象調(diào)用 SendSms 方法發(fā)起請求。注意請求方法名與請求對象是對應(yīng)的
             * 返回的 res 是一個 SendSmsResponse 類的實例,與請求對象對應(yīng) */
            SendSmsResponse sendSmsResponse = client.SendSms(request);
            //如果是批量發(fā)送,那么騰訊云短信會返回每條短信的發(fā)送狀態(tài),這里默認返回第一條短信的狀態(tài)
            if (null != sendSmsResponse && null != sendSmsResponse.getSendStatusSet()) {
                SendStatus sendStatus = sendSmsResponse.getSendStatusSet()[0];
                if (this.successCode.equals(sendStatus.getCode()))
                {
                    smsResponse.setSuccess(true);
                }
                else
                {
                    smsResponse.setCode(sendStatus.getCode());
                    smsResponse.setMessage(sendStatus.getMessage());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Send Aliyun Sms Fail: {}", e);
            smsResponse.setMessage("Send Aliyun Sms Fail!");
        }
        return smsResponse;
    }
}

4、在GitEgg-Cloud中新建業(yè)務(wù)調(diào)用方法,這里要考慮到不同租戶調(diào)用不同的短信配置進行短信發(fā)送,所以新建SmsFactory短信接口實例化工廠,根據(jù)不同的租戶實例化不同的短信發(fā)送接口,這里以實例化com.gitegg.service.extension.sms.factory.SmsAliyunFactory類為例,進行實例化操作,實際使用中,這里需要配置和租戶的對應(yīng)關(guān)系,從租戶的短信配置中獲取。

@Component
public class SmsFactory {

    private final ISmsTemplateService smsTemplateService;

    /**
     * SmsSendService 緩存
     */
    private final Map<long, smssendservice=""> SmsSendServiceMap = new ConcurrentHashMap<>();

    public SmsFactory(ISmsTemplateService smsTemplateService) {
        this.smsTemplateService = smsTemplateService;
    }

    /**
     * 獲取 SmsSendService
     *
     * @param smsTemplateDTO 短信模板
     * @return SmsSendService
     */
    public SmsSendService getSmsSendService(SmsTemplateDTO smsTemplateDTO) {

        //根據(jù)channelId獲取對應(yīng)的發(fā)送短信服務(wù)接口,channelId是唯一的,每個租戶有其自有的channelId
        Long channelId = smsTemplateDTO.getChannelId();
        SmsSendService smsSendService = SmsSendServiceMap.get(channelId);
        if (null == smsSendService) {
            Class cls = null;
            try {
                cls = Class.forName("com.gitegg.service.extension.sms.factory.SmsAliyunFactory");
                Method staticMethod = cls.getDeclaredMethod("getSmsSendService", SmsTemplateDTO.class);
                smsSendService = (SmsSendService) staticMethod.invoke(cls,smsTemplateDTO);
                SmsSendServiceMap.put(channelId, smsSendService);
            } catch (ClassNotFoundException | NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }
        return smsSendService;
    }
}
/**
 * 阿里云短信服務(wù)接口工廠類
 */
public class SmsAliyunFactory {

    public static SmsSendService getSmsSendService(SmsTemplateDTO sms) {
        AliyunSmsProperties aliyunSmsProperties = new AliyunSmsProperties();
        aliyunSmsProperties.setAccessKeyId(sms.getSecretId());
        aliyunSmsProperties.setAccessKeySecret(sms.getSecretKey());
        aliyunSmsProperties.setRegionId(sms.getRegionId());
        aliyunSmsProperties.setSignName(sms.getSignName());
        IClientProfile profile = DefaultProfile.getProfile(aliyunSmsProperties.getRegionId(), aliyunSmsProperties.getAccessKeyId(), aliyunSmsProperties.getAccessKeySecret());
        IAcsClient acsClient = new DefaultAcsClient(profile);
        return new AliyunSmsSendServiceImpl(aliyunSmsProperties, acsClient);
    }

}
/**
 * 騰訊云短信服務(wù)接口工廠類
 */
public class SmsTencentFactory {

    public static SmsSendService getSmsSendService(SmsTemplateDTO sms) {

        TencentSmsProperties tencentSmsProperties = new TencentSmsProperties();
        tencentSmsProperties.setSmsSdkAppId(sms.getSecretId());
        tencentSmsProperties.setExtendCode(sms.getSecretKey());
        tencentSmsProperties.setSenderId(sms.getRegionId());
        tencentSmsProperties.setSignName(sms.getSignName());

        /* 必要步驟:
         * 實例化一個認證對象,入?yún)⑿枰獋魅腧v訊云賬戶密鑰對 secretId 和 secretKey
         * 本示例采用從環(huán)境變量讀取的方式,需要預(yù)先在環(huán)境變量中設(shè)置這兩個值
         * 您也可以直接在代碼中寫入密鑰對,但需謹防泄露,不要將代碼復(fù)制、上傳或者分享給他人
         * CAM 密鑰查詢:https://console.cloud.tencent.com/cam/capi
         */
        Credential cred = new Credential(sms.getSecretId(), sms.getSecretKey());
        // 實例化一個 http 選項,可選,無特殊需求時可以跳過
        HttpProfile httpProfile = new HttpProfile();
        // 設(shè)置代理
//        httpProfile.setProxyHost("host");
//        httpProfile.setProxyPort(port);
        /* SDK 默認使用 POST 方法。
         * 如需使用 GET 方法,可以在此處設(shè)置,但 GET 方法無法處理較大的請求 */
        httpProfile.setReqMethod("POST");
        /* SDK 有默認的超時時間,非必要請不要進行調(diào)整
         * 如有需要請在代碼中查閱以獲取最新的默認值 */
        httpProfile.setConnTimeout(60);
        /* SDK 會自動指定域名,通常無需指定域名,但訪問金融區(qū)的服務(wù)時必須手動指定域名
         * 例如 SMS 的上海金融區(qū)域名為 sms.ap-shanghai-fsi.tencentcloudapi.com */
        if (!StringUtils.isEmpty(sms.getRegionId()))
        {
            httpProfile.setEndpoint(sms.getRegionId());
        }

        /* 非必要步驟:
         * 實例化一個客戶端配置對象,可以指定超時時間等配置 */
        ClientProfile clientProfile = new ClientProfile();
        /* SDK 默認用 TC3-HMAC-SHA256 進行簽名
         * 非必要請不要修改該字段 */
        clientProfile.setSignMethod("HmacSHA256");
        clientProfile.setHttpProfile(httpProfile);
        /* 實例化 SMS 的 client 對象
         * 第二個參數(shù)是地域信息,可以直接填寫字符串 ap-guangzhou,或者引用預(yù)設(shè)的常量 */
        SmsClient client = new SmsClient(cred, "",clientProfile);

        return new TencentSmsSendServiceImpl(tencentSmsProperties, client);
    }
}

5、定義短信發(fā)送接口及實現(xiàn)類
ISmsService業(yè)務(wù)短信發(fā)送接口定義

/**
 * <p>
 * 短信發(fā)送接口定義
 * </p>
 *
 * @author GitEgg
 * @since 2021-01-25
 */
public interface ISmsService {


    /**
     * 發(fā)送短信
     *
     * @param smsCode
     * @param smsData
     * @param phoneNumbers
     * @return
     */
    SmsResponse sendSmsNormal(String smsCode, String smsData, String phoneNumbers);

    /**
     * 發(fā)送短信驗證碼
     *
     * @param smsCode
     * @param phoneNumber
     * @return
     */
    SmsResponse sendSmsVerificationCode( String smsCode, String phoneNumber);

    /**
     * 校驗短信驗證碼
     *
     * @param smsCode
     * @param phoneNumber
     * @return
     */
    boolean checkSmsVerificationCode(String smsCode, String phoneNumber, String verificationCode);

}

SmsServiceImpl 短信發(fā)送接口實現(xiàn)類

/**
 * <p>
 * 短信發(fā)送接口實現(xiàn)類
 * </p>
 *
 * @author GitEgg
 * @since 2021-01-25
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SmsServiceImpl implements ISmsService {

    private final SmsFactory smsFactory;

    private final ISmsTemplateService smsTemplateService;

    private final RedisTemplate redisTemplate;

    @Override
    public SmsResponse sendSmsNormal(String smsCode, String smsData, String phoneNumbers) {
        SmsResponse smsResponse = new SmsResponse();
        try {
            QuerySmsTemplateDTO querySmsTemplateDTO = new QuerySmsTemplateDTO();
            querySmsTemplateDTO.setSmsCode(smsCode);
            //獲取短信code的相關(guān)信息,租戶信息會根據(jù)mybatis plus插件獲取
            SmsTemplateDTO smsTemplateDTO = smsTemplateService.querySmsTemplate(querySmsTemplateDTO);
            ObjectMapper mapper = new ObjectMapper();
            Map smsDataMap = mapper.readValue(smsData, Map.class);

            List<string> phoneNumberList =  JsonUtils.jsonToList(phoneNumbers, String.class);
            SmsData smsDataParam = new SmsData();
            smsDataParam.setTemplateId(smsTemplateDTO.getTemplateId());
            smsDataParam.setParams(smsDataMap);
            SmsSendService smsSendService = smsFactory.getSmsSendService(smsTemplateDTO);
            smsResponse = smsSendService.sendSms(smsDataParam, phoneNumberList);
        } catch (Exception e) {
            smsResponse.setMessage("短信發(fā)送失敗");
            e.printStackTrace();
        }
        return smsResponse;
    }

    @Override
    public SmsResponse sendSmsVerificationCode(String smsCode, String phoneNumber) {
        String verificationCode = RandomUtil.randomNumbers(6);
        Map<string, string=""> smsDataMap = new HashMap<>();
        smsDataMap.put(SmsConstant.SMS_CAPTCHA_TEMPLATE_CODE, verificationCode);
        List<string> phoneNumbers = Arrays.asList(phoneNumber);
        SmsResponse smsResponse = this.sendSmsNormal(smsCode, JsonUtils.mapToJson(smsDataMap), JsonUtils.listToJson(phoneNumbers));
        if (null != smsResponse && smsResponse.isSuccess()) {
            // 將短信驗證碼存入redis并設(shè)置過期時間為5分鐘
            redisTemplate.opsForValue().set(SmsConstant.SMS_CAPTCHA_KEY + smsCode + phoneNumber, verificationCode, 30,
                    TimeUnit.MINUTES);
        }
        return smsResponse;
    }

    @Override
    public boolean checkSmsVerificationCode(String smsCode, String phoneNumber, String verificationCode) {
        String verificationCodeRedis = (String) redisTemplate.opsForValue().get(SmsConstant.SMS_CAPTCHA_KEY + smsCode + phoneNumber);
        if (!StrUtil.isAllEmpty(verificationCodeRedis, verificationCode) && verificationCode.equalsIgnoreCase(verificationCodeRedis)) {
            return true;
        }
        return false;
    }
}

6、新建SmsFeign類,供其他微服務(wù)調(diào)用發(fā)送短信

/**
 * @ClassName: SmsFeign
 * @Description: SmsFeign前端控制器
 * @author gitegg
 * @date 2019年5月18日 下午4:03:58
 */
@RestController
@RequestMapping(value = "/feign/sms")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Api(value = "SmsFeign|提供微服務(wù)調(diào)用接口")
@RefreshScope
public class SmsFeign {

    private final ISmsService smsService;

    @GetMapping(value = "/send/normal")
    @ApiOperation(value = "發(fā)送普通短信", notes = "發(fā)送普通短信")
    Result<object> sendSmsNormal(@RequestParam("smsCode") String smsCode, @RequestParam("smsData") String smsData, @RequestParam("phoneNumbers") String phoneNumbers) {
        SmsResponse smsResponse = smsService.sendSmsNormal(smsCode, smsData, phoneNumbers);
        return Result.data(smsResponse);
    }

    @GetMapping(value = "/send/verification/code")
    @ApiOperation(value = "發(fā)送短信驗證碼", notes = "發(fā)送短信驗證碼")
    Result<object> sendSmsVerificationCode(@RequestParam("smsCode") String smsCode, @RequestParam("phoneNumber") String phoneNumber) {
        SmsResponse smsResponse = smsService.sendSmsVerificationCode(smsCode, phoneNumber);
        return Result.data(smsResponse);
    }

    @GetMapping(value = "/check/verification/code")
    @ApiOperation(value = "校驗短信驗證碼", notes = "校驗短信驗證碼")
    Result<boolean> checkSmsVerificationCode(@RequestParam("smsCode") String smsCode, @RequestParam("phoneNumber") String phoneNumber, @RequestParam("verificationCode") String verificationCode) {
        boolean checkResult = smsService.checkSmsVerificationCode(smsCode, phoneNumber, verificationCode);
        return Result.data(checkResult);
    }
}

項目源碼:

Gitee: https://gitee.com/wmz1930/GitEgg

GitHub: https://github.com/wmz1930/GitEgg

到此這篇關(guān)于SpringCloud 搭建企業(yè)級開發(fā)框架之實現(xiàn)多租戶多平臺短信通知服務(wù)的文章就介紹到這了,更多相關(guān)SpringCloud 短信通知服務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java agent 使用及實現(xiàn)代碼

    java agent 使用及實現(xiàn)代碼

    java agent的作用可以在字節(jié)碼這個層面對類和方法進行修改的技術(shù),能夠在不影響編譯的情況下,修改字節(jié)碼。本文主要給大家講解java agent 使用及實現(xiàn)代碼,感興趣的朋友一起看看吧
    2018-07-07
  • Springboot整合Mybatis傳值的常用方式總結(jié)

    Springboot整合Mybatis傳值的常用方式總結(jié)

    今天給大家?guī)淼氖顷P(guān)于Springboot的相關(guān)知識,文章圍繞著Springboot整合Mybatis傳值的常用方式展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Spring的@CrossOrigin注解處理請求源碼解析

    Spring的@CrossOrigin注解處理請求源碼解析

    這篇文章主要介紹了Spring的@CrossOrigin注解處理請求源碼解析,@CrossOrigin源碼解析主要分為兩個階段@CrossOrigin注釋的方法掃描注冊,請求匹配@CrossOrigin注釋的方法,本文從源碼角度進行解析,需要的朋友可以參考下
    2023-12-12
  • Java常用對象操作工具代碼實例

    Java常用對象操作工具代碼實例

    這篇文章主要介紹了Java常用對象操作工具代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • Spring?Boot?教程之創(chuàng)建項目的三種方式

    Spring?Boot?教程之創(chuàng)建項目的三種方式

    這篇文章主要分享了Spring?Boot?教程之創(chuàng)建項目的三種方式,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • java對象初始化代碼詳解

    java對象初始化代碼詳解

    這篇文章主要介紹了java對象初始化代碼詳解,涉及實例變量的初始化,類變量的初始化等相關(guān)介紹幾代碼示例,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • springboot+mybatis如何屏蔽掉mybatis日志

    springboot+mybatis如何屏蔽掉mybatis日志

    這篇文章主要介紹了springboot+mybatis如何屏蔽掉mybatis日志問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java集成swagger文檔組件

    Java集成swagger文檔組件

    這篇文章主要介紹了Java集成swagger文檔組件,通過詳細的圖文介紹和代碼分析展示,從頭展開說明詳細的過程,希望對你的開發(fā)有所幫助
    2021-06-06
  • Java 多線程synchronized關(guān)鍵字詳解(六)

    Java 多線程synchronized關(guān)鍵字詳解(六)

    這篇文章主要介紹了Java 多線程synchronized關(guān)鍵字詳解(六)的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • java遞歸算法實例分析

    java遞歸算法實例分析

    這篇文章主要介紹了java遞歸算法實例分析,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12

最新評論