科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道应用软件在 Java Web 开发框架中创建 VoiceXML 页面

在 Java Web 开发框架中创建 VoiceXML 页面

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

对于绝大多数 Web 开发人员来说,输出 HTML、XHTML 乃至 XML 是一项微不足道的任务,它只不过是创建和部署 Web 应用程序的常规流程。从显示 Internet 页面到应答电话呼叫看似一个巨大的飞跃,但实际并非如此。在这篇文章中,您将看到相同的技术如何帮助您动态创建 Web 页面以便接听(虚拟的)电话并应答呼叫。

作者:Brett McLaughlin 来源:IBMDW 2007年8月31日

关键字: java web 框架 VoiceXML

  • 评论
  • 分享微博
  • 分享邮件

在本页阅读全文(共19页)

在过去五年中,Web 得到了比以往任何时候都长足的发展。一度主要以文本为基础的软件程序媒介 “Web 浏览器” 现已发展成为一种可供任何具有连通性的设备使用的信息源。最早列入可访问 Web 页面的设备列表的是移动电话,随后是寻呼机、手持设备、个人电子助理和其他任何可通过无线连接连入 Web 的设备。在最近几年,电话也加入了这一阵营,通过普通电话线路使用 Web 程序的呼声也越来越高。

这种最新型的应用程序(用户通过电话访问在线服务)的更恰当的名称是电话应用程序(telephone application)。显而易见,由于电话无法用来 “单击一个链接”,应用程序交互几乎全部是通过语音处理的。用户不是采用单击链接的方式,而是说出 “账户信息” 或使用键盘按预先录制好的指令进行操作。

通过现有(或略加修改的)Web 应用程序为电话提供服务的能力是一种强大的想法,也是许多 Web 开发人员都渴望探索的领域之一。关于 Web 应用程序与电话应用程序,要了解的最重要的一件事就是您实际上可以使用相同的技术组合来创建这两种应用程序。HTML、XHTML 和 XML 是 Web 界面之下最常用的三种底层技术,VoiceXML(或简称为 VXML)则是一种密切相关的技术,它使得电话客户机可以利用 Web 交互。JavaServer Pages 与 servlet、PHP 脚本以及 Ruby 应用程序均可响应电话请求,就像响应那些进入手持设备或 Web 浏览器的请求一样轻松。在这篇文章中,我们主要关注使用 Java 平台为简单的 VoiceXML 应用程序提供服务,但您可将本文介绍的方法同样地应用到 PHP、Perl 或您选择的任何编程语言。

VoiceXML、CCXML 还是 CallXML?

从 Voxeo 开始!

您需要首先建立一个免费的 Voxeo 开发者账户,这样才能在学习本文过程中配合练习。尽管 Voxeo 并不是任何 VoiceXML 的服务所必需的,但它确实提供了一套出色的工具,更有数千份 VoiceXML、CallXML 和 CCXML 文档页面。本文同时介绍 Voxeo 与 VioiceXML 编程,后续 developerWorks 还将为您提供更多有关 Voxeo 的内容。

构建语音应用程序最常用的标准就是 VoiceXML。绝大多数 VXML 浏览器都支持 VoiceXML 2.0,本文通篇将使用这个版本的 VXML。VXML 符合 W3C 规范且发展迅速,目前的版本依然是 v2.1。VXML 3.0 即将推出。

CCXML 是 Call Control XML 的缩写,也是电话标记方面符合 W3C 规范的最新一员。CCXML 比大多数 VoiceXML 实现更为高级,提供了对回叫、事件侦听器和多路及多方会话的支持。但除非您特别需要这些属性,否则最好的选择或许是继续使用 VoiceXML,VoiceXML 更加稳定,应用也更广泛。

CallXML 是特定于 Voxeo 的一种平台。CallXML 学习起来非常轻松,并提供了对按键电话(touchtone)输入的支持(请注意,它并不支持语音识别)。CallXML 的最大缺陷就是特定于厂商。Voxeo 是一个非常出色的站点,具有无数资源,但被一家厂商锁定绝对不是个好主意。此时,大多数开发人员会再次发现,VoiceXML 更适合他们的需求。


VoiceXML 101

在接触 VoiceXML 世界的 Java 方面之前,您应大致理解 VoiceXML 应用程序的工作原理。之后,我将为您介绍一个非常简单的 VoiceXML 应用程序。示例应用程序将使您能够查看 VXML 文件,同时确保您有权访问(且可使用)Voxeo 的 call-assignment 服务,这个服务对本文后面的内容非常关键。

一个简单的 VXML 页面

VoiceXML 最少要以一个 VXML 文件开始,使用 VoiceXML 风格的 XML 版本告诉电话应用程序它们应该以及能够作些什么。清单 1 给出了一个非常简单的 VXML 文件。将这个文件保存到您的本机上(可通过 下载 部分下载完整的示例源代码,但无论如何,您应该养成自己处理此类文件的习惯)。


清单 1. 一个非常简单的 VXML 文件
                

<?xml version="1.0" encoding="UTF-8"?>

<vxml version="2.1">
  <form>
    <block>
      <prompt>
        Things are working correctly! Congratulations.
      </prompt>
    </block>
  </form>
</vxml>


对于 VoiceXML 来说,这非常基础,如果您对语法的了解还不够清楚,请查看 参考资料 中列出的其他 VoiceXML 文章。清单 1 中的 VXML 文件只包含一条提示信息,未提供任何交互功能,在处理 Java 代码的一节中,您将看到更高级的用法。但目前,使用这个简单的测试用例来确保您的环境工作正常。

上传应用程序

接下来,将您的 VXML 文件放在某个可以访问的位置。如果您有 ISP,只需将 VXML 文件上传到您的 Web 站点,您可能也会希望在 Web 根目录下为您的 VoiceXML 文件创建一个目录,例如 /voicexml 或 /voice。确保这些目录和文件可通过 Web 访问(如果您不清楚如何进行这些操作,请咨询您的系统管理员或 ISP)。

如果您没有 ISP,那么可以在 Voxeo 注册,以便使用该站点的 File Manager。您应已建立了一个 Voxeo 账户,它附带 10 MB 的主机空间,因此这是个不错的免费选择。(10 MB 可以容纳大量 VXML 文件!)

使 VXML 应用程序联机之后,我们还想确定能够通过在 Web 浏览器中输入 URL 来访问它。根据您所使用的具体浏览器不同,可能会要求您下载 XML 文件,也可能会在您的浏览器中以某种形式呈现它。这只是一个测试,确保您的 VXML 可用,因此即使您的计算机没有开始跟您交谈,也不要太过忧虑。VXML 联机后,也就作好了将其与一个电话号码连接的准备。

