赞
踩
我用的短信平台是阿里云的,需要付费购买服务,购买地址:https://common-buy.aliyun.com/?spm=5176.8195934.907839.sms6.312c4183mzE9Yb&&commodityCode=newdysmsbag#/buy
付费完成后,首先申请短信签名和短信模板:https://help.aliyun.com/document_detail/55327.html?spm=a2c4g.11186623.6.549.huzd56。
短信签名:根据用户属性来创建符合自身属性的签名信息。企业用户需要上传相关企业资质证明,个人用户需要上传证明个人身份的证明。注意:短信签名需要审核通过后才可以使用。
短信模板:短信模板,即具体发送的短信内容。短信模板可以支持验证码、短信通知、推广短信、国际/港澳台消息四种模式。验证码和短信通知,通过变量替换实现个性短信定制。推广短信不支持在模板中添加变量。短信模板需要审核通过后才可以使用。
短信示例:【阿里云】 验证码${number},您正进行支付宝的身份验证,打死不告诉别人!这里的短信签名:阿里云,短信模板: 验证码${number},您正进行支付宝的身份验证,打死不告诉别人!
最后获取 asscessKeyId 和 accessKeySecret 。结合阿里云提供的开发者文档即可进行接口开发,短信开发api文档:https://help.aliyun.com/product/44282.html?spm=a2c4g.750001.6.1.T84wBi
一、安装redis
下载地址:Redis,下载最新稳定版本。
在/usr/local创建redis文件夹上传下载的安装包到redis目录下:
安装:
$ tar -zxvf redis-4.0.9.tar.gz
$ cd redis-4.0.9
$ make
安装完成后的目录:
$ cd src
$ ./redis-server ../redis.conf
./redis-server 这种方式启动redis 使用的是默认配置。可以通过启动参数告诉redis使用指定配置文件使用下面命令启动。
二、创建maven工程,目录结构如下:
三、pom.xml文件添加redis依赖
- <!--redis-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
四、application.properties添加redis配置
五、随机生成验证码工具类
- public class IdentifyCodeUtil {
-
- public static String getRandom() {
- String num = "";
- for (int i = 0; i < 6; i++) {
- num = num + String.valueOf((int) Math.floor(Math.random() * 9 + 1));
- }
- return num;
- }
- }
六、redis缓存配置类
sprringboot启动类Application.java加入注解:@EnableCaching
配置redis采用缓存,设置key和value的序列化方式
package com.jp.tech.applet.ms.sms; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; /** * Redis缓存配置类 * * @author yangfeng */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { //缓存管理器 @Bean public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); //设置缓存过期时间 //cacheManager.setDefaultExpiration(20); return cacheManager; } @Bean public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory); setSerializer(template);//设置序列化工具 template.afterPropertiesSet(); return template; } private void setSerializer(StringRedisTemplate template) { @SuppressWarnings({"rawtypes", "unchecked"}) Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); } }
七、redis接口类
此接口用于连接redis操作生成的验证码,设置过期时间。
public interface IRedisService { /** * 设置key-value * @param key * @param value */ void setKey(String key, String value); /** * 获取key * @param key * @return */ String getValue(String key); /** * 删除key * @param key */ void delete(String key); }
八、redis接口实现类
保存、获取、删除验证码接口实现方法。
import com.jp.tech.applet.ms.sms.service.IRedisService; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @Service public class RedisService implements IRedisService { @Resource private RedisTemplate redisTemplate; @Override public void setKey(String key, String value) { ValueOperations<String, String> ops = redisTemplate.opsForValue(); ops.set(key, value, 900, TimeUnit.SECONDS);//15分钟过期 } @Override public String getValue(String key) { ValueOperations<String, String> ops = redisTemplate.opsForValue(); return ops.get(key); } @Override public void delete(String key) { redisTemplate.delete(key); } }
九、发送短信接口类
- package com.jp.zpzc.service;
-
- import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
- import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
- import com.aliyuncs.exceptions.ClientException;
- import com.jp.framework.common.model.ServiceResult;
-
- public interface ISmsService {
-
- /**
- * 发送短信接口
- *
- * @param phoneNums 手机号码
- * @param signName 模板签名
- * @param templeteCode 模板代码
- * @param templateParam 模板替换参数
- * @param outId 提供给业务方扩展字段
- * @return
- * @throws ClientException
- */
- SendSmsResponse sendSms(String phoneNums, String signName, String templeteCode,
- String templateParam, String outId) throws ClientException;
-
- /**
- * 查询短信发送明细
- *
- * @param phoneNumber
- * @param bizId 业务流水号
- * @return
- * @throws ClientException
- */
- QuerySendDetailsResponse querySendDetails(String phoneNumber, String bizId) throws ClientException;
-
- /**
- * 发送短信服务
- *
- * @param mobile 手机号
- * @return
- */
- ServiceResult<Object> sendMessage(String mobile);
-
-
- /**
- * 判断验证码是否正确
- *
- * @param mobile
- * @param identifyCode
- * @return
- */
- ServiceResult<Boolean> checkIsCorrectCode(String mobile, String identifyCode);
-
- }
十、短信接口实现类
pom中引入阿里云短信SDK:
- <!--阿里云短信服务平台-->
- <dependency>
- <groupId>com.aliyun</groupId>
- <artifactId>aliyun-java-sdk-core</artifactId>
- <version>3.3.1</version>
- </dependency>
- <dependency>
- <groupId>com.aliyun</groupId>
- <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
- <version>1.1.0</version>
- </dependency>
application.properties加入调用接口的认证信息:
- aliyun.sms.accessKeyId=LTAIr0fPaMi66tCy111
- aliyun.sms.accessKeySecret=bQ9Re8uV14yRKKhWdWAbbUO15EM7w1111
这里我用的是阿里云短信平台,根据提供的demo自己整理后的接口实现。
smsService.sendSms(phoneName, "XXXXXX", "XXXXXXX", JSON.toJSONString(codeMap), null)
第一个XXXXX代表申请的短信签名名称,第二个代表申请的短信模板编码,改成自己申请的即可。
- package com.jp.zpzc.service.impl;
-
- import com.alibaba.fastjson.JSON;
- import com.aliyuncs.DefaultAcsClient;
- import com.aliyuncs.IAcsClient;
- import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
- import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
- import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
- import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
- import com.aliyuncs.exceptions.ClientException;
- import com.aliyuncs.profile.DefaultProfile;
- import com.aliyuncs.profile.IClientProfile;
- import com.jp.framework.common.model.ServiceResult;
- import com.jp.framework.common.model.ServiceResultHelper;
- import com.jp.framework.common.util.Constant;
- import com.jp.framework.common.util.IdentifyCodeUtil;
- import com.jp.zpzc.service.IRedisService;
- import com.jp.zpzc.service.ISmsService;
- import org.apache.commons.lang3.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
-
- import javax.annotation.Resource;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
-
- @Service
- public class SmsService implements ISmsService {
-
- private Logger logger = LoggerFactory.getLogger(this.getClass());
-
- //产品名称:云通信短信API产品,开发者无需替换
- static final String product = "Dysmsapi";
- //产品域名,开发者无需替换
- static final String domain = "dysmsapi.aliyuncs.com";
-
- @Value("${aliyun.sms.accessKeyId}")
- private String accessKeyId;
- @Value("${aliyun.sms.accessKeySecret}")
- private String accessKeySecret;
-
- @Resource
- private IRedisService redisService;
-
- /**
- * 发送短信服务
- *
- * @param mobile
- * @return
- */
- public ServiceResult<Object> sendMessage(String mobile) {
- if (StringUtils.isEmpty(mobile)) {
- return ServiceResultHelper.genResultWithFaild(Constant.ErrorCode.INVALID_PARAM_MSG, Constant.ErrorCode.INVALID_PARAM_CODE);
- }
- String identifyCode;
- //1. 判断是否缓存该账号验证码
- String returnCode = redisService.getValue(mobile + Constant.SMS_LOGIN_IDENTIFY_CODE);
- if (!StringUtils.isEmpty(returnCode)) {
- identifyCode = returnCode;
- } else {
- identifyCode = IdentifyCodeUtil.getRandom();
- }
- //2.发送短信
- Map<String, String> codeMap = new HashMap<>();
- codeMap.put("code", identifyCode);
- SendSmsResponse response;
- try {
- response = sendSms(mobile, XXXXX, XXXXX, JSON.toJSONString(codeMap), null);
- //短信发送成功后存入redis
- if (response != null && Constant.SMS_SEND_STATUS_OK.equalsIgnoreCase(response.getCode()) && StringUtils.isEmpty(returnCode)) {
- redisService.setKey(mobile + Constant.SMS_LOGIN_IDENTIFY_CODE, identifyCode);
- }
- return ServiceResultHelper.genResultWithSuccess(response);
- } catch (Exception e) {
- logger.error("sendMessage method invoke error: {}", e.getMessage());
- }
- return ServiceResultHelper.genResultWithFaild("短信发送失败", null);
- }
-
- /**
- * 发送短信接口
- *
- * @param phoneNums
- * @param signName 模板签名
- * @param templeteCode 模板代码
- * @param templateParam 模板替换参数
- * @param outId 提供给业务方扩展字段
- * @return
- * @throws ClientException
- */
- @Override
- public SendSmsResponse sendSms(String phoneNums, String signName, String templeteCode, String templateParam, String outId) throws ClientException {
- //可自助调整超时时间
- System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
- System.setProperty("sun.net.client.defaultReadTimeout", "10000");
-
- //初始化acsClient,暂不支持region化
- IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
- DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
- IAcsClient acsClient = new DefaultAcsClient(profile);
-
- //组装请求对象-具体描述见控制台-文档部分内容
- SendSmsRequest request = new SendSmsRequest();
- //必填:待发送手机号
- request.setPhoneNumbers(phoneNums);
- //必填:短信签名-可在短信控制台中找到
- request.setSignName(signName);//众评众测
- //必填:短信模板-可在短信控制台中找到
- request.setTemplateCode(templeteCode);
- //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
- request.setTemplateParam(templateParam);//{"code":"152745"}
-
- //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
- //request.setSmsUpExtendCode("90997");
-
- //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
- request.setOutId(outId);//zpzc
-
- //hint 此处可能会抛出异常,注意catch
-
- SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
- acsClient.getAcsResponse(request);
- return sendSmsResponse;
- }
-
- /**
- * 判断验证码是否正确
- *
- * @param mobile
- * @param identifyCode
- * @return
- */
- public ServiceResult<Boolean> checkIsCorrectCode(String mobile, String identifyCode) {
- if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(identifyCode)) {
- return ServiceResultHelper.genResultWithFaild(Constant.ErrorCode.INVALID_PARAM_MSG, Constant.ErrorCode.INVALID_PARAM_CODE);
- }
- String returnCode = redisService.getValue(mobile + Constant.SMS_LOGIN_IDENTIFY_CODE);
- if (!StringUtils.isEmpty(returnCode) && returnCode.equals(identifyCode)) {
- return ServiceResultHelper.genResultWithSuccess();
- }
- return ServiceResultHelper.genResultWithFaild();
- }
-
- /**
- * 查询短信发送明细
- *
- * @param phoneNumber
- * @param bizId
- * @return
- * @throws ClientException
- */
- @Override
- public QuerySendDetailsResponse querySendDetails(String phoneNumber, String bizId) throws ClientException {
- //可自助调整超时时间
- System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
- System.setProperty("sun.net.client.defaultReadTimeout", "10000");
-
- //初始化acsClient,暂不支持region化
- IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
- DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
- IAcsClient acsClient = new DefaultAcsClient(profile);
-
- //组装请求对象
- QuerySendDetailsRequest request = new QuerySendDetailsRequest();
- //必填-号码
- request.setPhoneNumber(phoneNumber);
- //可选-流水号
- request.setBizId(bizId);
- //必填-发送日期 支持30天内记录查询,格式yyyyMMdd
- SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
- request.setSendDate(ft.format(new Date()));
- //必填-页大小
- request.setPageSize(10L);
- //必填-当前页码从1开始计数
- request.setCurrentPage(1L);
-
- //hint 此处可能会抛出异常,注意catch
- QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request);
- return querySendDetailsResponse;
- }
- }
十一、controller类
调用redis存入随机生成的验证码,调用短信平台接口发送验证码。
- package com.jp.zpzc.controller.sms;
-
- import com.jp.zpzc.service.ISmsService;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.annotation.Resource;
-
-
- /**
- * 短信验证码接口
- *
- * @author yangfeng
- * @date 2018-06-09 16:02
- **/
- @RestController
- @RequestMapping("/smsVerifityCode")
- public class SmsVerifityCodeController {
-
- private Logger LOG = LoggerFactory.getLogger(this.getClass());
-
- @Resource
- private ISmsService smsService;
-
- /**
- * 发送短信验证码
- *
- * @param mobile
- * @return
- */
- @RequestMapping("/sendMessage")
- public Object sendMessage(@RequestParam String mobile) {
- return smsService.sendMessage(mobile);
- }
-
- /**
- * 判断验证码是否正确
- *
- * @param mobile
- * @param identifyCode
- * @return
- */
- @RequestMapping("/checkIsCorrectCode")
- public Object checkIsCorrectCode(@RequestParam String mobile, @RequestParam String identifyCode) {
- return smsService.checkIsCorrectCode(mobile, identifyCode);
- }
- }
十二、短信发送测试
短信发送成功。
附:
Constant类代码:
- package com.jp.tech.applet.common.constant;
-
- public class Constant {
-
-
- public static class ErrorCode {
-
- /**
- * 无效参数
- */
- public static Integer INVALID_PARAM_CODE = -101;
- public static String INVALID_PARAM_MSG = "无效参数";
-
- /**
- * 没有权限
- */
- public static Integer PERMISSION_DENIED_CODE = -102;
- public static String PERMISSION_DENIED_MSG = "没有权限";
-
- /**
- * 通用错误
- */
- public static Integer COMMON_ERROR_CODE = -103;
- public static String COMMON_ERROR_MSG = "服务器繁忙,请稍后再试";
-
- /**
- * 登录失效
- */
- public static Integer INVALID_LOGIN_CODE = -104;
- public static String INVALID_LOGIN_MSG = "登录失效";
-
- /**
- * 数据库操作失败
- */
- public static Integer DATABASE_OPERATION_ERROR_CODE = -105;
- public static String DATABASE_OPERATION_ERROR_MSG = "数据库操作失败";
-
- /**
- * token失效
- */
- public static Integer INVALID_TOKEN_CODE = -106;
- public static String INVALID_TOKEN_MSG = "用户未登录或登录信息已失效";
-
- /**
- * 服务器异常
- */
- public static Integer SERVER_ERROR_CODE = -200;
- public static String SERVER_ERROR_MSG = "服务器异常";
-
- }
-
- }
-
ServiceResultHelper 类:
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by Fernflower decompiler)
- //
-
- package com.jp.framework.common.model;
-
- import com.jp.framework.common.util.Constant;
-
- public class ServiceResultHelper {
- public ServiceResultHelper() {
- }
-
- public static <T> ServiceResult<T> genResult(boolean succeed, int retCode, String msg, T obj) {
- ServiceResult ret = new ServiceResult();
- ret.setData(obj);
- ret.setMsg(msg);
- ret.setCode(retCode);
- ret.setSucceed(succeed);
- return ret;
- }
-
- public static <T> boolean isSuccess(ServiceResult<T> result) {
- return result != null && result.isSucceed() && result.getCode() == 0;
- }
-
-
- public static <T> ServiceResult<T> genResultWithSuccess(T obj) {
- return genResult(true, Constant.SUCCESS, "成功", obj);
- }
-
- public static <T> ServiceResult<T> genResultWithSuccess() {
- return genResult(true, Constant.SUCCESS, "成功", (Object)null);
- }
-
- public static <T> ServiceResult<T> genResultWithFaild() {
- return genResult(false, Constant.FAILED, "失败", (Object)null);
- }
-
- public static <T> ServiceResult<T> genResultWithFaild(String msg, Integer code) {
- return genResult(false, code, msg, (Object)null);
- }
-
- public static <T> ServiceResult<T> genResultWithDataNull() {
- return genResult(false, Constant.SUCCESS, "数据为空", (Object)null);
- }
-
- }
ServiceResult 类:
-
- import com.fasterxml.jackson.annotation.JsonInclude;
- import com.fasterxml.jackson.annotation.JsonInclude.Include;
- import com.backstage.core.constant.Constant;
- import org.apache.commons.lang3.builder.ToStringBuilder;
- import org.apache.commons.lang3.builder.ToStringStyle;
-
- import java.util.HashMap;
- import java.util.Map;
-
- @JsonInclude(Include.ALWAYS)
- public final class ServiceResult<T> {
- private static final long serialVersionUID = 6977558218691386450L;
- private boolean succeed = true;
- private int code;
- private int subCode;
- private String msg;
- private T data;
- private Map<String, Object> additionalProperties;
-
- public ServiceResult() {
- this.code = Constant.SUCCESS;
- this.subCode = Constant.SUCCESS;
- this.additionalProperties = new HashMap();
- }
-
- public static ServiceResult<Boolean> error() {
- return error(500, "未知异常,请联系管理员");
- }
-
- public static ServiceResult<Boolean> error(String msg) {
- return error(500, msg);
- }
-
- public static ServiceResult<Boolean> error(int subCode, String msg) {
- ServiceResult result = new ServiceResult();
- result.setCode(Constant.FAILED);
- result.setSubCode(subCode);
- result.setSucceed(false);
- result.setMsg(msg);
- return result;
- }
-
- public static ServiceResult ok() {
- return ok(Constant.SUCCESS, "成功");
- }
-
- public static ServiceResult ok(int code, String msg) {
- ServiceResult result = new ServiceResult();
- result.setCode(code);
- result.setSucceed(true);
- result.setMsg(msg);
- return result;
- }
-
- public static ServiceResult ok(Object data) {
- ServiceResult d = new ServiceResult();
- d.setSucceed(true);
- d.setData(data);
- d.setCode(Constant.SUCCESS);
- d.setMsg("成功");
- return d;
- }
-
- public static ServiceResult ok(Object data, Map<String, Object> additionalProperties) {
- ServiceResult d = new ServiceResult();
- d.setSucceed(true);
- d.setData(data);
- d.setCode(Constant.SUCCESS);
- d.setMsg("成功");
- d.additionalProperties.putAll(additionalProperties);
- return d;
- }
-
- public ServiceResult(T data) {
- this.code = Constant.SUCCESS;
- this.subCode = Constant.SUCCESS;
- this.additionalProperties = new HashMap();
- this.data = data;
- }
-
- public ServiceResult(boolean succeed, int code, String msg) {
- this.code = Constant.SUCCESS;
- this.subCode = Constant.SUCCESS;
- this.additionalProperties = new HashMap();
- this.succeed = succeed;
- this.code = code;
- this.msg = msg;
- }
-
- public ServiceResult(boolean succeed, T data, String msg) {
- this.code = Constant.SUCCESS;
- this.subCode = Constant.SUCCESS;
- this.additionalProperties = new HashMap();
- this.succeed = succeed;
- this.data = data;
- this.msg = msg;
- }
-
- public ServiceResult(boolean succeed, T data, int code, String msg) {
- this.code = Constant.SUCCESS;
- this.subCode = Constant.SUCCESS;
- this.additionalProperties = new HashMap();
- this.succeed = succeed;
- this.data = data;
- this.code = code;
- this.msg = msg;
- }
-
- public ServiceResult(boolean succeed, String msg) {
- this.code = Constant.SUCCESS;
- this.subCode = Constant.SUCCESS;
- this.additionalProperties = new HashMap();
- this.succeed = succeed;
- this.msg = msg;
- }
-
- public boolean isSucceed() {
- return this.succeed;
- }
-
- public void setSucceed(boolean succeed) {
- this.succeed = succeed;
- }
-
- public T getData() {
- return this.data;
- }
-
- public void setData(T data) {
- this.data = data;
- }
-
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
- }
-
- public String getMsg() {
- return this.msg;
- }
-
- public void setMsg(String msg) {
- this.msg = msg;
- }
-
- public int getCode() {
- return this.code;
- }
-
- public void setCode(int code) {
- this.code = code;
- }
-
- public int getSubCode() {
- return this.subCode;
- }
-
- public void setSubCode(int subCode) {
- this.subCode = subCode;
- }
-
- public Map<String, Object> getAdditionalProperties() {
- return this.additionalProperties;
- }
-
- public void setAdditionalProperties(String name, Object value) {
- this.additionalProperties.put(name, value);
- }
-
- public Object getAdditionalProperties(String name) {
- return this.additionalProperties.get(name);
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。