app通过api调用服务,dubbo框架下web端配置

  • spring-mvc.xml 配置拦截器

<!-- 将无法mapping到Controller的path交给default servlet handler处理 -->
<mvc:default-servlet-handler/>

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/gateway"/>
        <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
        <bean class="com.lifesea.gateway.intercept.BaseInvokeChainStackIntercept">
            <property name="interceptors">
                <list>
                    <!-- 系统参数解析 -->
                    <bean class="com.lifesea.gateway.intercept.ParamResolveIntercept"/>
                    <!-- 访问频次控制 -->
                    <bean class="com.lifesea.gateway.intercept.RequestLimitIntercept"/>
                </list>
            </property>
        </bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/test"/>
        <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
        <bean class="com.lifesea.gateway.intercept.BaseInvokeChainStackIntercept">
            <property name="interceptors">
                <list>
                    <!-- 系统参数解析 -->
                    <bean class="com.lifesea.gateway.intercept.ParamResolveIntercept"/>
                    <!-- 访问频次控制 -->
                    <bean class="com.lifesea.gateway.intercept.RequestLimitIntercept"/>
                </list>
            </property>
        </bean>
    </mvc:interceptor>
</mvc:interceptors>

- 系统参数解析
/**
*

系统级参数解析


* Created by iscdw on
*/
public class ParamResolveIntercept implements HandlerInterceptor {
    /**
     * DUBBBO默认DATE类型格式
     */
    private static final String DUBBO_DATE_DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * 参数类型简写
     */
    private static final Map<String, String> map = new HashMap<>();

    static {
        map.put("String", "java.lang.String");
        map.put("Integer", "java.lang.Integer");
        map.put("Long", "java.lang.Long");
        map.put("Short", "java.lang.Short");
        map.put("Byte", "java.lang.Byte");
        map.put("Float", "java.lang.Float");
        map.put("Double", "java.lang.Double");
        map.put("Boolean", "java.lang.Boolean");
        map.put("PagingRequest", PagingRequest.class.getName());
        map.put(PagingRequest.class.getName(), PagingRequest.class.getName());
    }

    private final Logger logger = LoggerFactory.getLogger(ParamResolveIntercept.class);
    /**
     * 时间戳格式
     */
    private final String FORMAT = "yyyyMMddHHmmss";
    /**
     * 最大时间误差为10分钟
     */
    private final long INTERVAL = 1000 * 60 * 10;
    private final DefaultHandler defaultHandler = new DefaultHandler();
    private final PagingRequestHandler pagingRequestHandler = new PagingRequestHandler();
    private final CustomObjHandler customObjHandler = new CustomObjHandler();
    private final NumericHandler numericHandler = new NumericHandler();
    private final CharHandler charHandler = new CharHandler();
    private final MapHandler mapHandler = new MapHandler();
    private final ListHandler listHandler = new ListHandler();
    private final JdkObjectHandler jdkObjectHandler = new JdkObjectHandler();
    private final DateHandler dateHandler = new DateHandler();
    @Autowired
    private JedisCluster jedisCluster;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("执行拦截器 ParamResolveIntercept 开始");
        logger.info("request={}", JSON.toJSONString(request.getParameterMap()));
        long time1 = System.currentTimeMillis();
        String sign = request.getParameter("sign");
        String token = request.getParameter("token");
        String method = request.getParameter("method");
        String version = request.getParameter("v");
        String timestamp = request.getParameter("timestamp");
        String appkey = request.getParameter("openid");
        String debug = request.getParameter("_debugg");

