某电商平台开发记要——客服系统
假如網(wǎng)站需要提供客服功能,如果只是簡單的聊天咨詢可以考慮營銷QQ、百度商橋等(目前大部分網(wǎng)站采用此方式,包括一些知名行業(yè)電商);如果需要更精細化的管理,比如客服人員安排、各項數(shù)據(jù)統(tǒng)計匯總,那么需要對接專業(yè)的第三方客服平臺,比如網(wǎng)易七魚,當(dāng)然價格不菲;然而若是如京東本身就是一個平臺,需要為每個商家提供各自的客服管理,首先目前第三方提供商并無此類產(chǎn)品(網(wǎng)易七魚據(jù)說已經(jīng)開發(fā)出來了,但是官網(wǎng)上沒找到),其次即使有,價格也肯定不便宜,而且數(shù)據(jù)在別人那里總歸不好。所以電商平臺的客服系統(tǒng),一般都是自己開發(fā)。當(dāng)然了,借助優(yōu)秀的開源項目,自主開發(fā)[一套簡單能用的]也變得輕松很多。
我采用了openfire+spark+layim,前兩者基于java平臺,layim是國人開發(fā)的一個webim前端組件。
先來看大致效果(左邊是瀏覽器layim-客戶提咨詢,右邊是spark聊天窗口-客服解答)
圖示:
本文涉及到的知識點(雜亂,后續(xù)會不定期添加內(nèi)容):
Java基礎(chǔ)
Intellij Idea:Java IDE
Mybatis:半ORM
XMPP協(xié)議
smack:XMPP協(xié)議的Java封裝
openfire
fastpath:openfire插件,我們需要依賴它實現(xiàn)客服功能
spark
一秒鐘入門Java
Java SE(J2SE):Standard Edition,可認(rèn)為是基礎(chǔ)庫,用于開發(fā)和部署桌面、服務(wù)器以及嵌入設(shè)備(J2ME)和實時環(huán)境中的Java應(yīng)用程序。
Java EE(J2EE):基于SE的高級庫,提供 Web 服務(wù)、組件模型、管理和通信 API,可以用來實現(xiàn)企業(yè)級的面向服務(wù)體系結(jié)構(gòu)。
可以知道J2EE比J2SE多了Web相關(guān)的組件和API,但是本人在使用SpringMVC框架開發(fā)Web應(yīng)用程序時,去官網(wǎng)Java SE頁面下載的JDK,也能正常開發(fā)。后來查看官網(wǎng)的Java EE的下載頁面,發(fā)現(xiàn)提供的SDK中主要包含一個叫GlassFish的開源組件和一些示例及文檔,而Java EE剛開始是以一種規(guī)范提出,GlassFish可以看作是實現(xiàn)了這些規(guī)范的JEE容器,而我們開發(fā)Web站點時部署到服務(wù)器(比如Tomcat),實現(xiàn)了JEE規(guī)范其中的Servlet容器部分,所以以JDK開發(fā)Web并不會出現(xiàn)問題。
JNDI 是什么
目前流行的IDE有Eclipse和IntelliJ IDEA,前者免費且由于歷史關(guān)系占有率一直很高,后者也有社區(qū)版,據(jù)說使用性上目前完勝前者。
final關(guān)鍵詞:類似于.NET的readonly
匿名內(nèi)部類:
定義一個類A(可以為abstract),為方便說明,在A中定義一個[抽象]方法dosth。在調(diào)用方法里可以直接new A,并且同時給dosth賦方法體。
public abstract A{ public void dosth() {} }public abstract B{ public void call() {final A a = new A() {public void dosth() {//這里寫方法體 }};} }看著是實例化了A的一個對象,其實是實例化了A類的匿名子類。
Access restriction:eclipse對某些java包(or 類?)有access rules,比如 sun.awt.shell.ShellFolder。因為這些JAR默認(rèn)包含了一系列的代碼訪問規(guī)則(Access Rules),如果代碼中引用了這些訪問規(guī)則所禁止引用類,那么就會提示這個錯誤信息。解決方法:既然存在訪問規(guī)則,那么修改訪問規(guī)則即可。打開項目的Build Path Configuration頁面,打開引用的[報錯]JAR包,選中Access rules條目,選擇右側(cè)的編輯按鈕,添加一個訪問規(guī)則即可。
Java NIO
Apache Mina
CopyOnWrite:CopyOnWrite容器即寫時復(fù)制的容器。通俗的理解是當(dāng)我們往一個容器添加元素的時候,不直接往當(dāng)前容器添加,而是先將當(dāng)前容器進行Copy,復(fù)制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。這樣做的好處是我們可以對CopyOnWrite容器進行并發(fā)的讀,而不需要加鎖。從JDK1.5開始Java并發(fā)包里提供了兩個使用CopyOnWrite機制實現(xiàn)的并發(fā)容器,它們是CopyOnWriteArrayList和CopyOnWriteArraySet。
Maven:項目管理工具。不像VS,eclipse是更純粹的編碼工具,在維護jar包和項目之間的依賴關(guān)系、項目的構(gòu)建目標(biāo)等方面的功能比較弱(比如拷貝了一個項目,我們需要手動去Configure Build Path),而Maven就是補足于此。Maven獨立于IDE,eclipse有一個插件叫M2E,里面內(nèi)置了Maven。Maven項目的配置信息保存在pom.xml文件中。
我們在導(dǎo)入Maven項目時,有時會發(fā)現(xiàn)不止一個pom.xml,那是因為項目中有子項目(module,module有自己的pom.xml),只要選擇最頂層的pom.xml文件即可,會自動加載引用到的子項目。
JavaBean:一般可看作是POJO,可參看?Java Bean 是個什么概念??(不過這個問題里有個答主說Java沒有事件的概念,讓我大吃一驚,不過轉(zhuǎn)念一想,Java主要用于開發(fā)服務(wù)端應(yīng)用,確實不怎么涉及到[自定義]事件。其實Java中是有事件機制的,只是不知變通,就一個半成品的觀察者模式,想想C#的委托,其實就一個函數(shù)指針的事)
MVC:當(dāng).Neter們在被Asp.Net的重量壓得踹不過氣來的時候,Java已經(jīng)有MVC的概念了。很多模式,.Net界都是直接copy,.Neter們并沒有對其歷史的認(rèn)知,所以接收不能,MVC就是如此。其實在Asp.Net時代已經(jīng)有MVC的影子,就是一般處理程序.ashx。很早以前,用戶提交都是提交到具體的一個頁面,于是會經(jīng)常導(dǎo)致一個頁面并不是用于顯示,而是用于業(yè)務(wù)邏輯的處理,于是后來把業(yè)務(wù)邏輯單獨拎出來,這便是Controller,用戶請求的是Controller,不再是具體頁面,并且Controller里不再使用類似HttpRequest或者HttpResponse獲取數(shù)據(jù)和返回響應(yīng),而是使用對象的形式(M),這便是MVC模式。可參看?Java Web開發(fā)模式
Java中的注解相當(dāng)于.NET中的Attribute。
Spring是一個IOC和AOP框架。我們可以通過在xml文件中配置bean,然后在代碼中使用@Autowired或@Resource注入bean實例,不過配置的環(huán)節(jié)稍顯繁瑣,能不能省略呢?答案是肯定的,Sping2.5開始支持注解注入,具體可看 spring注解注入:<context:component-scan>詳解。需要注意的是,@Component及相關(guān)的幾個注解類,在應(yīng)用到interface上的時候,可能并不如預(yù)期工作,因為interface并不能實例化,而這幾個注解類貌似又沒有@Inherited修飾,所以就算有實現(xiàn)類或運行時的動態(tài)實現(xiàn)類,也不會注冊到上下文中;且修飾的類要有公共構(gòu)造函數(shù)。另外注入[被注入方]一般只能在注入方本身是已注冊的bean里,若在普通類里想通過@Autowired或@Resource的方式注入bean,則稍微有點繞,可參看?Java普通類獲取Spring XML中Bean的方法總結(jié)
關(guān)于Servlet、Struts、Spring、SpringMVC的關(guān)系與區(qū)別可參看?Java開發(fā)web的幾種開發(fā)模式 和?SpringMVC與Struts2的對比
SpringMVC竟然URL和參數(shù)大小寫敏感,雖然有辦法配置,但這種預(yù)設(shè)沒有道理吧。。。
Servlet url-pattern /與/*區(qū)別:兩者的長度不同,根據(jù)最長路徑匹配的優(yōu)先級,/*比/更容易被選中,而/的真正含義是,缺省匹配。既所有的URL都無法被選中的時候,就一定會選中/,可見它的優(yōu)先級是最低的,這就兩者的區(qū)別。
xml文件也可以打包進jar包,但是訪問jar包里的xml文件就不能按文件目錄的方式來了,可參看?http://blog.csdn.net/jianxin1009/article/details/18814799?
application.getInitParameter:jsp中9個內(nèi)置對象之一application,它的數(shù)據(jù)對整個web應(yīng)用都有效,application有一個重要的用途就是通過getInitParameter()獲取web.xm中的配置參數(shù),這樣可以提高代碼的移植性。
dwr:簡化ajax調(diào)用,使得調(diào)用遠程服務(wù)器方法看上去像調(diào)用本地方法一樣。
在java項目中必不可少的是我們要指定一個jdk。在指定jdk的同時,還可以指定jdk的Language level,這個有點像我們工程最低支持版本。比如Language level 設(shè)置了5.0 只是就不能出現(xiàn)使用6.0/7.0特性的代碼,因為這些特性在5.0的環(huán)境下是無法編譯的。或者可以理解ide會安裝Language level指定的jdk版本來對我們的代碼進行編譯,以及錯誤檢查。即同樣的jdk對應(yīng)不同的Language Level會采用[可能]不同的編譯和優(yōu)化方式。
Java中也有類似.Net的字符串池的概念,請看?String中intern的方法
Java插件技術(shù): OSGi
貌似在同一package下,protected可見。(和.NET不同)
Java的泛型類型只能是引用類型,而不能是基礎(chǔ)類型,但是Java針對每個基礎(chǔ)類型有對應(yīng)的封裝類型,比如boolean對應(yīng)Boolean,后者是引用類型,可以為null,當(dāng)封裝類型不為null時,可以隱式轉(zhuǎn)換,但寫代碼時null的情況要自己處理,如
private boolean existUser(String username) {Boolean result = null; return result != null && result.booleanValue(); }Ant:類似于.NET的MSBuild,其構(gòu)建文件默認(rèn)為build.xml(可以在其中指定構(gòu)建基于的Java平臺版本),每個構(gòu)建文件都對應(yīng)于一個項目,但是大型項目經(jīng)常包含大量的子項目,每一個子項目都可以有自己的構(gòu)建文件。
一個.java文件中可以定義多個類,但是public修飾的只能至多有一個,且要與文件名相同,編譯后,有幾個類就會產(chǎn)生幾個對應(yīng)的.class文件。jar包類似.Net的dll,它將多個.class文件打包一塊。大多數(shù)?JAR?文件包含一個?META-INF?目錄,它用于存儲包和擴展的配置數(shù)據(jù),如安全性和版本信息。Java 2?平臺識別并解釋?META-INF?目錄中的下述文件和目錄,以便配置應(yīng)用程序、擴展和類裝載器。具體可看?MANIFEST.MF 文件內(nèi)容完全詳解。
System.getProperty()獲取系統(tǒng)/項目全局變量,比如Java運行時版本,當(dāng)然我們也可以通過System.setProperty()設(shè)置自定義變量。
Java桌面客戶端編程:Java Swing 。桌面程序畢竟不是Java的主流領(lǐng)域,因此各IDE貌似也并未作太多努力,相較VS的所見即所得的控件拖拽開發(fā)模式,Java GUI編程就吃力很多了。
Java國際化:i18n,注意中文的資源文件,貌似需要先UTF-8轉(zhuǎn)碼,大約就是像這樣。(可以使用JDK自帶的native2ascii.exe)
Intellij Idea
使用Intellij Idea創(chuàng)建spring mvc時(沒用maven),run都報?Error during artifact deployment. See server log for details 錯誤,后來把lib文件夾拷到WEB-INFO文件夾下就沒問題了,不知何故。
原因:tomcat默認(rèn)是去web-info/lib/下找依賴的jar包。手動拷j(luò)ar包畢竟不是一個好辦法,其實我們可以在下圖處進行Artifacts設(shè)置
運行項目,項目目錄下會多出一個out文件夾,生成所有的站點文件,依賴包會自動拷貝到下面的WEB-INF/lib/下,如下圖:
IDEA配置artifacts中Web Application:Exploded和Web Application:Archive的區(qū)別:前者以文件夾形式(War Exploded)發(fā)布項目,后者以war包形式(每次都會重新打包全部的)。Tomcat會自動解壓war包并啟動站點,缺點是會造成一段時間的站點不可用,而以文件夾形式發(fā)布的話,則支持熱部署(需進行額外的一些配置)。
當(dāng)然我們也可以使用Maven進行依賴包的管理。在當(dāng)前項目右鍵->Add Framework Support->Maven即可。注意需要在Project Structure-> Project Settings中移除之前非Maven引用的包依賴。此時運行項目,項目目錄下會多出一個target文件夾,其下有生成的站點文件。但是運行時發(fā)現(xiàn)WEB-INF下的文件除了web.xml外,其它的文件都不會覆蓋,貌似用maven管理的web工程,需要將applicationContext.xml等資源文件放在resource目錄下,然后以classpath的方式去訪問。后來發(fā)現(xiàn)jsp頁面也無法自動更新到target目錄,再后來聽說maven有一套約定的目錄結(jié)構(gòu),貌似又可以通過pom.xml進行自定義配置,神煩!目前靠手動覆蓋。參考?Maven使用點滴?配置即可(webappDirectory我沒設(shè)置,就設(shè)置了warSourceDirectory,能正常更新了)
Intellij Idea中有個Ant Build Window,默認(rèn)顯示的是主項目下build.xml中的targets,and?by default, IDEA only shows the default target and targets that have descriptions。對這個有疑問可參看?How to get Ant Build to list all targets in a hierarchy of build files.
可以在Run/Debug Configurations Window中設(shè)置自定義系統(tǒng)變量,如下圖(-D不能省):
MyBatis
一個半ORM框架,SQL語句并不是像EF一樣由框架解析,而是要預(yù)先寫在xml中或者寫在Java注解(同.Net的Attribute)中,且不支持匿名類型(即select出來的數(shù)據(jù)要么是基礎(chǔ)類型,要么要有對應(yīng)的Java Bean)。一般情況下,我們使用resultType映射查詢結(jié)果和對象即可(MyBatis 會在幕后自動創(chuàng)建一個 ResultMap),當(dāng)只想映射部分字段或者包含復(fù)雜類型屬性的時候,我們需要自定義ResultMap。
MyBatis不支持方法重載,因為它是通過方法名稱(不加參數(shù))去查找執(zhí)行方法,因此我們設(shè)置不同的方法名,或者使用動態(tài)sql。
XMPP協(xié)議
JID表示一個地址,由三部分組成——node、domain和resource。例如:xiaoming@xiaoming.home/sleeping,xiaoming就是node ,xiaoming.home就是domain,sleeping就是resource。node domain 和resource任何一部分都不能超過1023 字節(jié) ,加上@和 /,一個JID 總共不能超過3071字節(jié)。BareJid就是去掉resource,只包含node@domain。
XMPP包含IQ, message and presence 三種packet。
smack
ConnectionConfiguration.Builder的setXmppDomain和setHost的區(qū)別?一個是域(服務(wù)器集群),一個是其中的一臺服務(wù)器,應(yīng)該只要設(shè)置其中一個就可以了。
使用XMPPTCPConnectionConfiguration建立連接時報空指針錯誤,調(diào)試發(fā)現(xiàn)有個base64encoder未賦值,需要引用smack-java7包,該包會初始化base64encoder,如果是安卓開發(fā),那么就引用smack-android。
openfire?
使用idea導(dǎo)入openfire代碼,過程可參考將openfire源碼部署到IDEA中 或者?IntelliJ IDEA搭建openfire4.1.3開發(fā)環(huán)境 。使用openfire配置界面只能配置一個數(shù)據(jù)庫,且我也不打算完全依賴它生成的數(shù)據(jù)庫。我需要openfire部分功能使用現(xiàn)有的數(shù)據(jù)庫(比如用戶表),而openfire的業(yè)務(wù)數(shù)據(jù)仍然使用生成的數(shù)據(jù)庫,因此涉及到多庫連接。這只能去修改源碼了。
上面說到的配置界面設(shè)置的項最終存儲在ofproperty表中。在配置界面完成配置后,我們也可以在conf/openfire.xml中重新設(shè)置值,重啟openfire,配置文件中的值會更新到數(shù)據(jù)庫中。
以AuthFactory為例,其initProvider方法里有?JiveGlobals.migrateProperty("provider.auth.className"); ,XMLProperties根據(jù)"provider.auth.className"讀取xml文件中的值(getProperty方法)
//按逗號拆分為數(shù)組 String[] propName = parsePropertyName(name); // Search for this property by traversing down the XML hierarchy. Element element = document.getRootElement(); for (String aPropName : propName) {element = element.element(aPropName);if (element == null) {return null;} } value = element.getTextTrim();對應(yīng)的配置節(jié)寫法如下(可以看到,propName對應(yīng)各層級element,而非attribute形式)
<provider><auth><className>org.jivesoftware.openfire.auth.JDBCAuthProvider</className></auth> </provider>而后覆蓋數(shù)據(jù)庫值
public void migrateProperty(String name) {if (getProperty(name) != null) {if (JiveGlobals.getProperty(name) == null) {JiveGlobals.setProperty(name, getProperty(name));deleteProperty(name);}else if (JiveGlobals.getProperty(name).equals(getProperty(name))) {deleteProperty(name);}else if (!JiveGlobals.getProperty(name).equals(getProperty(name))) {Log.warn("XML Property '"+name+"' differs from what is stored in the database. Please make property changes in the database instead of the configuration file.");}} }當(dāng)然,若是我們有數(shù)據(jù)庫權(quán)限,直接進入數(shù)據(jù)庫修改也一樣。
openfire源碼采用JDBC方式操作數(shù)據(jù)庫,而且沒有做很好的封裝,重復(fù)代碼較多,如下圖所示
相似代碼在與數(shù)據(jù)庫交互的地方隨處可見。部分邏輯的抽取,莫過于lambda(回調(diào)函數(shù))的方式。考慮到Java8已經(jīng)支持lambda表達式,重構(gòu)如下:
public <T> T excuteQuery(String queryText, Function<ResultSet, T> func) {T result = null;Connection con = null;PreparedStatement pstmt = null;ResultSet rs = null;try {con = getConnection();pstmt = con.prepareStatement(queryText);rs = pstmt.executeQuery();if (rs.next()) {result = func.apply(rs);}} catch (SQLException e) {Log.error(e.getMessage(), e);} finally {DbConnectionManager.closeConnection(rs, pstmt, con);}return result; }但是在寫調(diào)用代碼的時候提示:
雖然我們在excuteQuery方法中已經(jīng)catch了這個異常,但是編譯器并不買賬。而且就算我們在方法定義時已經(jīng)throws了相關(guān)異常,也沒用,如下圖:
解決方法有兩種:可以在lambda體內(nèi)catch異常后不再throw;或者自定義一個Functional Interface,其中聲明一個定義了異常的方法,
@FunctionalInterface public interface CheckedSQLExceptionFunction<T, R> {R apply(T t) throws SQLException; }然后將Function<Result,T>的地方替換為CheckedSQLExceptionFunction<ResultSet, T>。這兩種都顯得別扭與不合理,導(dǎo)致這一問題的是,Java Lambda規(guī)定如果Lambda中拋出了異常,那么這個異常一定要在Functional Interface中的abstract方法上定義。這是一個讓人無法理解的規(guī)定。
遇到lambda的另一個坑:
由于username有重新賦值,所以編譯報錯,是不是很喜感?我不得不用一個臨時變量解決。。
官方提供了一種集成外部用戶體系的方法(Custom Database Integration Guide),然后并不支持加鹽密碼,于是我只能自己擼碼解決。關(guān)鍵是實現(xiàn)兩個接口:AuthProvider 和 UserProvider,只要實現(xiàn)部分方法即可,很簡單不贅述。
部署
部署到centos7。首先?rpm -qa | grep openjdk 查看所有已安裝的jdk,如果版本不滿足則先 rpm -e --nodeps [java-1.7.0-openjdk[-headless]] 卸載掉。然后去官網(wǎng)上下載合適版本的server jre/jre/sdk包(下面會進一步說明),然后解壓,設(shè)置環(huán)境變量,就算安裝完畢了(不過這種安裝方式通過rpm -qa可是找不到的哦)。具體可看?Centos7 JDK8安裝配置。
講道理,jdk是開發(fā)時候用的,部署的話我們只要安裝jre就可以了。我剛開始下載的是server jre包,在ant的時候報 package javafx.util does not exist 的錯(因為我在代碼里用到了Pair<>二元組,屬于javafx.util包),然而網(wǎng)上查了下,貌似javaFX是用于客戶端GUI方面的組件(不知道是否我這里報錯的javafx同個概念)。我懶得探究,馬上去官網(wǎng)下了jre包(官網(wǎng)說Covers most end-users needs. Contains everything required to run Java applications on your system.),載下來之后發(fā)現(xiàn)果然有jfxrt.jar(包含javafx.util),歡欣鼓舞,但是ant之后報無法找到/lib/tools.jar——因為build.xml里有用到這個jar——之前server jre是有的,也是日了狗了。馬上去下jdk,瘋狂操作之后終于編譯通過。
也可以在windows平臺編譯打包,然后拷貝到linux系統(tǒng)。
官網(wǎng)上是說./openfire start啟動openfire,然而我只找到openfire.bat和openfirectl,先試了./openfirectl start 報錯:Could not find Openfire installation under /opt,/usr/share,or /usr/local,查看openfirectl的shell代碼,發(fā)現(xiàn)當(dāng)OPENFIRE_HOME未設(shè)置時,會去這三個目錄下找openfire,于是為其設(shè)置真實根目錄,然而雖沒報錯,但還是沒有運行起來。試了下openfire.bat,報Permission denied,尼瑪,我可是用root登錄的。先不管原因,我再去官網(wǎng)下了4.1.6(目前最新版)的tar包,發(fā)現(xiàn)bin目錄下果然有個openfire文件,拷到服務(wù)器上后報同樣的Permission denied的錯誤——網(wǎng)上說root并不默認(rèn)就有所有文件的最高權(quán)限,但是他可以隨意給自己增加權(quán)限——好吧,設(shè)置了權(quán)限之后,執(zhí)行./openfire start 沒報錯,但是依舊沒有運行起來。。。后來發(fā)現(xiàn)沒有輸出錯誤信息,是因為shell里寫了/dev/null 2>&1,去掉之后終于提示——Could not find or load main class com.install4j.runtime.launcher.UnixLauncher——shell代碼里該類指向的目錄本地編譯不存在,最后在官網(wǎng)tar包里發(fā)現(xiàn)有一個名為.install4j的隱藏文件夾,拷貝后總算運行起來了。?
記得打開相應(yīng)端口。
webchat
用戶一般都是通過瀏覽器進行咨詢,有個webchat示例可以參考(openfire4.2 配置fastpath、webchat、spark實現(xiàn)客服系統(tǒng)),但那是基于很久以前的smack版本,轉(zhuǎn)過來也費了不少勁,特別是QueueUpdate包擴展已經(jīng)不再內(nèi)置支持,調(diào)試了半天在smack中找到幾個關(guān)鍵文件,這些都是內(nèi)置資源文件,項目運行時會讀取這些文件,調(diào)用ProviderManager.addExtensionProvider將配置項緩存起來,如果不修改xml的話,那么在外部調(diào)用該方法也是可以的。參照著寫了一個QueueUpdateProvider,順便了解了下XmlPullParser的用法。
關(guān)于自定義包和擴展,后來才發(fā)現(xiàn)官網(wǎng)上有介紹: Provider Architecture: Stanza Extensions and Custom IQ's,也是心累。
再后來,發(fā)現(xiàn)部分非內(nèi)置的擴展的Provider已經(jīng)在擴展類里[作為內(nèi)部類]定義好了,比如QueueUpdate.Provider。。。吐血。關(guān)于內(nèi)部類可參看?java中的內(nèi)部類總結(jié)
部署
在CentOS安裝tomcat9.0.1。去官網(wǎng)下載tar.gz包,解壓,然后去到bin目錄,在catalina.sh文件添加內(nèi)容export CLASSPATH=$JAVA_HOME/lib,然后./startup.sh即可,另外記得開放8080端口。當(dāng)然我們可以更改端口以及綁定域名,參考?tomcat發(fā)布應(yīng)用并配置域名。關(guān)于項目打包成war包,參考?Intellij IDEA社區(qū)版打包Maven項目成war包,并部署到tomcat上。
fastpath
增加幾個http接口,如新增客服組,添加客服等,示例代碼如下:
public class MasonServlet extends HttpServlet {@Overridepublic void init(ServletConfig config) throws ServletException {super.init(config);AuthCheckFilter.addExclude("fastpath/mason/*"); // 公共接口不需身份校驗 }@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String action = request.getRequestURI();action = action.substring(action.indexOf("mason/") + 6);OPResult result = null;if (action.toLowerCase().equals("createworkgroup")) {String wgName = request.getParameter("wgName");String description = request.getParameter("description");String agents = request.getParameter("agents");result = createWorkgroup(wgName, description, agents);}if (result == null) {result = new OPResult();result.setSuccess(false);result.setMessage("未找到對應(yīng)方法");}response.setContentType("application/json; charset=utf-8");response.setCharacterEncoding("UTF-8");Genson genson = new Genson();String json = genson.serialize(result);response.getOutputStream().write(json.getBytes("UTF-8"));}// 新增工作組(會同時建立一個默認(rèn)客服組,每個工作組可以包含多個客服組)private OPResult createWorkgroup(String wgName, String description, String agents) {OPResult result = new OPResult();Map errors = WorkgroupUtils.createWorkgroup(wgName, description, agents);if (errors.size() == 0) {Workgroup workgroup = WorkgroupManager.getInstance().getWorkgroup(wgName);result.setData(workgroup.getJID());result.setSuccess(true);} elseresult.setSuccess(false);return result;} }完了我們就可以重新構(gòu)建該插件了,在intellij中可以在窗口中設(shè)置(看了下build.xml,發(fā)現(xiàn)plugin任務(wù)可以構(gòu)建單個插件,它接收plugin的參數(shù)表明構(gòu)建的是哪個插件):
?
由于代碼中用到了genson這個第三方j(luò)ar包,雖然直接編譯沒問題(項目的其它地方有引用),但用ant構(gòu)建的時候會報錯,提示找不到這個組件,原因官網(wǎng)說了:Any JAR files your plugin needs during compilation should be put into the lib directory,因此我們需要將該jar包復(fù)制一份到fastpath/lib目錄下。
spark
此spark非彼spark,而是一個開源IM桌面客戶端。下載下來2.8.3代碼,導(dǎo)入到IntelliJ,運行輸出了空指針異常,調(diào)試發(fā)現(xiàn)找不到資源文件?"META-INF/plugins.xml",查看編譯后的jar文件,里面已經(jīng)包含了resources/META-INF/plugins.xml。再查看Project Structure,發(fā)現(xiàn)沒有為主模塊Spark設(shè)置Resource Folders,添加了resources文件夾后編譯運行正常,此時再看jar文件,里面并沒有resources目錄,META-INF直接在根目錄體現(xiàn)。
也就是說,將某個目錄設(shè)置為資源文件夾(Resource Folders),意即將該目錄下的子目錄一起打包進jar包(不包含該目錄本身),而getResource()方法獲取特定路徑的資源時,是直接去jar包根目錄下查找對應(yīng)文件。
似乎還要設(shè)置VM arguments:-Djava.library.path=build/lib/dist/windows64,具體值按照操作系統(tǒng)來。參看?openfire-spark 二次開發(fā)-(二)運行環(huán)境配置
?
相關(guān)資料:TCP長連接與短連接、心跳機制
?
轉(zhuǎn)載請注明本文出處:http://www.cnblogs.com/newton/p/7269373.html
總結(jié)
以上是生活随笔為你收集整理的某电商平台开发记要——客服系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python文件读取 ,json文件的存
- 下一篇: CefSharp For WPF响应页面