为您的应用程序分配一个电话号码

最后一次呼吁您使用 Voxeo!

如果您尚未注册获得一个 Voxeo 账户,那么现在就去注册吧!从这里开始,以下的示例都需要您使用 Voxeo 工具。建立账户是免费的,没有任何责任,您更会获得杰出的工具与支持。现在就去 注册获得一个开发者账户吧

与传统的 Web 应用程序不同,您无法直接打开 Web 浏览器然后浏览您的 VXML 文件,至少在您希望获得语音应答时不能这样做。为了测试基于电话的应用程序,您显然需要一台电话,这就意味着一个呼叫号码。有许多高成本的方法可以将号码映射到 VoiceXML 应用程序,但对于测试、登台(staging)和开发而言,Voxeo 提供了一种出色的免费映射服务。

导航到 Voxeo.com,登录(使用页面左上角的字段)。在 Account 菜单中选择 Application Manager,如 图 1 所示。


图 1. 使用 Voxeo Application Manager
选择 Application Manager 选项

选择 Add Application,然后选择 VoiceXML 2.0 作为部署平台。

接下来,提供您的 VXML 文件的 URL,另外还有您的应用程序的名称,您可以按照自己的偏好任选名称。图 2 展示了访问我的 VXML 文件的设置。从 Application Phone Number 下拉菜单中选择 Staging 选项。这将为应用程序分配一个临时登台电话号码(temporary staging phone number),以使您可以真正地用您自己的电话呼叫这个号码。


图 2. 将一个 VXML 文件映射到一个电话号码
向 Voxeo 提供您的应用程序的 URL 和名称

单击 Create Application,Voxeo 将为您的应用程序分配一些电话号码。图 3 展示了最终屏幕(略微向下滚动了一点),以及 VXML 文件的所有访问点。


图 3. 成功映射!
Voxeo 提供了多种访问您的应用程序的途径

这一功能值得您花时间去注册 Voxeo,您现在可以通过长途电话号码、800 免费电话号码和 Skype 访问您的 VXML 文件,而这些方法还只是其中的一小部分。这非常好,因为您不必使用 Voxeo 工具去测试应用程序。更好的是,您可以让您的老板在无需具备 Voxeo 站点账户的情况下完成测试!

测试应用程序

剩下的工作就是呼叫一个 Voxeo 提供的号码。拨号后,您的 VXML 应用程序应获取号码,并让您知道(用一种单调机械的声音):“Things are working correctly! Congratulations.”

好,就是这样:在大约五分钟内,您使您的电话与一个 XML 文件进行了交谈。现在就可以看看 Java 代码了,并了解如何动态地生成 VXML。


Java 和 VXML

这里,大多数 Java 开发人员都试图在自己的 Java Servlet 中手工编码 VXML,添加数百行的 out.println() 语句、为输出的内容类型而操心,通常也会给许多应用程序增加严重且不必要的复杂性。动手处理那些较为复杂的编程任务之前(只要应用得当,它们都是很有用的),请先通过本节了解一些关于 VoiceXML Servlet 编程的最基本的内容。

创建一个 VXML 文件的原型

首先要开发 VXML 文件。不要打开一个 IDE 并开始编写 Java 代码,而是启动一个文本编辑器,忍住立即添加 packageimport 语句的渴望。构建一个简单的 VXML 文件,就像本文前面给出的示例那样。

例如,清单 2 是又一个非常基础的 VXML 文件。它是一个语音识别 VXML 文件,接入一个恰当的设备并提供某些关于呼叫选择的注释。


清单 2. 另外一个基本的 VXML 文件
                

<?xml version="1.0" encoding="UTF-8"?>

<vxml version="2.1">
  <form id="MainMenu">
    <field name="instrument">
      <prompt>What is your  favorite musical instrument?</prompt>

      <!-- Insert an inline grammar -->
      <grammar type="text/gsl">
        [guitar mandolin dobro (violin fiddle) banjo]
      </grammar>

      <!-- Handle the case when they give no answer -->
      <noinput>
        Did you say something? I didn't hear you.
        <reprompt />
      </noinput>

      <!-- Handle the case when no match is found -->
      <nomatch>
        I suppose that's OK, but it's not on my top five. 
        Want to try again?
        <reprompt />
      </nomatch>
    </field>

    <!-- Handle the various options. -->
    <filled namelist="instrument">
      <if cond="instrument == 'guitar'">
        <prompt>That's right! Hang up and go practice.</prompt>
      <elseif cond="instrument == 'mandolin'" />
        <prompt>Nice... and only four strings to keep in tune.</prompt>
      <elseif cond="instrument == 'dobro'" />
        <prompt>Boy, that's no fun to learn, is it?</prompt>
      <elseif cond="instrument == 'violin'" />
        <prompt>We call that a fiddle, Mr. Fancy Pants.</prompt>
      <elseif cond="instrument == 'fiddle'" />
        <prompt>Does playing classical music on a 
        fiddle make it a violin?</prompt>
      <elseif cond="instrument == 'banjo'" />
        <prompt>Wow, I hope you live alone.</prompt>
      </if>
    </filled>
  </form>
</vxml>



编写这个 VXML、保存它、将它上传到 ISP,然后为它分配一个号码。只有在您完成所有这些步骤后 —— 确保您的 VXML 正常工作,才是准备好了,可以开始 考虑编写 Java 代码。

如果您直接跳到 Java,那么很可能会导致输出中出错,代码中也会出错。结果是要在一个 Web 框架内尝试同步调试一个 VXML 文件(XML)和一个 Servlet(Java),这种调试极其艰难。不要添加所有这些变量(没有双关的意思),务必从一个可正常工作的 VXML 文件入手。然后 准备运行 Java 代码。

读入文件

准备好 VXML 可供使用后,您也就为开始编码作好了最终的准备。首先从一个仅载入 VXML 文件的 Servlet 开始。清单 3 是一个实现此功能的 Servlet —— 载入 清单 2 中开发的 VXML。这段代码没有任何输出,所以期望值暂时不要太高。


清单 3. 载入一个 VXML 文件
                

package com.ibm.vxml;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;

public class VoiceXMLServlet extends HttpServlet {

  private static final String VXML_FILENAME =
    "simple-voice_recog.xml";

  public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    String vxmlDir = getServletContext().getInitParameter("vxml-dir");

    BufferedInputStream bis = null;
    ServletOutputStream out = null;