        try {

            CheckUtils.check(method, "method");
            CheckUtils.check(timestamp, "timestamp");
            CheckUtils.check(appkey, "openid");


            // 校验APP
            EopApp app = checkApp(appkey);

            // 校验API
            EopApi api = checkApi(method, version);

            // 如果需要则校验签名
            if (api.getSign() == EopApiConst.NEED_SIGN && StringUtils.isEmpty(debug)) {
                // 校验时间戳
                Date reqDate = DateUtils.parseDate(request.getParameter("timestamp"), FORMAT);
                if (Math.abs(System.currentTimeMillis() - reqDate.getTime()) > INTERVAL) {
                    throw new ErrorCodeException(ErrorCodeConst.GLOBAL_REQUEST_ERROR, "请求已过期");
                }

                checkSign(app.getAppSecret(), sign, request);
            }

            // 是否校验 TOKEN
            if (api.getToken() !=null && EopApiConst.NEED_CHECK_TOKEN == api.getToken() && StringUtils.isEmpty(debug)) {
                if (StringUtils.isEmpty(token)) {
                    throw new ErrorCodeException(ErrorCodeConst.GLOBAL_SESSION_TIME_OUT, PublicConfig.PUBLIC_MESSAGE.get("000001"));
                }

                String jsonToken = jedisCluster.get(token);

                if (StringUtils.isEmpty(jsonToken)) {
                    throw new ErrorCodeException(ErrorCodeConst.GLOBAL_SESSION_TIME_OUT, PublicConfig.PUBLIC_MESSAGE.get("000001"));
                }
                Token tok;
                try {
                    tok = JSON.toJavaObject(JSON.parseObject(jsonToken), Token.class);
                } catch (Exception ex) {
                    throw new ErrorCodeException(ErrorCodeConst.GLOBAL_SESSION_TIME_OUT, PublicConfig.PUBLIC_MESSAGE.get("000001"));
                }

                if (tok.getUserId() == null) {
                    throw new ErrorCodeException(ErrorCodeConst.GLOBAL_SESSION_TIME_OUT, PublicConfig.PUBLIC_MESSAGE.get("000001"));
                }

                jedisCluster.setex(token, PublicConfig.LOGIN_EXPIRE, jsonToken);
                logger.info("==================token校验完成");
            }


            // 解析参数
            InvokerContext invokerContext = bindRequestParams(api, request);

            // 存储线程变量
            ApiContext.getContext().setAppKey(appkey);
            ApiContext.getContext().setMethod(method);
            ApiContext.getContext().setVersion(version);
            ApiContext.getContext().setInterfaceName(api.getInterfaceName());
            ApiContext.getContext().setInterfaceMethod(api.getInterfaceMethod());
            ApiContext.getContext().setClientIp(IpUtils.getIpAddr(request));
            ApiContext.getContext().setInvokerContext(invokerContext);

        } catch (ErrorCodeException e) {
            ApiResponse apiResponse = new ApiResponse(e.getCode(), e.getMessage());
            logger.error("执行拦截器 ParamResolveIntercept 失败 times={},msg={}", System.currentTimeMillis() - time1, apiResponse.getMsg());
            response.getWriter().write(JSON.toJSONString(apiResponse));
            return false;
        }
        logger.info("执行拦截器 ParamResolveIntercept 结束 times={}", System.currentTimeMillis() - time1);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {

    }

    public EopApi checkApi(String method, String version) {
        if (StringUtils.isEmpty(version)) {
            version = "1.0.0";
        }
        EopApi api = ApiConst.APIS.get(method + version);
        if (api == null) {
            throw new ErrorCodeException(ErrorCodeConst.GLOBAL_REQUEST_ERROR, "Api接口不存在,请确认[method]参数是否正确");
        }
        if (api.getStatus().shortValue() == RecordStatus.STATUS_OFF_SHORT) {
            throw new ErrorCodeException(ErrorCodeConst.GLOBAL_REQUEST_ERROR, "Api接口已禁用");
        }

        return api;
    }

    public EopApp checkApp(String appkey) {
        EopApp app = ApiConst.APPS.get(appkey);
        if (app == null) {
            throw new ErrorCodeException(ErrorCodeConst.GLOBAL_REQUEST_ERROR, "应用未授权请确认[app_key]参数是否正确");
        }
        if (app.getStatus().shortValue() == RecordStatus.STATUS_OFF_SHORT) {
            throw new ErrorCodeException(ErrorCodeConst.GLOBAL_REQUEST_ERROR, "应用已禁用");
        }
        return app;
    }

    public void checkSign(String secret, String sign, HttpServletRequest request) {

        CheckUtils.check(sign, "sign");
        Map<String, String> params = ParamsUtils.getParams(request);
        String sysSign = SignUtils.getSignature(params, secret);
        logger.info("平台生成sign===========【sign:{}】", sysSign);
        if (!sysSign.equalsIgnoreCase(sign)) {
            throw new ErrorCodeException(ErrorCodeConst.GLOBAL_REQUEST_ERROR, "请求签名无效,请确认[sign]参数是否正确");
        }

    }


	 public InvokerContext bindRequestParams(EopApi api, HttpServletRequest request) throws IllegalAccessException, InstantiationException {
    logger.info("==================解析参数开始");
    long time1 = System.currentTimeMillis();
    InvokerContext invokerContext = new InvokerContext();

    List<EopApiParam> list = JSONArray.parseArray(api.getParams(), EopApiParam.class);
    String[] paramTypes = new String[list.size()];
    Object[] args = new Object[list.size()];

    for (int i = 0; i < args.length; i++) {
        EopApiParam param = list.get(i);
        param = resolveSingleParam(param, request);
        args[i] = param.getValue();
        paramTypes[i] = param.getType();
    }
    invokerContext.setParamTypes(paramTypes);
    invokerContext.setArgs(args);
    logger.info("do bindRequestParams times = {}", String.valueOf(System.currentTimeMillis() - time1));
    return invokerContext;
	 }

