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 前端展示入口文件复制代码系统截图