Customer-Support-v1

2018-08-05  本文已影响7人  shenyoujian

1、项目需求

为该公司网站中添加一个交互性的客户支持应用程序。它需要能够让用户提出问题或支持票据,并且员工也可以对这些查询做出响应。支持票据和评论都应该支持文件附件。对于紧急的事件,客户能够进入一个有特定客户支持代表参与的聊天窗口。并且最重要的是,作为跨国公司的网站,整个应用程序能够实现本地化,要求支持公司所需要的所有语言,并且该应用程序还必须非常安全。这些要求并不过分,对吧,明天上线ok。哈哈哈哈

2、创建项目

2.1、启动idea创建maven项目
image.png
image.png

选择maven和配置文件


image.png
工程名和项目路径
image.png

之后点击完成。

为了在pom.xml文件中添加依赖之后自动引入jar,点击右下角红圈的Enable Auto-Import选项


image.png

配置全局tomcat


image.png
然后就能在Run/Debug Configurations里设置tomcat了
(注意:如果指定了项目的url路径那么application context也要指定路径,见下图红圈)
image.png
image.png

添加完成tomcat后点击运行,运行成功后如下图所示,项目部署成功
(注意:请先在命令行停止已经运行的系统的tomcat服务,可使用命令systemctl stop tomcat8,否则无法启动idea的tomcat服务)


image.png

3、Customer-Support-v1

3.1、v1的功能,由三个页面组成,通过doGet处理,一个票据列表,一个创建票据的页面和一个查看单个票据的页面。还支持下载某个ticket票据文件的附件,以及接受POST请求用于创建新的票据。
3.2、pom.xml加入servlet依赖,scope详解
image.png
3.3、POJO类

首先右键main选择Mark Directory as选择Sources Root,这样才能创建类,创建pojo包并且创建Ticket类和Attachment类,如下

/**
 * @Author ljs
 * @Description TODO
 * @Date 2018/8/3 23:05
 **/
public class Attachment {

    private String name;
    
    private byte[] contents;

    //省略get和set
}

首先,一个票据可以有多个附件,然后这些附件有名字,所以我们创建一个LinkedMap键值对来存储而不是一个List,这样就可以通过名字(键)来获取某个附件。这里先不写dao层,所有增查功能对应的方法写在model类里。主要有四个接口:

/**
 * @Author ljs
 * @Description TODO
 * @Date 2018/8/3 22:56
 **/
public class Ticket {
    private String customerName;        //票据名

    private String subject;             //票类型

    private String body;

    private Map<String, Attachment> attachments = new LinkedHashMap<>();    //和附件是一对多

    public Attachment getAttachment(String name)
    {
        return this.attachments.get(name);
    }

    public Collection<Attachment> getAttachments()
    {
        return this.attachments.values();
    }

    public void addAttachment(Attachment attachment)
    {
        this.attachments.put(attachment.getName(), attachment);
    }

    public int getNumberOfAttachments()
    {
        return this.attachments.size();
    }
    //省略get和set
}
3.4、视图层与控制层分离

没分离之前向响应中输出动态的html代码全都写在servlet里的,非常不方便,所以使用jsp让业务逻辑与视图分离。首先加入依赖,因为jstl实现定义了相对旧版jsp和servlet规范的依赖,它们与当前版本的jsp和servlet规范的maven artifact id不同,所以使用exclusions将它们排除。也就是说加入了jstl依赖,但是这个依赖又依赖于旧版的jsp和servlet,根据maven的依赖传递,会把旧版的jsp和servlet依赖加入这个项目中,跟上面的servlet和jsp依赖冲突,所以使用exclusions排除依赖。

       <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>javax.servlet.jsp.jstl-api</artifactId>
            <version>1.2.1</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.servlet.jsp.jstl</artifactId>
            <version>1.2.2</version>
            <scope>compile</scope>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.servlet.jsp</groupId>
                    <artifactId>jsp-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.servlet.jsp.jstl</groupId>
                    <artifactId>jstl-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
3.5、jsp

如果每个jsp都有相似的属性,那么在每个jsp文件的顶部重复添加page指令是非常麻烦的工作。我们可以在web.xml里设置通用的jsp属性。标签<jsp-config>是在<web-app>标签下的,该标签可以包含任意数目的<jsp-property-group>标签。

下面这个属性组表示匹配项目中所有.jsp和.jspf的文件,把匹配到的所有jsp文件编码都设置为utf8类型为text/html,并且包含/WEB-INF/jsp/base.jspf这个jsp片段,<trim-directive-whitespaces>这个命令可以使jsp输出的html时去除多余的空行(jsp上使用EL和tag会产生大量的空格和空行)。

<jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <url-pattern>*.jspf</url-pattern>
            <page-encoding>utf-8</page-encoding>
            <include-prelude>/WEB-INF/jsp/base.jsp</include-prelude>
            <trim-directive-whitespaces>true</trim-directive-whitespaces>
            <default-content-type>text/html</default-content-type>
        </jsp-property-group>
    </jsp-config>

注意:intellij idea默认创建的web.xml版本为2.3版本,对应的jstl是1.1版本,最好替换为3.1版本的web.xml,对应的jstl是1.2版本是1.2版本,所以使用idea默认的web.xml是识别不了<jsp-config>标签的。修改如下重启:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

base.jsp,1、导入类2、声明jstl核心代码库。

<%@ page import="pojo.Ticket, pojo.Attachment" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

listTickets.jsp
1、禁止该jsp使用会话
2、该页面需要遍历显示ticket,所以需要ticketDatabase,这个值在servlet的listTickets方法中设置,存储到请求中,当转发到该页面是,通过request.getAttribute()取出,因为getAttribute()返回Object对象,所以需要强制转换,将对象强制转换是一个未检查操作,所以需要抑制警告。

<%@ page session="false" import="java.util.Map" %>
<%
    @SuppressWarnings("unchecked")
    Map<Integer, Ticket> ticketDatabase =
            (Map<Integer, Ticket>)request.getAttribute("ticketDatabase");
%>
<!DOCTYPE html>
<html>
<head>
    <title>Customer Support</title>
</head>
<body>
<h2>Tickets</h2>
<a href="<c:url value="/tickets">
            <c:param name="action" value="create" />
        </c:url>">Create Ticket</a><br /><br />
<%
    if(ticketDatabase.size() == 0)
    {
%><i>There are no tickets in the system.</i><%
}
else
{
    for(int id : ticketDatabase.keySet())
    {
        String idString = Integer.toString(id);
        Ticket ticket = ticketDatabase.get(id);
%>Ticket #<%= idString %>: <a href="<c:url value="/tickets">
                        <c:param name="action" value="view" />
                        <c:param name="ticketId" value="<%= idString %>" />
                    </c:url>"><%= ticket.getSubject() %></a> (customer:
<%= ticket.getCustomerName() %>)<br /><%
        }
    }
%>
</body>
</html>

viewTicket.jsp

<%@ page session="false" %>
<%
    String ticketId = (String)request.getAttribute("ticketId");
    Ticket ticket = (Ticket)request.getAttribute("ticket");
%>
<!DOCTYPE html>
<html>
<head>
    <title>Customer Support</title>
</head>
<body>
<h2>Ticket #<%= ticketId %>: <%= ticket.getSubject() %></h2>
<i>Customer Name - <%= ticket.getCustomerName() %></i><br /><br />
<%= ticket.getBody() %><br /><br />
<%
    if(ticket.getNumberOfAttachments() > 0)
    {
%>Attachments: <%
    int i = 0;
    for(Attachment a : ticket.getAttachments())
    {
        if(i++ > 0)
            out.print(", ");
%><a href="<c:url value="/tickets">
                        <c:param name="action" value="download" />
                        <c:param name="ticketId" value="<%= ticketId %>" />
                        <c:param name="attachment" value="<%= a.getName() %>" />
                    </c:url>"><%= a.getName() %></a><%
    }
%><br /><br /><%
    }
%>
<a href="<c:url value="/tickets" />">Return to list tickets</a>
</body>
</html>

ticketForm.jsp

<%@ page session="false" %>
<!DOCTYPE html>
<html>
<head>
    <title>Customer Support</title>
</head>
<body>
<h2>Create a Ticket</h2>
<form method="POST" action="tickets" enctype="multipart/form-data">
    <input type="hidden" name="action" value="create"/>
    Your Name<br/>
    <input type="text" name="customerName"><br/><br/>
    Subject<br/>
    <input type="text" name="subject"><br/><br/>
    Body<br/>
    <textarea name="body" rows="5" cols="30"></textarea><br/><br/>
    <b>Attachments</b><br/>
    <input type="file" name="file1"/><br/><br/>
    <input type="submit" value="Submit"/>
</form>
</body>
</html>

3.6、Servlet
@WebServlet(
        name = "ticketServlet",
        urlPatterns = {"/tickets"},
        loadOnStartup = 1
)
@MultipartConfig(
        fileSizeThreshold = 5_242_880, //5MB
        maxFileSize = 20_971_520L, //20MB
        maxRequestSize = 41_943_040L //40MB
)
public class TicketServlet extends HttpServlet
{
    private volatile int TICKET_ID_SEQUENCE = 1;

