二维码初体验 com.google.zxing 实现续 - web api封装

文章目录

  • 一、概述
  • 二、最终效果
  • 三、源码结构
  • 四、完整代码

一、概述

在 二维码初体验 com.google.zxing 实现 我们实现了二维码的生成,但是大部分情况下,二维码的相关功能是作为API接口来提供服务的。

我们下面便演示在springboot、Knife4j下封装api接口来实现二维码生成功能。

二、最终效果

在这里插入图片描述

三、源码结构

在这里插入图片描述

四、完整代码

如何使用下面的备份文件恢复成原始的项目代码,请移步查阅:神奇代码恢复工具
dog.jpg 在这里插入图片描述

//goto pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.fly</groupId><artifactId>qrcode-web</artifactId><version>1.0.0</version><packaging>jar</packaging><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.4.RELEASE</version><relativePath /></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>1.8</java.version></properties><dependencies><!-- Compile --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>2.0.8</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.4.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- 异步日志,需要加入disruptor依赖 --><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.4.2</version></dependency></dependencies><build><finalName>${project.artifactId}-${project.version}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><useSystemClassLoader>false</useSystemClassLoader></configuration></plugin></plugins></build>
</project>
//goto src\main\java\com\fly\core\config\Knife4jConfig.java
package com.fly.core.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;import io.swagger.annotations.ApiOperation;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;/*** knife4j** @author jack*/
@EnableKnife4j
@Configuration
@EnableSwagger2WebMvc
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfig
{@Value("${knife4j.enable: true}")private boolean enable;@BeanDocket defaultApi(){return new Docket(DocumentationType.SWAGGER_2).enable(enable).apiInfo(apiInfo()).groupName("0.1版").select().apis(RequestHandlerSelectors.basePackage("com.fly.qrcode.api")).apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 同时满足apis多个条件.paths(PathSelectors.any()).build();}private ApiInfo apiInfo(){return new ApiInfoBuilder().title("数据接口API").description("接口文档").termsOfServiceUrl("http://00fly.online/").version("1.0.0").build();}
}
//goto src\main\java\com\fly\core\utils\SpringContextUtils.java
package com.fly.core.utils;import java.net.InetAddress;
import java.net.UnknownHostException;import javax.servlet.ServletContext;import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;import lombok.extern.slf4j.Slf4j;/*** Spring Context 工具类* * @author 00fly**/
@Slf4j
@Component
public class SpringContextUtils implements ApplicationContextAware
{private static ApplicationContext applicationContext;/*** web服务器基准URL*/private static String SERVER_BASE_URL = null;@Overridepublic void setApplicationContext(ApplicationContext applicationContext)throws BeansException{log.info("###### execute setApplicationContext ######");SpringContextUtils.applicationContext = applicationContext;}public static ApplicationContext getApplicationContext(){return applicationContext;}public static <T> T getBean(Class<T> clazz){Assert.notNull(applicationContext, "applicationContext is null");return applicationContext.getBean(clazz);}/*** execute @PostConstruct May be SpringContextUtils not inited, throw NullPointerException* * @return*/public static String getActiveProfile(){Assert.notNull(applicationContext, "applicationContext is null");String[] profiles = applicationContext.getEnvironment().getActiveProfiles();return StringUtils.join(profiles, ",");}/*** can use in @PostConstruct* * @param context* @return*/public static String getActiveProfile(ApplicationContext context){Assert.notNull(context, "context is null");String[] profiles = context.getEnvironment().getActiveProfiles();return StringUtils.join(profiles, ",");}/*** get web服务基准地址,一般为 http://${ip}:${port}/${contentPath}* * @return* @throws UnknownHostException* @see [类、类#方法、类#成员]*/public static String getServerBaseURL()throws UnknownHostException{if (SERVER_BASE_URL == null){ServletContext servletContext = getBean(ServletContext.class);Assert.notNull(servletContext, "servletContext is null");String ip = InetAddress.getLocalHost().getHostAddress();SERVER_BASE_URL = "http://" + ip + ":" + getProperty("server.port") + servletContext.getContextPath();}return SERVER_BASE_URL;}/*** getProperty* * @param key eg:server.port* @return* @see [类、类#方法、类#成员]*/public static String getProperty(String key){return applicationContext.getEnvironment().getProperty(key, "");}
}
//goto src\main\java\com\fly\qrcode\api\ApiController.java
package com.fly.qrcode.api;import java.awt.image.BufferedImage;
import java.net.URL;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;import com.fly.qrcode.qr.QRCodeUtil;import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;/*** * QR* * @author 00fly* @version [版本号, 2021年4月14日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Controller
@Api(tags = "二维码API接口")
public class ApiController
{@ApiOperation("生成二维码")@ApiImplicitParam(name = "content", value = "二维码文本", required = true, example = "乡愁是一棵没有年轮的树,永不老去")@PostMapping(value = "/create", produces = MediaType.IMAGE_JPEG_VALUE)public void index(String content, HttpServletResponse response)throws Exception{if (StringUtils.isNotBlank(content)){Resource resource = new ClassPathResource("qrCode/dog.jpg");URL imgURL = resource.getURL();BufferedImage image = QRCodeUtil.createImage(content, imgURL, true);// 输出图象到页面ImageIO.write(image, "JPEG", response.getOutputStream());}}
}
//goto src\main\java\com\fly\qrcode\qr\BufferedImageLuminanceSource.java
package com.fly.qrcode.qr;import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;import com.google.zxing.LuminanceSource;public class BufferedImageLuminanceSource extends LuminanceSource
{private BufferedImage image;private int left;private int top;public BufferedImageLuminanceSource(BufferedImage image){this(image, 0, 0, image.getWidth(), image.getHeight());}public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height){super(width, height);int sourceWidth = image.getWidth();int sourceHeight = image.getHeight();if (left + width > sourceWidth || top + height > sourceHeight){throw new IllegalArgumentException("Crop rectangle does not fit within image data.");}for (int y = top; y < top + height; y++){for (int x = left; x < left + width; x++){if ((image.getRGB(x, y) & 0xFF000000) == 0){image.setRGB(x, y, 0xFFFFFFFF);}}}this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);this.image.getGraphics().drawImage(image, 0, 0, null);this.left = left;this.top = top;}@Overridepublic byte[] getRow(int y, byte[] row){if (y < 0 || y >= getHeight()){throw new IllegalArgumentException("Requested row is outside the image: " + y);}int width = getWidth();if (row == null || row.length < width){row = new byte[width];}image.getRaster().getDataElements(left, top + y, width, 1, row);return row;}@Overridepublic byte[] getMatrix(){int width = getWidth();int height = getHeight();int area = width * height;byte[] matrix = new byte[area];image.getRaster().getDataElements(left, top, width, height, matrix);return matrix;}@Overridepublic boolean isCropSupported(){return true;}@Overridepublic LuminanceSource crop(int left, int top, int width, int height){return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);}@Overridepublic boolean isRotateSupported(){return true;}@Overridepublic LuminanceSource rotateCounterClockwise(){int sourceWidth = image.getWidth();int sourceHeight = image.getHeight();AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);Graphics2D g = rotatedImage.createGraphics();g.drawImage(image, transform, null);g.dispose();int width = getWidth();return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);}
}
//goto src\main\java\com\fly\qrcode\qr\QRCodeUtil.java
package com.fly.qrcode.qr;import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.util.Hashtable;import javax.imageio.ImageIO;import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;public class QRCodeUtil
{private static final String CHARSET = "utf-8";/*** 二维码尺寸*/private static final int QRCODE_SIZE = 300;/*** LOGO宽度*/private static final int WIDTH = 60;/*** LOGO高度*/private static final int HEIGHT = 60;/*** 给定内容、图标生成二维码图片* * @param content 內容* @param imgURL 图标* @param needCompress 是否压缩尺寸* @return* @throws Exception* @see [类、类#方法、类#成员]*/public static BufferedImage createImage(String content, URL imgURL, boolean needCompress)throws Exception{Hashtable<EncodeHintType, Object> hints = new Hashtable<>();hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);hints.put(EncodeHintType.CHARACTER_SET, CHARSET);hints.put(EncodeHintType.MARGIN, 1);BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);int width = bitMatrix.getWidth();int height = bitMatrix.getHeight();BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int x = 0; x < width; x++){for (int y = 0; y < height; y++){image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);}}if (imgURL == null){return image;}// 插入图片insertImage(image, imgURL, needCompress);return image;}private static void insertImage(BufferedImage source, URL imgURL, boolean needCompress)throws Exception{if (imgURL == null){System.err.println("文件不存在!");return;}Image src = ImageIO.read(imgURL);int width = src.getWidth(null);int height = src.getHeight(null);if (needCompress){// 压缩LOGOwidth = Math.min(width, WIDTH);height = Math.min(height, HEIGHT);Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = tag.getGraphics();g.drawImage(image, 0, 0, null);g.dispose();src = image;}// 插入LOGOGraphics2D graph = source.createGraphics();int x = (QRCODE_SIZE - width) / 2;int y = (QRCODE_SIZE - height) / 2;graph.drawImage(src, x, y, width, height, null);Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);graph.setStroke(new BasicStroke(3f));graph.draw(shape);graph.dispose();}/*** 解析二维码图* * @param file* @return* @throws Exception* @see [类、类#方法、类#成员]*/public static String decode(File file)throws Exception{BufferedImage image = ImageIO.read(file);if (image == null){return null;}BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));Hashtable<DecodeHintType, String> hints = new Hashtable<>();hints.put(DecodeHintType.CHARACTER_SET, CHARSET);Result result = new MultiFormatReader().decode(bitmap, hints);return result.getText();}/*** 解析二维码图* * @param path* @return* @throws Exception* @see [类、类#方法、类#成员]*/public static String decode(String path)throws Exception{return decode(new File(path));}
}
//goto src\main\java\com\fly\QrCodeApplication.java
package com.fly;import java.io.IOException;import org.apache.commons.lang3.SystemUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;import com.fly.core.utils.SpringContextUtils;import lombok.extern.slf4j.Slf4j;@Slf4j
@SpringBootApplication
public class QrCodeApplication implements CommandLineRunner
{public static void main(String[] args){SpringApplication.run(QrCodeApplication.class, args);}@Overridepublic void run(String... args)throws IOException{if (SystemUtils.IS_OS_WINDOWS){log.info("★★★★★★★★  now open Browser ★★★★★★★★ ");String url = SpringContextUtils.getServerBaseURL();Runtime.getRuntime().exec("cmd /c start /min " + url + "/doc.html");}}
}
//goto src\main\resources\application.yml
server:port: 8080servlet:context-path: /session:timeout: 1800
spring:mvc:view:prefix: /WEB-INF/views/suffix: .jsp
//goto src\main\resources\log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="off"><!-- 日志文件目录和压缩文件 --><Properties><Property name="fileName">../logs/hello</Property><Property name="fileGz">../logs/hello/7z</Property></Properties><Appenders><!--这个输出控制台的配置 --><Console name="console" target="SYSTEM_OUT"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="NEUTRAL" /><!--输出日志的格式 --><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} %L %M - %msg%xEx%n" /></Console><!--这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --><RollingRandomAccessFile name="rollingInfoFile" fileName="${fileName}/springboot-info.log" immediateFlush="false"filePattern="${fileGz}/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.springboot-info.gz"><PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} [%t] %-5level %logger{36} %L %M - %msg%xEx%n" /><Policies><TimeBasedTriggeringPolicy interval="6" modulate="true" /><SizeBasedTriggeringPolicy size="50 MB" /></Policies><Filters><!-- 只记录info级别信息 --><ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL" /><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" /></Filters><!-- 指定每天的最大压缩包个数,默认7个,超过了会覆盖之前的 --><DefaultRolloverStrategy max="50" /></RollingRandomAccessFile><RollingRandomAccessFile name="rollingErrorFile" fileName="${fileName}/springboot-error.log" immediateFlush="false"filePattern="${fileGz}/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.springboot-error.gz"><PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} [%t] %-5level %logger{36} %L %M - %msg%xEx%n" /><Policies><TimeBasedTriggeringPolicy interval="6" modulate="true" /><SizeBasedTriggeringPolicy size="50 MB" /></Policies><Filters><!-- 只记录error级别信息 --><ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY" /></Filters><!-- 指定每天的最大压缩包个数,默认7个,超过了会覆盖之前的 --><DefaultRolloverStrategy max="50" /></RollingRandomAccessFile></Appenders><Loggers><!-- 全局配置,默认所有的Logger都继承此配置 --><AsyncRoot level="INFO" additivity="false"><AppenderRef ref="console" /><AppenderRef ref="rollingInfoFile" /><AppenderRef ref="rollingErrorFile" /></AsyncRoot></Loggers>
</Configuration>