    try {
      // Load the VXML file
      File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
      FileInputStream fis = new FileInputStream(vxml);
      bis = new BufferedInputStream(fis);

      // Output the VXML file 
      int readBytes = 0;
      while ((readBytes = bis.read()) != -1) {
        // output the VXML
      }
    } finally {
      if (out != null) out.close();
      if (bis != null) bis.close();
    }
  }
}


这段代码非常直观。它载入一个 XML 文件 —— 通过 servlet 的配置上下文中的目录和一个常量文件名指定,然后遍历文件内容。您要将文件的路径硬编码到 servlet 中,但至少将目录名存储到 Web.xml 文件中是一个非常不错的主意,此文件位于 servlet 上下文的 WEB-INF/ 目录下。清单 4 展示了 Web.xml 中的上下文参数。


清单 4. servlet 的上下文参数
                
  <context-param>
    <param-name>vxml-dir</param-name>
    <param-value>/path-to-your-voicexml-dir/voicexml</param-value>
  </context-param>



若编译 servlet 并尝试在 Web 浏览器中载入它,您只会看到一个空白的屏幕,同样,您应确保至少会看到这样的空白屏幕。如果得到错误,就需要予以更正。例如,常常会出现文件访问问题或 VXML 文件路径录入错误。一旦得到了空白屏幕,也就准备好实际输出 VXML 文件了。

从 servlet 中输出 VXML

首先,您需要访问一个输出对象,这样才能向浏览器发送内容。这非常简单:

      
// Load the VXML file
      File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
      FileInputStream fis = new FileInputStream(vxml);
      bis = new BufferedInputStream(fis);

      // Let the browser know that XML is coming
      out = res.getOutputStream();
            

从文件提取内容也非常简单,只要使用一行代码即可:

      
// Output the VXML file 
      int readBytes = 0;
      while ((readBytes = bis.read()) != -1) {
        // output the VXML
        out.write(readBytes);
      }


虽然上述代码看似已经足够,但您依然需要告知浏览器您正在向它发送 XML。切记,浏览器用于 HTML,某些浏览器可能无法顺利接收 XML。您可设置内容类型,也可设置内容的长度,只要再次使用 HttpServletResponse 对象即可:

     
 // Let the browser know that XML is coming
      out = res.getOutputStream();
      res.setContentType("text/xml");
      res.setContentLength((int)vxml.length());
            

清单 5 展示了添加到前文介绍的 清单 3 给出的 servlet 中的所有代码。


清单 5. 完整且准备好载入 VXML 文件的 VoiceXMLServlet
                

package com.ibm.vxml;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;

public class VoiceXMLServlet extends HttpServlet {

  private static final String VXML_FILENAME =
    "simple-voice_recog.xml";

  public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    String vxmlDir = getServletContext().getInitParameter("vxml-dir");

    BufferedInputStream bis = null;
    ServletOutputStream out = null;

    try {
      // Load the VXML file
      File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
      FileInputStream fis = new FileInputStream(vxml);
      bis = new BufferedInputStream(fis);

      // Let the browser know that XML is coming
      out = res.getOutputStream();
      res.setContentType("text/xml");
      res.setContentLength((int)vxml.length());

      // Output the VXML file 
      int readBytes = 0;
      while ((readBytes = bis.read()) != -1) {
        // output the VXML
        out.write(readBytes);
      }
    } finally {
      if (out != null) out.close();
      if (bis != null) bis.close();
    }
  }
}

测试 servlet 载入的 VoiceXML

完成上述更改后编译您的 servlet,若需要请重启 servlet 引擎。浏览 servlet,您应看到如 图 4 所示的输出结果。成功!


图 4. VoiceXML servlet 输出 VXML
大多数浏览器都会给为您给出某种 XML,呈现您的 VXML 文件

若您未得到类似输出,确定您的文件是否位于您希望的位置,并确保没有任何权限问题。您还要检查 servlet 引擎的日志或请求系统管理员的帮助。

现在就准备好将电话号码映射到您的 servlet 了。重新回到 Voxeo.com 的 Application Mnager,添加一个新应用程序(可能会看到之前您使用过的应用程序)。确保选中 VoiceXML 2.0,然后输入新应用程序的名称和 servlet 的 URL。Voxeo 将创建应用程序并为其分配一个电话号码。

拨入这个新号码,您应听到 清单 2 中的 VXML 给出的提示。祝贺您!您已经编写好了一个输出 VXML 的 Java servlet 的代码,还在其中挂接了一个电话号码。

部分可选的附加项

您可能希望向 servlet 代码中添加一些小附加项。它们都不是必需的,但都会给现有的版本增加一些健壮性和文档。

首先,您可能想允许用户通过 POST 请求访问 VXML。这可能在用户单击表单上的一个按钮时发生,该表单将对 VoiceXMLServlet 作出一个 POST 请求。在 servlet 中处理这一操作非常简单,只要编写一个委托已有 doGet() 方法的 doPost() 即可,如下所示:

  
public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    doGet(req, res);
  }


另外一个简单的附加项用于允许浏览器切实获知您正在输出一个 VXML 文件的内容。为此,设置 servlet 中的 Content-disposition 响应头,如下:

      
// Let the browser know that XML is coming
      out = res.getOutputStream();
      res.setContentType("text/xml");
      res.setContentLength((int)vxml.length());
      res.addHeader("Content-Disposition",
        "attachment; filename=" + vxml);
            

现在读取您的响应的浏览器(或其他代码)就可以发现所服务的 VXML 文件了。但务必不要包含完整的文件路径,这会造成安全隐患!


动态 VoiceXML

有了输出 VXML 文件的 servlet 之后,将其转换成动态输出 VXML 的 servlet(使用代码作为模型或模板)轻而易举。换句话说,您可以超越简单地载入静态的 VXML 文件,开始通过编程创建 VXML。

当您开始考虑动态 VoiceXML 时,Java 平台就显示出了自己的优势。它提供了轻松输出 XML 的能力,还有与数据库、目录服务器、身份验证存储和会话的交互。此外,它还能够证实,构建动态 VXML 将消除基于语音的系统的部分刻板性。

在这一节中,我将逐步为您介绍创建一个输出动态 VXML 的 Java servlet 的步骤。

通过 out.println() 输出 VXML

您已经了解了如何访问 ServletOutputStream,然后在输出流中插入字节。但如果从源(例如一个静态 VXML 文件)传输到输出流的不仅仅是字节,那么直接处理字节的方式几乎无法管理控制。