    private Map<Integer, Ticket> ticketDatabase = new LinkedHashMap<>();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        String action = request.getParameter("action");
        if(action == null)
            action = "list";
        switch(action)
        {
            case "create":
                this.showTicketForm(request, response);
                break;
            case "view":
                this.viewTicket(request, response);
                break;
            case "download":
                this.downloadAttachment(request, response);
                break;
            case "list":
            default:
                this.listTickets(request, response);
                break;
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        String action = request.getParameter("action");
        if(action == null)
            action = "list";
        switch(action)
        {
            case "create":
                this.createTicket(request, response);
                break;
            case "list":
            default:
                response.sendRedirect("tickets");
                break;
        }
    }

    private void showTicketForm(HttpServletRequest request,
                                HttpServletResponse response)
            throws ServletException, IOException
    {
        request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp")
               .forward(request, response);
    }

    private void viewTicket(HttpServletRequest request,
                            HttpServletResponse response)
            throws ServletException, IOException
    {
        String idString = request.getParameter("ticketId");
        Ticket ticket = this.getTicket(idString, response);
        if(ticket == null)
            return;

        request.setAttribute("ticketId", idString);
        request.setAttribute("ticket", ticket);

        request.getRequestDispatcher("/WEB-INF/jsp/view/viewTicket.jsp")
               .forward(request, response);
    }

    private void downloadAttachment(HttpServletRequest request,
                                    HttpServletResponse response)
            throws ServletException, IOException
    {
        String idString = request.getParameter("ticketId");
        Ticket ticket = this.getTicket(idString, response);
        if(ticket == null)
            return;

        String name = request.getParameter("attachment");
        if(name == null)
        {
            response.sendRedirect("tickets?action=view&ticketId=" + idString);
            return;
        }

        Attachment attachment = ticket.getAttachment(name);
        if(attachment == null)
        {
            response.sendRedirect("tickets?action=view&ticketId=" + idString);
            return;
        }

        response.setHeader("Content-Disposition",
                "attachment; filename=" + attachment.getName());
        response.setContentType("application/octet-stream");

        ServletOutputStream stream = response.getOutputStream();
        stream.write(attachment.getContents());
    }

    private void listTickets(HttpServletRequest request,
                             HttpServletResponse response)
            throws ServletException, IOException
    {
        request.setAttribute("ticketDatabase", this.ticketDatabase);

        request.getRequestDispatcher("/WEB-INF/jsp/view/listTickets.jsp")
                .forward(request, response);
    }

    private void createTicket(HttpServletRequest request,
                              HttpServletResponse response)
            throws ServletException, IOException
    {
        Ticket ticket = new Ticket();
        ticket.setCustomerName(request.getParameter("customerName"));
        ticket.setSubject(request.getParameter("subject"));
        ticket.setBody(request.getParameter("body"));

        Part filePart = request.getPart("file1");
        if(filePart != null && filePart.getSize() > 0)
        {
            Attachment attachment = this.processAttachment(filePart);
            if(attachment != null)
                ticket.addAttachment(attachment);
        }

        int id;
        synchronized(this)
        {
            id = this.TICKET_ID_SEQUENCE++;
            this.ticketDatabase.put(id, ticket);
        }

        response.sendRedirect("tickets?action=view&ticketId=" + id);
    }

    private Attachment processAttachment(Part filePart)
            throws IOException
    {
        InputStream inputStream = filePart.getInputStream();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        int read;
        final byte[] bytes = new byte[1024];

        while((read = inputStream.read(bytes)) != -1)
        {
            outputStream.write(bytes, 0, read);
        }

        Attachment attachment = new Attachment();
        attachment.setName(filePart.getSubmittedFileName());
        attachment.setContents(outputStream.toByteArray());

        return attachment;
    }

    private Ticket getTicket(String idString, HttpServletResponse response)
            throws ServletException, IOException
    {
        if(idString == null || idString.length() == 0)
        {
            response.sendRedirect("tickets");
            return null;
        }

        try
        {
            Ticket ticket = this.ticketDatabase.get(Integer.parseInt(idString));
            if(ticket == null)
            {
                response.sendRedirect("tickets");
                return null;
            }
            return ticket;
        }
        catch(Exception e)
        {
            response.sendRedirect("tickets");
            return null;
        }
    }
}

3.7、上传到git
上一篇下一篇

猜你喜欢

热点阅读