接下來,就是來開發咱們的投訴受理管理模塊了…..咱們來看看原型圖與需求吧:javascript
查詢用戶提交的投訴信息,能夠根據投訴部門(部門A/B)、投訴時間段、狀態進行查詢。在列表信息中展現投訴標題、被投訴部門、被投訴人、投訴時間、狀態(待受理、已受理、已失效)、操做;其中操做欄內內容爲「處理」,點擊「處理」則在打開的查詢頁面中查看具體的投訴信息而且能夠屢次回覆投訴信息;一旦回覆則說明已受理該投訴。css
投訴詳細信息:在本頁面中首先要明顯地展現出當前投訴是否已經受理;而後再顯示投訴人信息、被投訴信息、受理信息(歷史受理信息)三部份內容,而且在頁面中能夠無限次的對本次受理進行回覆。投訴人信息包括:是否匿名投訴、投訴人單位、投訴人姓名、投訴人手機,若是是匿名投訴,則不顯示投訴人單位、姓名並對手機號中間4位號碼使用*號代替。被投訴信息包括:投訴時間、被投訴部門、被投訴人、投訴標題、投訴內容。受理信息:若是有屢次回覆則將屢次的回覆信息顯示,顯示內容包括回覆時間、回覆部門、回覆人、受理回覆內容;能夠再次回覆。java
根據上面兩張原型圖以及文字說明,咱們能夠發現:一個投訴信息可對應多個回覆。ajax
在「工做主頁」中點擊「我要投訴」進入頁面,添加內容包括:投訴標題、被投訴部門(部門A/B)、被投訴人、投訴詳情、是否匿名投訴。sql
關鍵在於匿名投訴的那一部分,咱們該怎麼寫….數據庫
統計:根據年度將相應年度的每月的投訴數進行統計,並以圖表的形式展現在頁面中;在頁面中能夠選擇查看當前年度及其前4年的投訴數。在頁面中能夠選擇不一樣的年度,而後頁面展現該年度的曲線統計圖。apache
這個統計圖,大概也須要用到組件來生成出來的吧???json
自動投訴受理:在每月月底最後一天對本月以前的投訴進行自動處理;將投訴信息的狀態改成 已失效。在後臺管理中不能對該類型投訴進行回覆。瀏覽器
自動投訴受理??在每月的最後一天判斷投訴信息,程序對其自動受理。。服務器
咱們首先來畫一個流程圖看看它的大概思路是怎麼樣的:
咱們通過上面的分析,知道了:一個投訴信息可對應多個回覆。是一對多的關係。咱們下面使用powerdesginer來畫出它的概念數據模型圖
生成物理模型圖:
生成數據庫表:
/*==============================================================*/ /* DBMS name: MySQL 5.0 */ /* Created on: 2017/6/12 19:06:20 */ /*==============================================================*/ drop table if exists complain; drop table if exists complain_reply; /*==============================================================*/ /* Table: complain */ /*==============================================================*/ create table complain ( comp_id varchar(32) not null, comp_company varchar(100), comp_name varchar(20), comp_mobile varchar(20), is_NM bool, comp_time datetime, comp_title varchar(200) not null, to_comp_name varchar(20), to_comp_dept varchar(100), comp_content text, state varchar(1), primary key (comp_id) ); /*==============================================================*/ /* Table: complain_reply */ /*==============================================================*/ create table complain_reply ( reply_id varchar(32) not null, comp_id varchar(32) not null, replyer varchar(20), reply_dept varchar(100), reply_time datetime, reply_content varchar(300), primary key (reply_id) ); alter table complain_reply add constraint FK_comp_reply foreign key (comp_id) references complain (comp_id) on delete restrict on update restrict;
生成實體與配置文件:
Intellij idea下生成出來的映射文件是沒有對應的關聯關係的。也就是說:一對多或多對多的關係,它是不會幫你自動生成的【好像是這樣子的】。。。所以,須要咱們本身添加Set【若是須要】
package zhongfucheng.complain.entity; import java.sql.Timestamp; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Created by ozc on 2017/6/12. */ public class Complain { private String compId; private String compCompany; private String compName; private String compMobile; private Byte isNm; private Timestamp compTime; private String compTitle; private String toCompName; private String toCompDept; private String compContent; private String state; //狀態 public static String COMPLAIN_STATE_UNDONE = "0"; public static String COMPLAIN_STATE_DONE = "1"; public static String COMPLAIN_STATE_INVALID = "2"; public static Map<String, String> COMPLAIN_STATE_MAP; static { COMPLAIN_STATE_MAP = new HashMap<String, String>(); COMPLAIN_STATE_MAP.put(COMPLAIN_STATE_UNDONE, "待受理"); COMPLAIN_STATE_MAP.put(COMPLAIN_STATE_DONE, "已受理"); COMPLAIN_STATE_MAP.put(COMPLAIN_STATE_INVALID, "已失效"); } //添加set集合 private Set complainReplies = new HashSet(0); public Set getComplainReplies() { return complainReplies; } public void setComplainReplies(Set complainReplies) { this.complainReplies = complainReplies; } public String getCompId() { return compId; } public void setCompId(String compId) { this.compId = compId; } public String getCompCompany() { return compCompany; } public void setCompCompany(String compCompany) { this.compCompany = compCompany; } public String getCompName() { return compName; } public void setCompName(String compName) { this.compName = compName; } public String getCompMobile() { return compMobile; } public void setCompMobile(String compMobile) { this.compMobile = compMobile; } public Byte getIsNm() { return isNm; } public void setIsNm(Byte isNm) { this.isNm = isNm; } public Timestamp getCompTime() { return compTime; } public void setCompTime(Timestamp compTime) { this.compTime = compTime; } public String getCompTitle() { return compTitle; } public void setCompTitle(String compTitle) { this.compTitle = compTitle; } public String getToCompName() { return toCompName; } public void setToCompName(String toCompName) { this.toCompName = toCompName; } public String getToCompDept() { return toCompDept; } public void setToCompDept(String toCompDept) { this.toCompDept = toCompDept; } public String getCompContent() { return compContent; } public void setCompContent(String compContent) { this.compContent = compContent; } public String getState() { return state; } public void setState(String state) { this.state = state; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Complain complain = (Complain) o; if (compId != null ? !compId.equals(complain.compId) : complain.compId != null) return false; if (compCompany != null ? !compCompany.equals(complain.compCompany) : complain.compCompany != null) return false; if (compName != null ? !compName.equals(complain.compName) : complain.compName != null) return false; if (compMobile != null ? !compMobile.equals(complain.compMobile) : complain.compMobile != null) return false; if (isNm != null ? !isNm.equals(complain.isNm) : complain.isNm != null) return false; if (compTime != null ? !compTime.equals(complain.compTime) : complain.compTime != null) return false; if (compTitle != null ? !compTitle.equals(complain.compTitle) : complain.compTitle != null) return false; if (toCompName != null ? !toCompName.equals(complain.toCompName) : complain.toCompName != null) return false; if (toCompDept != null ? !toCompDept.equals(complain.toCompDept) : complain.toCompDept != null) return false; if (compContent != null ? !compContent.equals(complain.compContent) : complain.compContent != null) return false; if (state != null ? !state.equals(complain.state) : complain.state != null) return false; return true; } @Override public int hashCode() { int result = compId != null ? compId.hashCode() : 0; result = 31 * result + (compCompany != null ? compCompany.hashCode() : 0); result = 31 * result + (compName != null ? compName.hashCode() : 0); result = 31 * result + (compMobile != null ? compMobile.hashCode() : 0); result = 31 * result + (isNm != null ? isNm.hashCode() : 0); result = 31 * result + (compTime != null ? compTime.hashCode() : 0); result = 31 * result + (compTitle != null ? compTitle.hashCode() : 0); result = 31 * result + (toCompName != null ? toCompName.hashCode() : 0); result = 31 * result + (toCompDept != null ? toCompDept.hashCode() : 0); result = 31 * result + (compContent != null ? compContent.hashCode() : 0); result = 31 * result + (state != null ? state.hashCode() : 0); return result; } }
<set name="complainReplies" inverse="true" cascade="save-update,delete" lazy="false" > <key> <column name="comp_id" length="32" not-null="true" /> </key> <one-to-many class="zhongfucheng.complain.entity.ComplainReply" /> </set>
編寫dao、service、action都很是簡單。記得要把模塊的配置文件加載到總配置文件中!
ComplainAction代碼以下:
public class ComplainAction extends BaseAction { /*************注入Service************************/ @Autowired private ComplainService complainServiceImpl; /************數據自動封裝,給出setter和getter*************************/ private Complain complain; public Complain getComplain() { return complain; } public void setComplain(Complain complain) { this.complain = complain; } /************Action中7大方法*************************/ //拋出Action異常 public String listUI() throws ServiceException, UnsupportedEncodingException { //把狀態的集合帶過去 QueryHelper queryHelper = new QueryHelper(Complain.class, "c"); //當前頁數沒有值,那麼賦值爲1 if (currentPageCount == 0) { currentPageCount = 1; } //把狀態帶過去給JSP頁面 ActionContext.getContext().getContextMap().put("complainStateMap", Complain.COMPLAIN_STATE_MAP); pageResult = complainServiceImpl.getPageResult(queryHelper,currentPageCount); return "listUI"; } }
導入對應的JSP頁面…..獲得的效果以下:
咱們來看一下條件查詢有幾個:能夠根據投訴的標題、投訴的時間、投訴的狀態進行查詢。
對於投訴標題和投訴的狀態咱們均可以很容易地拿到條件:
//根據complain標題是否爲null來判斷是不是條件查詢。若是complain爲空,那麼是查詢全部。 if (complain != null) { if (org.apache.commons.lang.StringUtils.isNotBlank(complain.getCompTitle())) { selectCondition = URLDecoder.decode(complain.getCompTitle(),"UTF-8"); complain.setCompTitle(selectCondition); queryHelper.addCondition(" c.compTitle like ? ", "%" + complain.getCompTitle() + "%"); } if (org.apache.commons.lang.StringUtils.isNotBlank(complain.getState())) { queryHelper.addCondition(" c.state like ? ", "%" + complain.getState() + "%"); } }
那麼根據投訴時間來進行查詢,咱們要怎麼作呢???咱們約定時間是這樣的格式:yyyy-MM-dd HH:mm。這樣的格式Struts2默認是不支持解析的,那麼咱們怎麼獲取呢???
有的同窗可能會想到類型轉換器,咱們在Struts2的時候的確是學過類型轉換器。。。可是呢,這種方法並非最好的。咱們能夠使用DateUtils工具類來獲得日期數據!
//先判斷時間,經過時間進行篩選後,再進行like模糊查詢。那麼性能會好一些 if (StringUtils.isNotBlank(startTime)) { startTime = URLDecoder.decode(startTime,"UTF-8"); queryHelper.addCondition(" c.compTime >= ? ", DateUtils.parseDate(startTime, new String[]{"yyyy-MM-dd HH:mm"})); } if (StringUtils.isNotBlank(endTime)) { endTime = URLDecoder.decode(endTime,"UTF-8"); queryHelper.addCondition(" c.compTime <= ? ", DateUtils.parseDate(endTime, new String[]{"yyyy-MM-dd HH:mm"})); }
咱們在JSP頁面上也使用datepicker組件來讓用戶選擇日期:
投訴時間:<s:textfield id="startTime" name="startTime" cssClass="s_text" cssStyle="width:160px;" readonly="true" onfocus="WdatePicker({'skin':'whyGreen','dateFmt':'yyyy-MM-dd HH:mm'});"/> - <s:textfield id="endTime" name="endTime" cssClass="s_text" cssStyle="width:160px;" readonly="true" onfocus="WdatePicker({'skin':'whyGreen','dateFmt':'yyyy-MM-dd HH:mm'});"/>
提供處理受理的UI界面。根據id查找投訴的所有信息。
//提供受理的UI public String dealUI() { //把狀態傳遞過去 ActionContext.getContext().getContextMap().put("complainStateMap", Complain.COMPLAIN_STATE_MAP); //獲得想要受理的記錄 if (complain != null) { complain = complainServiceImpl.findObjectById(complain.getCompId()); } return "dealUI"; }
在處理受理的JSP頁面上要把投訴的id給發送給Action處理。否則在保存信息的時候,就會把投訴信息丟失了!。在Action中經過id從新查找回投訴的信息!
//受理 public String deal() { //修改投訴信息的處理狀態 if (complain != null) { //查找到信息 complain = complainServiceImpl.findObjectById(complain.getCompId()); //若是狀態是已處理了,那麼就不用再修改了 if (!complain.getState().equals(Complain.COMPLAIN_STATE_DONE)) { complain.setState(Complain.COMPLAIN_STATE_DONE); } } //保存回覆的信息 if (reply != null) { //更新回覆的日期 reply.setReplyTime(new Timestamp(new Date().getTime())); //把回覆信息添加到投訴信息中【關聯關係】 reply.setComplain(complain); complain.getComplainReplies().add(reply); } //級聯更新 complainServiceImpl.update(complain); return "list"; }
咱們在處理投訴的時候,應該把回覆的歷史信息給處理的人看…..
把回覆的信息遍歷出來。
<s:iterator value="complain.complainReplies" status="st"> <tr> <%--獲得全部回覆的信息--%> <td colspan="2"> <fieldset style="border: solid 1px #c0c0c0;margin-top:5px;"><legend style="color:green;font-weight:bold;"> 回覆<s:property value="#st.count"/> </legend> <div style="width:100%; text-align:center;color:#ccc;maring-top:5px;"> 回覆部門:<s:property value="replyDept"/> 回覆人:<s:property value="replyer"/> 回覆時間:<s:date name="replyTime" format="yyyy-MM-dd HH:mm"/> </div> <div style="width:100%;maring-top:10px;font-size:13px;padding-left:5px;"><s:property value="replyContent"/></div> </fieldset> </td> </tr> </s:iterator>
如今有一個問題,就是咱們使用的是set集合,它全部的回覆信息並非按照順序來排列的。咱們能夠在hbm配置文件中指定咱們set集合的順序:
那麼在顯示的時候,咱們的回覆順序就不會被搞亂了。
在需求中,咱們已經看到了,若是投訴的人是匿名投訴的,那麼咱們不能顯示該投訴人的名字、部門。他的號碼應該設置成137****2342類型的。
其實咱們只要在顯示對應值的前面判斷該投訴人是不是匿名投訴就好了。
用戶能夠在首頁上經過「我要投訴」超連接對工做人員進行投訴..固然了,用戶點擊「我要投訴」超連接的時候,應該在新的頁面上給出對應的頁面,因此指定target爲「_blank」..
咱們在指定部門的時候,下拉菜單應該在後臺給出對應的的員工。這就須要咱們用到ajax進行二級菜單的二級聯動了。
咱們在返回JSON格式有兩種方式:第一種就是沒有使用Struts2框架的時候,
使用三個開發包commons-beanutils-1.8.0,ezmorph-1.0.6,json-lib-2.3-jdk15。使用JSONObject對象來構建JSON字符串,使用流對象返回給瀏覽器。
咱們若是使用了Struts2框架的話,直接導入:struts2-json-plugin-2.3.20這麼一個開發包,而且在配置文件中指定繼承json-defalut包,返回的類型是JSON的話。那麼Struts2框架就會自動幫咱們在該Action中所擁有getter方法的屬性就生成JSON格式返回給瀏覽器。。。固然了,咱們可能不想Struts2把所有帶有getter的屬性都生成JSON返回給瀏覽器,咱們只要在返回JSON類型上指定參數root,就能夠指定生成哪個屬性自動生成JSON字符串返回給瀏覽器了。
固然了,不管是沒有使用Struts2框架,仍是有使用Struts2框架,咱們都是有過Demo的。詳情請參考博文:http://blog.csdn.net/hon_3y/article/details/72468761和http://blog.csdn.net/hon_3y/article/details/72480126
另外,咱們手動訪問Acttion給出對應的參數,就能夠看到服務器返回的JSON是什麼了。最後咱們使用HiJson這樣的工具,就能夠把返回的JSON進行格式化。
那麼,咱們的代碼是這樣的:
使用ajax返回服務器。
function doSelectDept() { var $dept = $("#toCompDept option:selected").val(); $.ajax({ type: "post", url: "${basePath}sys/home_getUserJson.action", data: {"dept":$dept}, dataType: "json", success: function (data) { if("success" == data.msg){ var toCompName = $("#toCompName"); toCompName.empty(); $.each(data.userList, function(index, user){ toCompName.append("<option value='" + user.name + "'>" + user.name + "</option>"); }); } else {alert("獲取被投訴人列表失敗!");} }, error: function () { alert("失敗咯") } }); }
使用一個Map集合裝載這些數據,Struts2自動把Map集合的數據轉成是JSON格式的,返回給瀏覽器。
private Map<String, Object> return_map; public Map<String, Object> getReturn_map() { return return_map; } public String getUserJson() { //獲得帶過來的dept String dept = ServletActionContext.getRequest().getParameter("dept"); if (dept != null) { //根據部門查詢全部的員工 QueryHelper queryHelper = new QueryHelper(User.class, "u"); queryHelper.addCondition(" u.dept like ? ", "%" +dept); //二、根據部門查詢用戶列表 return_map = new HashMap(); return_map.put("msg", "success"); return_map.put("userList", userServiceImpl.findObjects(queryHelper)); } return "success"; }
咱們在投訴的內容上添加上富文本框,讓用戶能夠在文本域上傳上圖片….
加上一個富文本框是很是簡單的,只要導入對應的js文件,在textarea上寫上ueditor的id就能夠完成效果了。。。
<script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/ueditor.config.js"></script> <script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/ueditor.all.min.js"> </script> <script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/lang/zh-cn/zh-cn.js"></script> <script type="text/javascript"> //配置ueditor的根路徑 var UEDITOR_HOME_URL = "${basePath}js/ueditor/"; var ue = UE.getEditor('editor'); </script> <td><s:textarea id="editor" name="comp.compContent" cssStyle="width:90%;height:160px;"/></td>
再次觀察咱們的投訴頁面,表單裏面的值只有是被投訴人的信息,投訴人的信息是沒有的。因而咱們在表單中把投訴人的信息經過隱藏域將其添加進去….
<s:hidden name="comp.compCompany" value="%{#session.SYS_USER.dept}"></s:hidden> <s:hidden name="comp.compName" value="%{#session.SYS_USER.name}"></s:hidden> <s:hidden name="comp.compMobile" value="%{#session.SYS_USER.mobile}"></s:hidden>
那咱們保存「我要投訴」信息的流程應該是怎麼樣的呢???爲了達到更好的用戶體驗,咱們應該先把提示用戶數據已經保存起來了,而後刷新父窗口,接着把「我要投訴」本頁面給關閉了。這樣用戶看起來,就以爲他的操做已是成功了!
下面是整個「我要投訴」操做的時序圖:
代碼:
public void saveComplain() { try { if (comp != null) { //把投訴的缺乏的信息補全 comp.setState(Complain.COMPLAIN_STATE_UNDONE); comp.setCompTime(new Timestamp(new Date().getTime())); //調用service保存 complainServiceImpl.save(comp); //告訴瀏覽器保存信息成功了。 ServletActionContext.getResponse().getWriter().write("success"); } } catch (IOException e) { e.printStackTrace(); } }
提示用戶已經投訴成功,把父窗口刷新,本頁面關閉。
function saveComplain() { $.ajax({ url: "${basePath}sys/home_saveComplain.action", /*將整個表單的屬性轉成是JSON*/ data: $("form").serialize(), type: "post", success: function (backdata) { if(backdata == "success"){ //告訴用戶,保存成功了。 alert("投訴成功!!!"); //把父窗口刷新 window.opener.parent.location.reload(true); //把本頁面關閉 window.close(); } }, error:function () { alert("保存投訴信息失敗了!"); } }); }
在信息管理模塊的時候,咱們就提出了三圈的問題了。何爲三圈問題呢???就是當咱們使用條件查詢出數據的時候,再對查詢出的數據進行操做【修改、保存】,當保存完以後回到列表顯示頁面上的時候,查詢條件就會丟失掉了。也就是說,咱們原來查詢出的數據不見了。
首先,咱們在Action中使用兩個變量把有可能成爲查詢條件的變量記住:
/************三圈問題數據回顯*************************/ private String compTitle; private String state; public String getCompTitle() { return compTitle; } public void setCompTitle(String compTitle) { this.compTitle = compTitle; } public String getState() { return state; } public void setState(String state) { this.state = state; }
接着,在跳轉處處理投訴頁面的JSP上的時候,把查詢條件的數據查詢出來,把它賦值給變量。而後使用request域對象把數據發給JSP頁面
//把查詢條件帶過去給JSP頁面 ActionContext.getContext().getContextMap().put("compTitle", complain.getCompTitle()); ActionContext.getContext().getContextMap().put("state", complain.getState()); ActionContext.getContext().getContextMap().put("startTime", startTime); ActionContext.getContext().getContextMap().put("endTime", endTime);
而後在處理投訴頁面的JSP上,經過隱藏域把數據給回Action。。Action在重定向到listUI頁面的時候,就經過配置文件,把參數帶過去:
<!--返回列表展現頁面,重定向到列表展現--> <result name="list" type="redirectAction"> <param name="actionName">complain_listUI</param> <param name="complain.state">${state}</param> <param name="complain.compTitle">${compTitle}</param> <param name="endTime">${startTime}</param> <param name="startTime">${startTime}</param> <param name="encode">true</param> </result>
這樣一來,咱們的查詢條件就沒有丟失了。當咱們操做完數據的時候,咱們的查詢出來的數據仍是原來那部分。