如果您希望自行创建 VXML,最好使用 PrintWriter。利用这个类,您可发出整个字符串,使之对于创建和输出动态内容更为有用。这只需要对代码略加修改,如下所示:

  
public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    String vxmlDir = getServletContext().getInitParameter("vxml-dir");

    BufferedInputStream bis = null;
    ServletOutputStream out = null;

    try {
      // Load the VXML file
      File vxml = new File(vxmlDir + "/" + VXML_FILENAME);
      FileInputStream fis = new FileInputStream(vxml);
      bis = new BufferedInputStream(fis);

      // Let the browser know that XML is coming
      PrintWriter out = res.getOutputStream();
      res.setContentType("text/xml");
      res.setContentLength((int)vxml.length());

      // Output content using PrintWriter
    } finally {
      if (out != null) out.close();
      if (bis != null) bis.close();
    }
  }


另外,不要忘记导入 java.io.PrintWriter 类:它不会自动成为对您的 servlet 的代码基可用。

使用 PrintWriter,您现在可以输出基于字符串的内容了。例如,清单 6 输出与 清单 1 相同的 VXML,但是通过 servlet 输出,并未从静态文件载入 VXML 内容。


清单 6. 动态输出 VXML
                

package com.ibm.vxml;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.*;
import javax.servlet.http.*;

public class DynamicVoiceXMLServlet extends HttpServlet {

  public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    BufferedInputStream bis = null;
    PrintWriter out = null;

    try {
      // Let the browser know that XML is coming
      out = res.getWriter();
      res.setContentType("text/xml");

      // Output VXML
      out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
      out.println("<vxml version=\"2.1\">");
      out.println(" <form><block><prompt>");
      out.println("  Things are working correctly! Congratulations.");
      out.println(" </prompt></block></form>");
      out.println("</vxml>");

    } finally {
      if (out != null) out.close();
      if (bis != null) bis.close();
    }
  }

  public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    doGet(req, res);
  }
}


现在您可编译这个 servlet、向 Voxeo 注册它,并通过电话访问它,与处理 清单 1 的方法相同。现在我们来看一些示例,从而展示像 Java 这样的语言的动态编程能力。

添加时间提醒

基于 servlet 的 VXML 输出的一项最简单的用途就是添加时间提醒。利用 Java 代码获取当前日期和时间非常轻松,因此这是个不错的起点。

使用 Calendar 类可轻松获得一天中的具体时间(实际上,可以获得与当前日期相关的任何内容)。清单 7 给出了获得 Calendar 类新实例的代码,从而得到一天中的具体时间(以 24 小时的格式返回),然后根据这个时间组合出一条简单的欢迎词。


清单 7. 动态输出 VXML
                

package com.ibm.vxml;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import javax.servlet.*;
import javax.servlet.http.*;

public class DynamicVoiceXMLServlet extends HttpServlet {

  public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    BufferedInputStream bis = null;
    PrintWriter out = null;

    try {
      // Let the browser know that XML is coming
      out = res.getWriter();
      res.setContentType("text/xml");

      // Output VXML
      out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
      out.println("<vxml version=\"2.1\">");
      out.println(" <form><block><prompt>");

      // Output a greeting based on the time of day
      Calendar cal = Calendar.getInstance();
      int hour = cal.get(Calendar.HOUR_OF_DAY);
      if (hour < 6) {
        out.println("You're up early. Good morning.");
      } else if (hour < 12) {
        out.println("Good morning. How's your day so far?");

      } else if (hour < 18) {
        out.println("Half the day is done... good afternoon!");
      } else{
        out.println("Hope you are enjoying your evening.");
      }

      out.println(" </prompt></block></form>");
      out.println("</vxml>");

    } finally {
      if (out != null) out.close();
      if (bis != null) bis.close();
    }
  }

  public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

    doGet(req, res);
  }
}

呼叫者和 VXML 生成者之间的差异

清单 7 也展示了 Voice XML 与动态生成的 VXML 的另外一项特性:呼叫者可能并非 VXML 本身。例如,假设有一名居住在新西兰的用户呼叫 清单 7 所示的应用程序。若当时新西兰时间是 10:00 PM,但输出 VXML 的服务器位于科罗拉多州的丹佛,两者之间问候的消息可能非常古怪,例如 “您起得真早,早上好!” 这完全不恰当,情况还有可能更糟:如果您已为每周的特定日子添加好了问候语,那么实际上总会出现匹配错误。

根本问题源于 VXML 和 Java 是在特定服务器所在的位置和时区运行的,但可为世界各地的呼叫者所用。如果您的 servlet 未考虑到这方面的问题,就会令一些呼叫者倍感困惑。您有以下几种选择:

  • 忽略差异,寄希望于呼叫者了解您的服务器并非在他们所在的时区运行代码。
  • 明确声明日期和时间对应于服务器所在位置。例如,下午问候语可以是 “本地现在是下午。祝您愉快”。
  • 编写代码询问时区或与 GMT 的时差,然后根据这一信息提供问候语。

遗憾的是,这些选择中没有任何一种富于吸引力。第一种选择的所作所为正如所说明的那样:基本忽略呼叫者。不言而喻,忽略呼叫者绝非 赢得和维持业务关系的正确方法。第二种想法 —— 给出本地时间并明确说明这是本地时间 —— 也没有太大的帮助,因为这种方法依然倾向于忽略呼叫者,只是在此过程中考虑得略微周全一些。

最后一种选择乍看上去似乎很有吸引力,可以很容易地编写出 VXML,允许用户提供一个与 GMT 的时差值,然后根据该信息应答。但呼叫者倾向于尽可能迅速地获得信息,应答提示越多,惹恼呼叫者的风险就越大,他们可能会不满地挂断电话。因此,除非您提供的以日期或时间为基础的服务,否则要求呼叫者指明时区就是浪费资源。甚至可能更糟的是,很多呼叫者并不知道自己所在地与 GMT 的时差,因此您要面临提供时区、时区缩写、夏令时……那将是一个冗长烦琐的列表。

为什么要自寻烦恼?

那么我们为什么要介绍这种基于日期的 VXML 生成呢?很大程度上是因为它正展示了这些问题!您需要密切关注您的听众,尽力只提供那些与他们有关的信息,而不是与您的服务器或您所在地有关的信息。

对于基于日期的处理,您要学习的课程就是:或许应该采用一种更好的最终选择来应对呼叫者,除非绝对必要,否则应完全避免采用基于日期或时间的事务。如果您预计到将有来自其他时区的呼叫者,那么提供与时间相关的功能就是自找麻烦。这一原则同样适用于任何随州、国家或陆地标线的不同可能发生变化的数据。