	public EopApiParam resolveSingleParam(EopApiParam param, HttpServletRequest request) {

    if (StringUtils.isEmpty(param.getType())) {
        param.setType("java.lang.String");
    }

    // 参数类型简写处理
    if (map.containsKey(param.getType())) {
        param.setType(map.get(param.getType()));
    }

    if (param.getType().equalsIgnoreCase(PagingRequest.class.getName())) { // 分页参数优化
        pagingRequestHandler.handler(param, request);
        return param;
    }

    // 参数属性处理
    if (param.getProps() != null && !param.getProps().isEmpty()) {
        HashMap<String, Object> objPropMap = new HashMap<>();
        for (EopApiParam paramItem : param.getProps()) {
            resolveSingleParam(paramItem, request);
            objPropMap.put(paramItem.getDubboName(), paramItem.getValue());
        }
        param.setValue(objPropMap);
    } else {
        // 如果未配置http参数名称默认采用dubboName的小写做为http参数
        if (StringUtils.isEmpty(param.getDubboName()) && StringUtils.isNotEmpty(param.getName())) {
            param.setDubboName(param.getName().toLowerCase());
        }

        // 特定类型参数处理
        if (param.getType().equalsIgnoreCase("short") || param.getType().equalsIgnoreCase("int")
                || param.getType().equalsIgnoreCase("long") || param.getType().equalsIgnoreCase("float")
                || param.getType().equalsIgnoreCase("double") || param.getType().equalsIgnoreCase("byte")) {

            numericHandler.handler(param, request);

        } else if (param.getType().equalsIgnoreCase("java.util.Date")) { // 日期类型参数

            dateHandler.handler(param, request);

        } else if (param.getType().equalsIgnoreCase("java.util.List")) {
            try {
                listHandler.handler(param, request);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } else if (param.getType().equalsIgnoreCase("char")) { // char主要进行了非空的校验

            charHandler.handler(param, request);

        } else if (param.getType().equalsIgnoreCase("Map") || param.getType().equalsIgnoreCase("java.util.Map")) { // Map类型放入全部的请求参数

            mapHandler.handler(param, request);

        } else if (param.getType().startsWith("java.")) {// 其他java类型对象直接根据参数名称赋值

            jdkObjectHandler.handler(param, request);

        } else {

            customObjHandler.handler(param, request);

        }
    }

    // 必填参数校验
    if (param.isRequired() && param.getValue() != null && StringUtils.isEmpty(StringUtils.trim(param.getValue().toString()))) {
        throw new ErrorCodeException(ErrorCodeConst.GLOBAL_REQUEST_ERROR, String.format("参数[%s]不能为空", param.getName()));
    }

    return param;
}
  • 访问频次控制
  •  /**
      * <p>访问量控制</p>
      * Created by ChenDeWei on 2016/5/21.
      */
     public class RequestLimitIntercept implements HandlerInterceptor {
     
         private final Logger logger = LoggerFactory.getLogger(RequestLimitIntercept.class);
     
         @Autowired
         private JedisCluster jedisCluster;
     
         private final String[] LIMIT_TYPENAME = new String[]{"", "接口", "应用"};
     
     
         @Override
         public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
             logger.info("执行 RequestLimitIntercept");
             long time1 = System.currentTimeMillis();
     
             try {
     
                 if (!checkAppLimit() || !checkApiLimit()) {
                     response.setStatus(503);
                     return false;
                 }
             } catch (Exception e) {
                 logger.error("redis error", e);
             }
             logger.info("do RequestLimitIntercept times:{}", System.currentTimeMillis() - time1);
     
             return true;
         }
     
         @Override
         public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
     
         }
     
         @Override
         public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {
     
         }
     
         public boolean checkAppLimit() {
             Set<EopApiLimit> appLimits = ApiConst.APP_LIMITS.get(ApiContext.getContext().getAppKey());
             if (appLimits == null || appLimits.isEmpty()) {
                 return true;
             }
             for (EopApiLimit limit : appLimits) {
                 String key = "eop_limit_app_" + ApiContext.getContext().getAppKey();
                 if (!check(limit, key)) {
                     return false;
                 }
             }
             return true;
         }
     
