近幾年悟空CRM開源項目在GitHub和碼雲上迅速躥紅,並獲得了上千用戶的關注。目前累計2,000,000下載量。社區人數達到30,000多人。在國內浩瀚的開源市場,悟空CRM在開源道路上傾注10多年的心血。
2010年初,受國外salesforce、zoho等saas供應商的影響,越來越多的企業開始投入CRM的研發。為找到屬於自己的品牌方向,悟空團隊以openerp(odoo)為標杆,開始了在開源道路上探索,併為之進行了近十年的投入。
2014年第一個版本發佈
悟空CRM項目在2010開始啟動,對於初創團隊來講,產品的研發迭代以及投入精力有限,直到2104年悟空CRM推出第一個開源CRM版本V0.0.1,當時的國內企業管理軟件的開源市場並未完全成熟,再加上V0.0.1版本存在功能和技術的限制,產品發佈後並沒有獲得大量的用戶群體。
2016年繼續迭代
悟空CRM在版本迭代上並沒有想想的那麼快,一部分研發投入開源、一部分研發投入二次開發項目,畢竟對於一個企業來講,生存是第一要素。經過兩年的12次迭代代,2016年悟空CRM版本迭代至V0.4.5,並在碼雲和Github上發佈後,獲得了上千家用戶的下載和體驗。V0.4.5該版本在原有的基礎上做了全面升級和改造,是目前市場上使用較為穩定一個版本。
但是在這個技術日新月異的時代。node、vue、微服務、Python、AI各種技術不斷興起,悟空CRM開源提供的技術已經很難滿足現有技術愛好者的需求,開源道路在國內再一次受到阻力,開源項目暫且擱置。
2019年一次新的挑戰
是否繼續開源?是一個很艱難的決定。畢竟開源項目要投入大量的精力。對企業的成本是不小的挑戰。但是悟空CRM畢竟在開源道路上已經傾注多年的心血,開源是必將繼續!
重整旗鼓!這一次在技術方面面臨一個重大的調整,悟空CRM決定推出目前流行的前後端分離技術,後端採用PHP和JAVA 兩種開發語言,前端採用最火爆的vue架構。兩種後臺開發語言架構是國內唯一嘗試的,無論是PHP或者JAVA任一技術棧的開發人員都可嘗試學習。
2019年做極致的開源產品
2019年4月PHP版發佈了:基於TP5.0+vue+ElementUI的前後端分離CRM系統
2019年6月JAVA版發佈了:基於jfinal+vue+ElementUI的前後端分離CRM系統項目
下載地址:https://gitee.com/wukongcrm
兩個產品同年發佈,發佈後便獲得的大量開源愛好者的關注和親睞。高質量的源碼、高標準的UI設計,這一次不僅僅在技術上悟空CRM投入的大量的時間和精力,在用戶體驗和UI上也進行了全面的升級的改造。
10年磨一劍,悟空CRM的開源道路還需繼續前進,也會不斷面臨更大的挑戰,相信這個開源方向已經嵌入悟空CRM的骨髓,悟空的金箍棒總有一天會變得更強大。
悟空CRM開源項目主要功能:悟空CRM開源版一共包含了100多種模塊應用
JAVA版本
核心框架:jfinal3.8
緩存:redis caffeine
數據庫連接池:Druid
工具類:hutool,fastjson,poi-ooxml
定時任務:jfinal-cron
項目構建工具:maven
Web容器:tomcat,undertow(默認)
前端MVVM框架:Vue.JS 2.5.x
路由:Vue-Router 3.x
數據交互:AxiosUI框架:Element-UI 2.6.3
項目結構
├── LICENSE
├── README.md
├── build // 配置
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── favicon.ico
├── index.html // 入口html文件
├── package-lock.json
├── package.json // 定義項目所需要的各種模塊,以及配置信息
├── src
│ ├── App.vue // 頁面入口
│ ├── api // api接口
│ │ ├── businessIntelligence // 商業智能
│ │ ├── common.js // 公共
│ │ ├── customermanagement // 客戶管理
│ │ ├── login.js // 登錄
│ │ ├── oamanagement //辦公
│ │ ├── personCenter
│ │ └── systemManagement
│ ├── assets // 圖片和圖標
│ │ ├── 401_images
│ │ ├── 404_images
│ │ ├── iconfont
│ │ └── img
│ ├── components // 自定義組件
│ │ ├── CreateCom // 新建
│ │ │ ├── CrmRelative.vue // 關聯客戶管理列表
│ │ │ ├── CrmRelativeCell.vue
│ │ │ ├── CrmRelativeTable.vue
│ │ │ ├── XhBusinessStatus.vue // 商機狀態
│ │ │ ├── XhCustomerAddress.vue // 新建客戶下的地圖位置
│ │ │ ├── XhDate.vue // 時間選擇
│ │ │ ├── XhDateTime.vue
│ │ │ ├── XhFiles.vue // 附件
│ │ │ ├── XhInput.vue // 單行輸入框
│ │ │ ├── XhMultipleSelect.vue // 多選
│ │ │ ├── XhProduct.vue // 產品關聯
│ │ │ ├── XhProuctCate.vue // 產品類別
│ │ │ ├── XhReceivablesPlan.vue // 回款計劃
│ │ │ ├── XhSelect.vue // 單選
│ │ │ ├── XhStrucUserCell.vue // 員工部門選擇框
│ │ │ ├── XhStructure.vue // 部門選擇
│ │ │ ├── XhStructureCell.vue
│ │ │ ├── XhTextarea.vue // 多行輸入框
│ │ │ ├── XhUser.vue // 員工選擇
│ │ │ ├── XhUserCell.vue
│ │ │ ├── arrayMixin.js // 公共邏輯
│ │ │ ├── index.js
│ │ │ ├── objMixin.js
│ │ │ └── stringMixin.js
│ │ ├── CreateSections.vue // 容器佈局
│ │ ├── CreateView.vue
│ │ ├── DetailCell.vue
│ │ ├── EditImage.vue // 編輯圖片
│ │ ├── Examine // 審批展示和操作
│ │ ├── MapView.vue // 地圖預覽位置
│ │ ├── SlideView.vue
│ │ ├── emoji.vue // 表情
│ │ ├── flexbox // flex
│ │ │ ├── flexbox-item.vue
│ │ │ ├── flexbox.vue
│ │ │ └── index.js
│ │ ├── relatedBusiness.vue // 關聯客戶內容
│ │ ├── reminder.vue
│ │ ├── selectEmployee // 選擇員工
│ │ └── vuePictureViewer // 文件預覽
│ ├── directives // 自定義指令
│ │ ├── empty
│ │ ├── photo
│ ├── filters // 過濾器
│ ├── main.js // 程序入口
│ ├── permission.js // 路由處理
│ ├── route加載更多
複製代碼通過權限註解在攔截器判斷用戶是否擁有訪問權限
@Override
public void intercept(Invocation invocation) {
//TODO 權限功能後臺攔截
Permissions permissions=invocation.getMethod().getAnnotation(Permissions.class);
if(permissions!=null&&permissions.value().length>0){
JSONObject jsonObject= Aop.get(AdminRoleService.class).auth(BaseUtil.getUserId());
//組裝應有權限列表
List<String> arr=queryAuth(jsonObject, "");
boolean isRelease=false;
for (String key : permissions.value()) {
if(!isRelease){
if(arr.contains(key)){
isRelease=true;
}
}
}
if(!isRelease){
invocation.getController().renderJson(R.error("無權訪問"));
return;
}
}
invocation.invoke();
}複製代碼通過AOP和註解對數據進行非空校驗,無需一個個判斷參數是否為空,數據為空直接返回 自定義分頁數據接收,自動處理分頁參數和數據對象,給controller方法加上參數 BasePageRequest,T為對象類型,然後參數就會自動組裝成分頁參數和定義的對象類,以下為實現代碼:
public class PageParaGetter extends ParaGetter<BasePageRequest> {
public PageParaGetter(String parameterName, String defaultValue) {
super(parameterName, defaultValue);
}
@Override
protected BasePageRequest to(String s) {
return null;
}
@Override
@SuppressWarnings("unchecked")
public BasePageRequest get(Action action, Controller controller) {
Parameter[] parameters=action.getMethod().getParameters();
Class clazz=null;
for (Parameter parameter:parameters){
if(BasePageRequest.class.isAssignableFrom(parameter.getType())){
Type parameterizedType=parameter.getParameterizedType();
if (parameterizedType instanceof ParameterizedType) {
Type[] params = ((ParameterizedType) parameterizedType).getActualTypeArguments();
clazz= TypeUtils.getClass(params[0]);
}
break;
}
}
boolean isJson=controller.getHeader("Content-Type")!=null&&controller.getHeader("Content-Type").toLowerCase().contains("application/json");
return isJson?new BasePageRequest(controller.getRawData(),clazz):new BasePageRequest(controller.getKv(),clazz);
}
}
複製代碼自定義json工廠,實現對數據的個性化解析返回,如實現將數據返回時將數據轉成駝峰規則,自定義某種類型的對象的返回格式等。可以自定義錯誤處理模板,在出現錯誤或者其他異常的情況下,可以給予用戶一個清晰的提示,避免用戶看到一些無用的錯誤信息等功能文件可以上傳到項目目錄之外,避免了重新打包項目後文件的丟失
@Override
public void configConstant(Constants me) {
me.setDevMode(prop.getBoolean("jfinal.devMode", true));
me.setInjectDependency(true);
//設置上傳文件到哪個目錄
me.setBaseUploadPath(BaseConstant.UPLOAD_PATH);
me.setBaseDownloadPath(BaseConstant.UPLOAD_PATH);
//自定義json工廠
me.setJsonFactory(new ErpJsonFactory());
//限制上傳100M
me.setMaxPostSize(104857600);
}
複製代碼採用項目分層化的設計,職責分工明確,降低代碼的耦合性Hander->對指定規則的url進行捕獲或者放心Interceptor->環繞式AOP攔截,對訪問權限,數據權限,參數等進行校驗,可以配置在全局,單個路由,單個controller,單個方法等上面,可進行自定義實現,對數據進行處理Router->對不同規則的數據進行分發,不同url進入不同路由和controllerController->對參數進行組裝,將數據傳入到service處理後進行render返回Service->對業務代碼進行處理,並將數據轉入Db處理或緩存 Db->對數據庫進行操作 Render->將service返回的數據在controller進行返回,以及出錯後通過SQL模板功能,將sql寫入到xx.sql文件中,如果sql文件有變動,無需重新編譯打包,直接改動sql文件中的sql即可,以下為自動掃描指定路徑下sql文件的代碼:
private void getSqlTemplate(String path, ActiveRecordPlugin arp) {
File file = new File(path);
if (file.exists()) {
File[] files = file.listFiles();
if (files != null && files.length > 0) {
for (File childFile : files) {
if (childFile.isDirectory()) {
getSqlTemplate(childFile.getAbsolutePath(), arp);
} else {
if (childFile.getName().toLowerCase().endsWith(".sql")) {
arp.addSqlTemplate(childFile.getAbsolutePath().replace(PathKit.getRootClassPath(), "").replace("\\", "/"));
}
}
}
}
}
}
PHP版本後端框架:
ThinkPHP 5.0.2
前端MVVM框架:Vue.JS 2.5.x
路由:Vue-Router 3.x
數據交互:AxiosUI
框架:Element-UI 2.6.3
悟空crm9.0的運行環境要求PHP5.6以上悟空CRM9.0(PHP)版本應用部署
目錄結構
├─application 應用目錄(可設置)
│ ├─admin 系統設置目錄
│ │ ├─config.php 模塊配置文件
│ │ ├─common.php 模塊函數文件
│ │ ├─controller 控制器目錄
│ │ ├─model 模型目錄
│ │ ├─validate 驗證器目錄
│ │ ├─view 視圖目錄
│ │ └─lang 語言包
│ ├─bi 商業智能模塊目錄
│ │ ├─config.php 模塊配置文件
│ │ ├─common.php 模塊函數文件
│ │ ├─controller 控制器目錄
│ │ ├─model 模型目錄
│ │ ├─validate 驗證器目錄
│ │ ├─view 視圖目錄
│ │ └─lang 語言包
│ ├─common 公共模塊目錄
│ │ ├─adapter 認證權限類目錄
│ │ ├─behavior 行為(鉤子)目錄
│ │ ├─controller 公共控制器目錄
│ │ └─lang 語言包
│ ├─crm 客戶管理目錄
│ │ ├─config.php 模塊配置文件
│ │ ├─common.php 模塊函數文件
│ │ ├─controller 控制器目錄
│ │ ├─model 模型目錄
│ │ ├─validate 驗證器目錄
│ │ ├─view 視圖目錄
│ │ └─lang 語言包
│ ├─lang 語言包
│ ├─oa 辦公目錄
│ │ ├─config.php 模塊配置文件
│ │ ├─common.php 模塊函數文件
│ │ ├─controller 控制器目錄
│ │ ├─model 模型目錄
│ │ ├─validate 驗證器目錄
│ │ ├─view 視圖目錄
│ │ └─lang 語言包
│ ├─work 項目管理目錄
│ │ ├─config.php 模塊配置文件
│ │ ├─common.php 模塊函數文件
│ │ ├─controller 控制器目錄
│ │ ├─model 模型目錄
│ │ ├─validate 驗證器目錄
│ │ ├─view 視圖目錄
│ │ └─lang 語言包
│ ├─command.php 命令行工具配置文件
│ ├─common.php 應用公共(函數)文件
│ ├─tags.php 應用行為擴展定義文件
├─config 配置目錄(可定義)
│ ├─config.php 應用(公共)配置文件
│ ├─database.php 數據庫配置文件
│ ├─route_admin.php 系統設置路由文件
│ ├─route_bi.php 商業智能路由文件
│ ├─route_crm.php 客戶管理路由文件
│ ├─route_oa.php 辦公路由文件
│ ├─route_work.php 項目管理路由文件
│ └─version.php 版本信息文件
├─extend 擴展類庫目錄(可定義)
├─public WEB 部署目錄(對外訪問目錄)
│ ├─sql 安裝及更新sql目錄
│ ├─static 靜態資源存放目錄(css,js,image)
│ └─.uploads 上傳文件目錄
├─runtime 應用的運行時目錄(可寫,可設置)
├─static 前端VUE打包目錄=
├─vendor 第三方類庫目錄(Composer)
├─thinkphp 框架系統目錄
│ ├─lang 語言包目錄
│ ├─library 框架核心類庫目錄
│ │ ├─think Think 類庫包目錄
│ │ └─traits 系統 Traits 目錄
│ ├─tpl 系統模板目錄
│ ├─.htaccess 用於 apache 的重寫
│ ├─.travis.yml CI 定義文件
│ ├─base.php 基礎定義文件
│ ├─composer.json composer 定義文件
│ ├─console.php 控制檯入口文件
│ ├─convention.php 慣例配置文件
│ ├─helper.php 助手函數文件(可選)
│ ├─LICENSE.txt 授權說明文件
│ ├─phpunit.xml 單元測試配置文件
│ ├─README.md README 文件
│ └─start.php 框架引導文件
├─composer.json composer 定義文件
├─LICENSE.txt 授權說明文件
├─README.md README 文件
├─think 命令行入口文件
├─index.php 應用入口文件
├─index.html 前端展示入口文件複製代碼系統截圖