最后,有很多时候使用 servlet 输出 VXML 并非良策。如果您只是要从一个静态文件中提取 VXML,使用 servlet 带来的好处极为有限(可能只有一点灵活性而已),但要添加代码、编译、调试、一个 servlet 引擎和其他许许多多的东西,从而使语音应用程序的复杂性大为增加。在这些简单的情况下,应继续使用静态 VXML 文件。


一些有趣的想法

致此,您已看到,有时 servlet 生成的 VXML 并无 意义。在结束本文之前,考虑以下几种情况,在这些时候使用 Java 这样的语言是绝佳 的电话应用程序解决方案。此处未提供完整的示例,以后的文章中将予以介绍。

从一个数据库中载入 VXML

最显而易见的与 VoiceXML 相关的 Java 应用程序就是:使用数据库提供动态 VXML 输出。这或许也是大部分读者在选择阅读本文时希望了解的内容(但这篇文章中没有核心示例,因此您可能未学到足够多的知识)。无论如何,JDBC 都能使您轻松连接数据库,然后利用 SQL 查询的结果填充 VXML。

例如,您要开发一个表,包含 VXM 的全部语法信息,然后将这些语法载入您所输出的每个 VXML 文件中。您不必为每一个 VXML 文件编码语法,而是可以在类似的文件间共享语法。更好的是,您可在所有 servlet 或一个特定 servlet 的实例中预载入这些语法。从而得益于将语法存储在数据库中,无需浪费成本为每个请求载入语法。

根据用户凭证载入 VXML

另外一项出色的 Java 功能 —— 特别是在与 servlet、JSP 和基于 Web 的编程相关时 —— 就是在会话中存储用户凭证的能力。这为您带来稳定的身份验证和授权,以及高度定制的内容。

例如,考虑一个语音应用程序,从询问用户 ID 号和 PIN 开始(与当今的大多数银行或金融应用程序类似)。您可根据数据库(依靠 Java 平台的强大力量)对这些凭证进行验证,然后将呼叫者的 ID 存储到一个会话变量中。此后,每个处理这名呼叫者的请求的 Java servlet 或 JSP 都可根据这些凭证了解为用户提供哪些选择。

尽管许多 VoiceXML 替代产品都提供了类似的功能性,但很少有产品以与其基于 Web 的应用程序版本之间共享代码为自豪。换言之,Java 平台允许您在 VoiceXML 应用程序及其基于 Web 的版本间共享的不仅仅是数据库,还包括代码组件。生成 VXML 的 servlet 可使用相同的身份验证和授权工具类作为生成 HTML 和 XHTML 的 servlet,应答电话呼叫的 JSP 可与处理 HTTP 请求的 JSP 共享缓存数据库连接。因而,您将得到一个能够处理多种类型客户机的应用基础设施,而不必为每种类型的客户机创建一个完整的应用程序。


结束语

本文蜻蜓点水地介绍了可用 VXML 和 Java 平台实现的功能。介绍了开发 VXML 的过程,然后为您展示了如何将 Java 技术整合到这一过程之中。介绍过程中给出了很多线索,告诉您利用 Java 代码来开发丰富、动态的 VoiceXML 应用程序的所有有趣的方式。