         public boolean checkApiLimit() {
             Set<EopApiLimit> apiLimits = ApiConst.API_LIMITS.get(ApiContext.getContext().getMethod());
             if (apiLimits == null || apiLimits.isEmpty()) {
                 return true;
             }
             for (EopApiLimit limit : apiLimits) {
                 String key = "eop_limit_api_" + ApiContext.getContext().getAppKey() + ApiContext.getContext().getMethod() + ApiContext.getContext().getClientIp();
                 if (!check(limit, key)) {
                     return false;
                 }
             }
             return true;
         }
     
         public boolean check(EopApiLimit limit, String key) {
             key = MD5Util.MD5(key);
             if (limit.getStatus() == RecordStatus.STATUS_OFF_SHORT) {
                 return true;
             }
             long count = jedisCluster.incrBy(key, 1);
             if (count == 1) {
                 jedisCluster.expire(key, limit.getUnitTime());
             }
             if (count > limit.getReqLimit()) {
                 logger.info("应用请求过过于繁被拒绝[ip:{},type:{},time:{},limit:{},count:{}]", ApiContext.getContext().getClientIp(), LIMIT_TYPENAME[limit.getType()], limit.getUnitTime(), limit.getReqLimit(), count);
                 return false;
             }
     
             return true;
         }
     }
    

- BaseInvokeChainStackIntercept
- /**
* Created by iscdw on 2016/5/21.
*/
public class BaseInvokeChainStackIntercept implements HandlerInterceptor {

	    private final Logger logger = LoggerFactory.getLogger(BaseInvokeChainStackIntercept.class);
	
	    private List<HandlerInterceptor> interceptors = new ArrayList<>();
	
	    @Override
	    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
	        long time1 = System.currentTimeMillis();
	        for (HandlerInterceptor interceptor : interceptors) {
	            if (!interceptor.preHandle(request, response, o)) {
	                return false;
	            }
	        }
	        logger.info("do interceptors const:{}",System.currentTimeMillis() - time1);
	        return true;
	    }
	
	    @Override
	    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
	        for (HandlerInterceptor interceptor : interceptors) {
	            interceptor.postHandle(request, response, o, modelAndView);
	        }
	    }
	
	    @Override
	    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
	        for (HandlerInterceptor interceptor : interceptors) {
	            interceptor.afterCompletion(request, response, o, e);
	        }
	    }
	
	    public List<HandlerInterceptor> getInterceptors() {
	        return interceptors;
	    }
	
	    public void setInterceptors(List<HandlerInterceptor> interceptors) {
	        this.interceptors = interceptors;
	    }
	}

- Controller 层控制
@Controller
@RequestMapping(value = “/gateway”)
public class OpenApiController {

    public static final Map<String, ReferenceConfig> REFERENCE_MAP = new HashMap<>();
    private static final Logger logger = LoggerFactory.getLogger(OpenApiController.class);
    private static SerializeConfig mapping = new SerializeConfig();
    private static final String LOCK_SUCCESS = "OK";

    static {
        mapping.put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
    }

    @Autowired
    private ApplicationConfig applicationConfig;
    @Autowired
    private RegistryConfig registryConfig;
    @Autowired
    private LogBusinessFacade logBusinessFacade;
    @Autowired
    private JedisCluster jedisCluster;