有任何问题和建议,都可以向我提问讨论,大家一起进步,谢谢!

-over-

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/295390.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Prometheus+Grafana搭建Jmeter性能监控平台

&#x1f4e2; 专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2; 资源分享&#xff1a;耗时200小时精选的「软件测试…

vue3项目 - 使用 pnpm 包管理器来创建项目

创建项目 npm install -g pnpm pnpm create vue 输入项目名称、包名称、选择要安装的依赖&#xff0c;最后 pnpm install pnpm format #规范格式 pnpm dev #启动项目

【教程】使用ipagurd打包与混淆Cocos2d-x的Lua脚本

文章目录 摘要引言正文1. 准备工作2. 使用ipaguard处理Lua文件3. 运行ipagurd进行混淆代码加密具体步骤测试和配置阶段IPA 重签名操作步骤4. IPA重签名与发布 总结 摘要 本文将介绍如何使用ipagurd工具对Cocos2d-x中的Lua脚本进行打包与混淆&#xff0c;以及在iOS应用开发中的…

【华为OD机试真题2023CD卷 JAVAJS】多段线数据压缩

华为OD2023(C&D卷)机试题库全覆盖,刷题指南点这里 多段线数据压缩 知识点数组栈递归矩阵循环 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 下图中,每个方块代表一个像素,每个像素用其行号和列号表示。 为简化处理,多段线的走向只能是水平、竖直、斜向45…