我还说明了 VoiceXML 开发人员在语音应用程序误用 Java 技术的几种常见形式。处理日期和时间时耍小聪明、试图提供地方性的服务或是忘却服务器当地时间和呼叫者当地时间之间的差异无疑会令用户灰心离去。应将 Java 视为 VoiceXML 的一种工具,不要滥用 DateCalendar 类。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁惧墽鎳撻—鍐偓锝庝簻椤掋垺銇勯幇顏嗙煓闁哄被鍔戦幃銏ゅ传閸曟垯鍨介弻锝夘敇閻曚焦鐤佸Δ鐘靛仦閻楁洝褰佸銈嗗坊閸嬫挸鈹戦檱閸嬫劗妲愰幒妤€绠涙い鎾楀嫮鏉归梻浣告惈閻鎹㈠┑鍡欐殾闁割偅娲栭悡娑樏归敐澶嬫暠闁活偄绻樺缁樻媴閸涘﹥鍎撳銈嗗灥閹虫﹢寮绘繝鍥ㄦ櫜濠㈣泛锕ㄩ幗鏇㈡⒑閸濆嫭绁紒杈ㄦ礋閹潡鍩€椤掑嫭鐓熼幖鎼灣缁夐潧霉濠婂懎浠︾紒鍌涘浮閹瑩顢楅崒婊呮婵犵數鍋涢悧鍡涙倶濠靛鍊堕柣妯肩帛閻撳繘鏌涢銈呮瀾闁稿﹥鍔楅埀顒侇問閸犳牠鈥﹀畡閭﹀殨闁圭虎鍠楅弲顒勬煕閺囥劌骞楁繝銏″灴濮婅櫣鎷犻幓鎺戞瘣缂傚倸绉村Λ娆戠矉瀹ュ鍐€妞ゆ挾鍋熼敍娑㈡⒑鐟欏嫬绀冩い鏇嗗洤绀冮柍褜鍓欓—鍐Χ閸℃ê钄奸梺鍛婃煥濞撮鍒掑▎鎺旂杸婵炴垶鐟㈤幏娲煟閻樺厖鑸柛鏂胯嫰閳诲秹骞囬悧鍫㈠幍闂佸憡鍨崐鏍偓姘炬嫹

    濠电姷鏁告慨鐑藉极閸涘﹥鍙忛柣鎴f閺嬩線鏌涘☉姗堟敾闁告瑥绻橀弻锝夊箣濠垫劖缍楅梺閫炲苯澧柛濠傛贡缁骞掗弬鍝勪壕闁挎繂绨肩花浠嬫煕閺冩挾鐣辨い顏勫暣婵″爼宕卞Δ鈧ḿ鎴︽⒑缁嬫鍎愰柟鐟版喘瀵鈽夐姀鈥充簻闂備礁鐏濋鍛閹绢喗鈷戠紒顖涙礃閺夊綊鏌涚€n偅灏い顏勫暣婵″爼宕卞Δ鈧ḿ鎴︽⒑缁嬫鍎愰柟绋垮⒔濡叉劙骞橀幇浣告倯闂佸憡绮岄崯鎶藉触椤愨懡鏃堟偐闂堟稐绮堕梺鍝ュ櫏閸嬪鎮橀幒妤佺厽闁绘ê寮剁粚鍧楁倶韫囨梻鎳呯紒顕嗙秮閹瑩鎮滃Ο閿嬪闁荤喐绮庢晶妤冩暜閹烘挾顩插ù鐓庣摠閻撴洟鏌熼幆褜鍤熸繛鍙夋尦閺岀喖顢欓挊澶婂Б闁绘挶鍊栭妵鍕疀閹炬潙娅濋梺褰掓敱濡炶棄顫忔繝姘<婵ê宕·鈧梻浣告啞椤ㄥ棗煤閻旈鏆﹀┑鍌溓瑰敮闂侀潧锛忛崟顓炲及婵犵數濮烽弫鍛婃叏閹绢喗鏅濋柕鍫濇礌閸嬫挸顫濋悡搴$睄閻庤娲╃紞鈧紒鐘崇洴瀵噣宕掑⿰鍛潓婵犵數濮烽弫鍛婃叏閹绢喖纾圭紓浣股戝▍鐘崇箾閹存瑥鐏柣鎾存礋閹鏁愰崘銊ヮ瀳婵犵鈧尙鐭欓柡灞炬礋瀹曟儼顦叉い蹇e幘閳ь剚顔栭崰鏇犲垝濞嗘劒绻嗘慨婵嗙焾濡插綊姊洪柅鐐茶嫰婢ь垶鎮介銈囩瘈鐎殿喖顭烽幃銏ゅ礂閻撳海妾┑鐘灱濞夋盯鏁冮妷銉㈡灁濠电姵纰嶉埛鎴︽煕濠靛棗顏柣蹇涗憾閺屾盯鎮╁畷鍥р拰濡ょ姷鍋涢崯顐︺偑娴兼潙閱囨繝闈涚墕楠炴劙姊绘担鍛靛綊寮甸鍌滅煓闁规儳绠嶆禍褰掓倵閿濆骸鏋熼柣鎾跺枛楠炴牕菐椤掆偓閳ь兙鍊曞玻鍧楀箛椤撶姷顔曢梺鍛婄懃椤ャ垽顢旈崼鐔蜂患濠电娀娼ч悧蹇涙儗濞嗘挻鍋i柟顓熷笒婵¤姤绻涢崼銉х暫婵﹥妞藉畷婊堝箵閹哄秶鍑规繝鐢靛仜瀵爼鎮ч悩璇茬畺闁炽儲鏋煎Σ鍫ユ煏韫囥儳纾块柛姗€浜堕弻锝堢疀閺囩偘绮舵繝鈷€鍌滅煓闁诡垰鐬奸埀顒婄秵閸嬪棛绮绘ィ鍐╃厱妞ゆ劑鍊曢弸鎴︽煕婵犲啫濮堥柟渚垮妽缁绘繈宕熼鐐殿偧闂備胶鎳撻崲鏌ュ箠閹邦喖鍨濋柣銏㈩暯閸嬫捇鏁愭惔婵堝嚬闂佷紮绲奸崡鍐差潖缂佹ɑ濯撮悷娆忓娴犫晠姊洪崨濠冪叆妞ゆ垵顦悾鐑芥晸閻樿尙顦ㄩ梺鑲┾拡閸撴艾鈻撴ィ鍐┾拺闂傚牊绋撶粻鐐烘煕婵犲啯绀嬬€规洘锕㈤崺鈧い鎺嗗亾妞ゎ亜鍟存俊鍫曞幢濡も偓椤洭姊虹粙鍧楊€楃€规洦鍓熼幆鍐敆閸曨兘鎷婚梺绋挎湰閻熝囧礉瀹ュ鍊电紒妤佺☉閹虫劙鎯屽▎鎾寸厵閻庣數枪琚ラ梺绋款儐閹告悂锝炲┑瀣亗閹艰揪绱曢惈鍕煟鎼淬値娼愭繛鍙夊灴瀹曪繝宕橀懠顒佹闂佸憡顨堥崐锝夋偄閸忓皷鎷归梺褰掑亰閸犳艾鈻旈崸妤佲拻闁稿本鐟х粣鏃€绻涙担鍐叉处閸嬪鏌涢埄鍐槈缂佺姷濞€楠炴牕菐椤掆偓閻忣亪鏌涘▎蹇曠闁靛洤瀚伴獮鎺楀幢濡炴儳顥氶梻鍌欑閻ゅ洭锝炴径鎰瀭闁秆勵殔閺勩儵鏌曟径鍡樻珔妤犵偑鍨烘穱濠囨倷閹绘巻鎸冮梺鍛婂灱椤鎹㈠┑瀣仺闂傚牊鍒€閻愮儤鐓曢柕濠忛檮閵囨繈鏌℃担鍓插剶闁哄睙鍥ㄥ殥闁靛牆鎳嶅▽顏呯箾閿濆懏鎼愰柨鏇ㄤ簼娣囧﹪宕奸弴鐐茬獩濡炪倖鏌ㄩ崥瀣敂閿熺姵鈷掑ù锝呮嚈瑜版帩鏁勯柛娑欐綑绾惧綊鏌涢敂璇插箺鐎规洖寮剁换娑㈠箣濞嗗繒浠煎Δ鐘靛亼閸ㄧ儤绌辨繝鍥舵晬婵犲灚鍔曞▓顓犵磼閻愵剚绶茬紒澶婄秺瀵鏁愰崱妯哄妳闂侀潧楠忕徊浠嬎夊┑鍡╂富闁靛牆绻愰々顒勬煛娴g瓔鍤欐い鏇悼閹风姴顔忛鍏煎€梻浣稿閸嬫帡宕戦崨鎼晛闁搞儺鍓氶埛鎺懨归敐鍛暈闁哥喓鍋ら弻鐔煎箹娴h櫣鏆悗瑙勬礈婢ф骞嗛弮鍫氣偓锕傚箣閻愬瓨鐝ㄩ梻鍌欑劍鐎笛呮崲閸岀倛鍥ㄧ鐎n亝鐎梺绯曞墲閵囨粓鍩€椤掍礁绗掓い顐g箞椤㈡绻濋崒姘兼浆闂傚倷鐒﹀鍧楀储娴犲鈧啴宕卞▎鎰簥濠电偞鍨崹鍦不婵犳碍鐓涘璺侯儏閻忊晠鏌涢弬鎯у祮婵﹨娅g划娆忊枎閹冨闂備礁婀遍幊鎾趁洪鐑嗗殨濠电姵纰嶉弲鏌ユ煕濞戝彉绨兼い顐㈢Т閳规垿鎮欓崣澶樻!闂佸憡姊瑰ú婊勭珶閺囥垹绠柤鎭掑劗閹锋椽姊洪崨濠勭畵閻庢凹鍘奸敃銏ゅ箥椤斿墽锛滈柣搴秵閸嬪嫰鎮橀幘顔界厱闁冲搫鍟禒杈殽閻愬樊鍎旈柡浣稿暣閸┾偓妞ゆ巻鍋撴い鏂跨箰閳规垿宕堕妷銈囩泿闂備礁鎼ú銊╁磻閻愬搫闂繛宸簼閻撳啴鎮归崶顏嶅殝闁告梻鍠撶槐鎺撴綇閵婏箑纾抽悗瑙勬礃鐢帡鍩㈡惔銊ョ婵犻潧妫悗鏉戔攽閿涘嫬浜奸柛濠冨灴瀹曟繂鐣濋崟顐ょ枃濠殿喗銇涢崑鎾搭殽閻愬弶顥滈柣锝嗙箞瀹曠喖顢曢妶蹇曞簥闂傚倷鑳剁划顖炲礉濞嗗繄缂氱憸蹇曞垝婵犲洦鏅濋柛灞剧〒閸樼敻姊虹粙璺ㄧ闁稿鍔欏鍐差潨閳ь剟寮诲☉銏犳閻犳亽鍔庨崝顖炴⒑鐠団€虫灓闁稿鍊曢悾鐤亹閹烘繃鏅濋梺缁樓规禍顒勬儎鎼淬劍鈷掑ù锝呮啞鐠愶繝鏌涙惔娑樷偓婵嗙暦瑜版帗鍋ㄩ柛鎾冲级閺呮粓姊洪崘鍙夋儓闁瑰啿绻樺畷鎰板垂椤愶絽寮垮┑顔筋殔濡鏅剁紒妯圭箚闁圭粯甯炵粔娲煛鐏炵偓绀嬬€规洘鍎奸ˇ鍙夈亜韫囷絽骞橀柍褜鍓氶鏍窗閺囩姴鍨濇繛鍡樺姃缁诲棙鎱ㄥ┑鍡欑劸婵℃彃銈稿娲閳哄啰銈烽梺绋块绾绢厼危閹版澘绠婚悗娑櫭鎾剁磽娴e湱鈽夋い鎴濇噹閳绘捇顢橀悙鈺傛杸闂佸疇妫勫Λ妤佺濠靛牏纾奸悹鍥皺婢ф洘銇勯弴顏嗙ɑ缂佺粯绻傞~婵嬵敇閻愭壆鐩庣紓鍌欒兌閸嬫挻鍒婇懞銉d粓闁归棿绀侀悿楣冩煥濠靛棭妲归柛瀣у墲缁绘繃绻濋崒姘闁轰礁鐗撳铏规嫚閳ヨ櫕鐏嶉梺鎸庢磸閸ㄤ粙鐛繝鍥ㄥ亹婵炶尙绮弲銏ゆ⒑缁嬫寧婀扮紒顕嗙悼濡叉劙寮婚妷锔规嫼闂佸憡绻傜€氬嘲危閹间焦鐓熸俊銈傚亾閻庢碍婢橀悾宄扳攽閸ャ劌鍔呴梺鎸庣箓濞诧絿绮径鎰拺缂備焦锚婵鏌涙惔娑樷偓婵嬪箖閳ユ枼妲堥柕蹇娾偓鏂ュ亾閸洘鐓熼柟閭﹀灡绾墽鎮鑸碘拺闁告縿鍎辨牎闂佹寧娲忛崹钘夘嚕婵犳艾鐒洪柛鎰ㄦ櫅椤庢捇姊洪懡銈呮瀾婵犮垺锕㈤敐鐐哄箳濡や礁鈧敻鎮峰▎蹇擃仾缂佲偓閸儲鐓曢柣妯虹-婢х敻鏌曢崱鏇狀槮妞ゎ偅绮撻崺鈧い鎺戝缁犳牠鏌嶉崫鍕櫤闁诡垳鍋為妵鍕箛閳轰讲鍋撳┑瀣濠电姵纰嶉埛鎺楁煕鐏炴崘澹橀柍褜鍓欓崲鏌ユ箒闂佹悶鍎滈崨顔筋啎闂備礁澹婇悡鍫ュ磻閸涙潙鐭楅煫鍥ㄧ⊕閻撴瑩鏌熼娑欑凡鐞氭岸姊洪幎鑺ユ暠婵﹤顭烽崺鈧い鎺嗗亾缂佺姴绉瑰畷鏇㈡焼瀹撱儱娲、娑㈡倷閹绘帞鈧參姊虹粙璺ㄧ伇闁稿鍋ら幃锟犲閳ヨ尙绠氬銈嗙墬缁矂鎯冮幋鐘电<閺夊牄鍔岄崫娲煛鐏炶濡奸柍瑙勫灴瀹曢亶鍩¢崒鍌﹀缁辨挻鎷呴幓鎺嶅闂佽鍑界紞鍡涘磻娴e湱顩叉繝濠傜墛閻撴稓鈧箍鍎遍崯顐d繆閸ф鐓冮柦妯侯樈濡插湱绱掔紒妯兼创鐎规洖銈搁幃銏ゆ惞閸︻厼甯ㄥ┑鐘愁問閸犳牠鏁冮妸銉㈡瀺闁挎繂鎳夊Σ鍫ユ煟閵忊懚鐟邦啅濠靛洢浜滈柡鍌涘劤鐎氬酣鏌嶈閸撴岸宕濋弴鈶┾偓鏃堝礃椤斿槈褔骞栫划鍏夊亾閼碱剙鍤┑鐘垫暩閸嬬姷浜稿▎鎾崇獥闁哄诞鍛濠电偛妯婃禍婊堟倿閸偁浜滈柟鍝勭Ч濡惧嘲霉濠婂嫮鐭掗柡宀€鍠栧畷顐﹀礋椤掑顥e┑鐐茬摠缁挾绮婚弽褜娼栨繛宸簻缁犱即骞栧ǎ顒€鐏柍瑙勭⊕缁绘繄鍠婂Ο宄颁壕闁惧浚鍋呴幃娆撴煕濡ゅ懍鎲鹃柡宀€鍠栭幃褔宕奸悢鍝勫殥缂傚倷绶¢崑鍕矓瑜版帒钃熸繛鎴欏焺閺佸啴鏌ㄥ┑鍡樺窛闁伙絿鍘ч—鍐Χ閸℃ê闉嶅┑锛勫仩濡嫰锝炶箛娑欐優闁革富鍘鹃敍婊冣攽閳藉棗鐏犻柟纰卞亰閿濈偛鐣濋崟顒€鈧灚绻涢崼婵堜虎婵炲懏锕㈤弻娑㈠箻鐎靛憡鍣梺璇茬箰閸熸潙顫忓ú顏勫窛濠电姳鑳剁换渚€姊洪崫銉バg€光偓缁嬭法鏆﹂柕蹇嬪€曠粻鐟懊归敐鍛辅闁归攱妞藉娲川婵犲啫闉嶉悷婊勬緲閸燁垳绮嬪鍡欘浄閻庯綆鍋嗛崢閬嶆煟鎼搭垳绉靛ù婊勭矒椤㈡棃顢橀姀锛勫幍濡炪倖姊归弸濠氭嚀閸ф鐓涘ù锝嚽归弳锝団偓瑙勬礃鐢帡鍩ユ径濠庢僵妞ゆ巻鍋撶紒鐘靛仱濮婄粯鎷呯粵瀣闂佸憡鍨归弲顐ゆ閻愬搫骞㈡繛鎴炨缚閿涙盯姊虹化鏇炲⒉閽冭鲸绻涢崨顖毿i柕鍥у楠炴帡骞嬪┑鍐ㄤ壕闁煎鍊曢ˉ姘攽閸屾碍鍟為柣鎾存礋閹﹢鎮欓幓鎺嗘寖闂佸疇妫勯ˇ杈╂閹烘埈娓婚柨鏇楀亾婵炶绠撻幃鈥斥枎閹惧鍘介梺缁樻煥閹诧紕娆㈤崣澶堜簻闊浄绲藉顕€鏌″畝鈧崰鏍箖閳╁啯鍎熼柕蹇ョ悼椤㈠懘姊绘担鐑樺殌鐎殿喖鐖奸獮鎰板礃閼碱剚娈鹃梺缁樻煥閸氬藟閸喓绠鹃柟瀵稿仧閹冲嫰鏌e┑鎾剁瘈婵﹤顭峰畷鎺戭潩椤戣棄浜鹃柟闂寸绾剧懓顪冪€n亝鎹i柣顓炴閵嗘帒顫濋敐鍛婵°倗濮烽崑鐐烘偋閻樻眹鈧線寮撮姀鐘栄囨煕鐏炲墽鐓瑙勬礀閳规垿顢欓惌顐簻閻g兘顢楅崟顐㈠亶闁诲海鏁哥涵鍫曞磻閹捐埖鍠嗛柛鏇ㄥ墰椤︺儳绱撻崒姘毙㈤柨鏇ㄤ簻椤曪絾绻濆顒€鑰垮┑掳鍊曢敃銈夊箖閹达附鈷戦柛娑橈梗缁堕亶鏌涢妸褎娅曟俊鍙夊姍閺屽棗顓奸崱娆忓箺闂佺澹堥幓顏嗘閺囥垺鍋╂い鎾卞灪閻撴洟骞栧ǎ顒€濡洪柟鏌ョ畺閹稿﹤鈹戦崰銏犵秺瀹曟宕楅懖鈺冣枏缂備胶鍋撳畷妯衡枖閺囥垹鐒垫い鎺嗗亾缂佺姴绉瑰畷鏇㈡焼瀹撱儱娲︾€佃偐鈧稒锚娴滄姊洪崫鍕偍闁搞劍妞介幃鈥斥槈閵忥紕鍘遍梺闈涱檧缁蹭粙宕濆顑芥斀闁挎稑瀚敮鑸点亜椤撯€冲姷妞ぱ傜窔閺屾盯濡搁妶鍛ギ濠电姭鍋撳〒姘e亾婵﹨娅g槐鎺懳熼幘铏础缂侇喗妫冮、姘跺焵椤掑嫨鈧線寮崼婵嬪敹闂佺粯妫佸▍锝夊汲閵忋倖鈷掗柛灞捐壘閳ь剛鍏橀幃鐐烘晜闁款垰浜剧紒妤佺☉閹冲繐鐣烽弻銉︾厱閻忕偟铏庨崵銈嗙箾閹寸儐鐒搁柡鍐ㄧ墕瀹告繃銇勯幘璺盒㈡鐐村浮濮婄粯鎷呴崨濠冨創濠电偛鐪伴崹钘夌暦閻熸噴娲敂閸涱厺绨甸梻浣告啞閸旓附绂嶅┑瀣獥闁归偊鍘剧弧鈧繝鐢靛Т閸婄粯鏅堕弴鐘电<闁逞屽墴瀹曞ジ鎮㈢粙鍨紟婵犵妲呴崹杈┾偓绗涘懏鍏滃Δ锝呭暞閻撱垽鏌涢幇鍏哥盎闁哄鐩弻锛勪沪閻愵剛顦ㄧ紓浣虹帛缁嬫牠藝閺屻儲鍊垫慨妯哄船閸樺鈧娲橀崹鎸庝繆閹间礁鐓涢柛灞绢殕鐎氳偐绱撻崒姘偓鐑芥倿閿曞倹鏅┑鐘愁問閸犳牕煤閿曞倸桅闁告洦鍨伴崡鎶芥煏婵炲灝鍔氱紒顐㈢Ч濮婅櫣鍖栭弴鐔告緭闂佹悶鍔忓Λ鍕敋閿濆绠绘い鏃傗拡濞煎﹪姊洪悙钘夊姕闁哄銈稿畷鎴﹀箻缂佹ê浠洪梺鍛婄☉閿曪箓宕i崱妞绘斀闁绘ḿ绮☉褎淇婇锝庢疁闁糕斁鍋撳銈嗗笒閸婂綊宕甸埀顒勬⒑鐎圭媭鍤欑紒澶屾嚀閻g兘宕奸弴鐐嶁晠鏌ㄥ┑鍡楊伀闁烩晛閰e缁樼節鎼粹€茬盎濠电偠顕滄俊鍥╁垝濞嗘挸绠涢柡澶庢硶閻ゅ懏淇婇妶蹇曞埌闁哥噥鍨堕幃鈥斥槈閵忊€斥偓鍫曟煟閹扮増娑уù婊冨⒔缁辨帡鍩€椤掑倵鍋撻敐搴″幋闁稿鎸鹃幉鎾礋椤掑偆妲瑰┑鐐茬摠缁矂鎮ユ總绋课ュù锝呭濞笺劑鏌嶈閸撶喖鐛崘顔碱潊闁挎稑瀚板顔界節閵忥絾纭炬い锕侀哺瀵板嫰鏁撻敓锟�

    重磅专题
    往期文章
    最新文章