    @RequestMapping(path = "", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public Object rest(HttpServletRequest request, HttpServletResponse response1, ApiEntity apiEntity) {
        long time1 = System.currentTimeMillis();//时间戳
        //logger.info("request={}", JSON.toJSONString(request.getParameterMap()));

        String accept = request.getHeader("Accept");

        ApiResponse apiResponse = null;
        Object response = null;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response1;
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        httpServletResponse.setHeader("Access-Control-Max-Age", "0");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpServletResponse.setHeader("XDomainRequestAllowed", "1");
        try {
        	
        	
            ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<GenericService>();
            referenceConfig.setInterface(ApiContext.getContext().getInterfaceName());
            referenceConfig.setVersion(ApiContext.getContext().getVersion());
            referenceConfig.setGeneric(true); // 声明为泛化接口
            referenceConfig.setApplication(applicationConfig);
            referenceConfig.setRegistry(registryConfig);
            referenceConfig.setTimeout(30000);

            GenericService genericService = ReferenceConfigCache.getCache().get(referenceConfig);

            // 如果启动OpenApi时 后端Dubbo服务没有提供者则需要销毁缓存,否则等提供者正常运行后无法正常获取提供者
            if (genericService == null) {
                ReferenceConfigCache.getCache().destroy(referenceConfig);
                return new ApiResponse(ErrorCodeConst.GLOBAL_UNKNOW_SERVICE, "服务不可用");
            }
            
            //接口防重
            if(checkRepeatRequest(apiEntity)) {
            	return new ApiResponse(ErrorCodeConst.GLOBAL_REQUEST_REPEAT, "请求已被处理!");
        	}
            
            response = genericService.$invoke(ApiContext.getContext().getInterfaceMethod(), ApiContext.getContext().getInvokerContext().getParamTypes(), ApiContext.getContext().getInvokerContext().getArgs());
            if (MediaType.APPLICATION_OCTET_STREAM_VALUE.equals(accept)) {
                outStream(request, response1, response);
                return null;
            }	
        } catch (ErrorCodeException e) {
            logger.error("请求处理失败,error={}", e.getMessage());
            return new ApiResponse(e.getCode(), e.getMessage());
        } catch (GenericException e) {
            logger.error("请求处理失败,error={}", e.getMessage());
            if (e.getExceptionClass().equalsIgnoreCase(ErrorCodeException.class.getName())) {
                return new ApiResponse(ErrorCodeConst.GLOBAL_RESPONSE_ERROR, e.getExceptionMessage());
            } else {
                return new ApiResponse(ErrorCodeConst.GLOBAL_SYS_UNKNOW, "系统异常");
            }
        } catch (IllegalStateException e) {
            logger.error("请求服务不可用,code={},msg={}", ErrorCodeConst.GLOBAL_UNKNOW_SERVICE, e.getMessage());
            return new ApiResponse(ErrorCodeConst.GLOBAL_UNKNOW_SERVICE, "服务不可用");
        } catch (Exception e) {
            logger.error("系统异常", e);
            return new ApiResponse(ErrorCodeConst.GLOBAL_SYS_UNKNOW, "系统异常");
        }

        apiResponse = new ApiResponse();
        apiResponse.setCode("0");
        apiResponse.setData(response);

        PropertyFilter filter = new PropertyFilter() {
            public boolean apply(Object source, String name, Object value) {
                if ("class".equals(name)) {
                    return false;
                }
                return true;
            }
        };
        LogBusinessDto log = this.getLog(request);
        
    	SerializerFeature[] serializerFeatureArray =new SerializerFeature[] {SerializerFeature.WriteNullStringAsEmpty,SerializerFeature.QuoteFieldNames};
        
        SerializeWriter sw = new SerializeWriter(serializerFeatureArray);

        JSONSerializer serializer = new JSONSerializer(sw);
        serializer.getPropertyFilters().add(filter);

        serializer.getMapping().put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
        serializer.write(apiResponse);
        log.setOutParam(sw + "");
        try {
            this.saveLog(log);
        }catch (Exception e){
            logger.info("============{}",e.getMessage());
        }

        logger.info("response={} times={}", sw, String.valueOf(System.currentTimeMillis() - time1));

        return sw.toString();
    }
    
    /**
     * 
     * @param apiEntity
     * @return true 重复请求 false 正常请求
     */
    public Boolean checkRepeatRequest(ApiEntity apiEntity){
    	
    	Boolean result = false;
    	
    	//老版本调法 无需校验是否重复请求
    	if(!StringUtils.isEmpty(apiEntity.getUniqueKey())) {
    		String redisKey = "CHECK_REPEAT_REQUEST_"+apiEntity.getUniqueKey();
    		String lockResult = jedisCluster.set(redisKey, UUID.randomUUID().toString(), "NX", "PX", 10*60*1000);
            if (!LOCK_SUCCESS.equals(lockResult)) {
            	result =  true;
            	logger.error("重复的请求,method={},key={}",apiEntity.getMethod(),apiEntity.getUniqueKey());
            }
    	}
    	
    	return result;
    }
    
    @Async
    private void saveLog(LogBusinessDto dto) {
        logBusinessFacade.addLogBusiness(dto);
    }

    private LogBusinessDto getLog(HttpServletRequest request) {
        String method = request.getParameter("method");
        String version = request.getParameter("v");
        LogBusinessDto log = new LogBusinessDto();
        log.setAddTime(new Date());
        log.setInParam(JSON.toJSONString(request.getParameterMap()));
        log.setlType("1");
        EopApi api = ApiConst.APIS.get(method + version);
        if (api != null) {
            log.setlTitle(api.getApiName());
        }
        log.setlObject(method);
        return log;
    }

    private void outStream(HttpServletRequest request, HttpServletResponse response, Object bytesObject) {
        try {
            byte[] bytes = (byte[]) bytesObject;
            response.setContentType(MediaType.IMAGE_PNG_VALUE);
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes);
            outputStream.close();
        } catch (IOException e) {
            throw new ErrorCodeException(ErrorCodeConst.GLOBAL_RESPONSE_ERROR, PublicConfig.PUBLIC_MESSAGE.get("000301"));
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值