地震勘探原理---数字滤波处理

目录 一. 地震数字滤波的目标 二. 数字滤波器 2.1 数字滤波器的分类 三. 一维数字滤波器 3.1 傅里叶变换与傅里叶逆变换 3.2 滤波流程 四. 二维数字滤波 4.1为什么有二维数字滤波 4.2 f-k域滤波 4.3 τ-p域滤波 4.4 相干滤波 四. 总结 一. 地震数字滤波的目标 核心任务&am…

使用 Spring Boot + MyBatis开发需要注意的事项以及开发模版

前言&#xff1a; 注意&#xff0c;本篇不适用于有相关开发经验的开发者&#xff0c;作为一个在职开发者&#xff0c;我经常在完成从0-1的模块&#xff0c;也就是从数据库表开始到创建实体类&#xff0c;以及dao层&#xff0c;Service层等业务需要添加相关注解&#xff0c;这样…

WEB渗透—PHP反序列化(八)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

【C语言】打印内存数据

C语言&#xff0c;用函数封装&#xff1a;16进制打印unsigned char *p指向的内存&#xff0c;长度为int l。16个字节&#xff0c;换一次行。16个字节用一个字符串缓存&#xff0c;一次打印。 以下是一个使用函数封装的C语言代码&#xff0c;用于以16进制格式打印unsigned char …

Tomcat远程调试

windows环境 写一个 startup-debug.bat&#xff0c;指定tomcat的根目录&#xff0c;端口自己定义 rem *******设置Tomcat目录*******-- set CATALINE_HOMED:\asd\A8-2\tomcat d: rem 8787为可用端口,为远程调试监听端口-- cd %CATALINE_HOME%/bin set JPDA_ADDRESS8787 set J…

单片机原理及应用:流水灯的点亮

流水灯是一种简单的单片机控制电路&#xff0c;由许多LED组成&#xff0c;电路工作时LED会按顺序点亮&#xff0c;类似于流水的效果。 下面是运行在keil上的代码&#xff0c;分别使用了数组&#xff0c;移位符和库函数来表示。 //数组法 #include <reg52.h> //头文…

跨平台应用程序开发软件,携RAD Studio 12新版上线

RAD Studio 是一款专为程序员而准备的跨平台应用程序开发软件&#xff0c;内置Delphi和CBuilder这两种开发工具&#xff0c;另外还提供了新的C功能&#xff0c;扩展了对ExtJS的RAD服务器支持&#xff0c;增强了对vcL的高dpi支持&#xff0c;提高了firemonk (FMX)的质量等等&…

今日港股期货(港股期货今日交易动向)

港股期货收涨0.6% 首次突破31000点 今日港股期货大涨&#xff0c;形势一时看好。其中&#xff0c;恒生指数期货一度突破31000点关口&#xff0c;创出历史新高。分析人士表示&#xff0c;市场情绪积极&#xff0c;投资者对于全球经济复苏前景和中国经济增长的预期不断提高&…