聊聊这个倾注10年的开源CRM项目,如何一步步火爆GitHub!

近几年悟空CRM开源项目在GitHub和Gitee上迅速蹿红,并获得了上千用户的关注。目前累计2,000,000下载量。社区人数达到30,000多人。在国内浩瀚的开源市场,悟空CRM在开源道路上倾注10多年的心血

 

 

2010年的一个灵感

2010年初,受国外salesforce、zoho等saas供应商的影响,越来越多的企业开始投入crm的研发。为找到属于自己的品牌方向,悟空团队以openerp(odoo)为标杆,开始准备在开源道路上探索,并为之进行了近十年的投入。

 

2014年第一个版本发布

悟空CRM项目在2010开始启动,对于初创团队来讲,产品的研发迭代以及投入精力有限,直到2014年悟空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官方网站:https://www.5kcrm.com

 

技术干货

聊聊开源悟空CRM的技术干货

悟空CRM开源项目主要功能:

 

客户管理线索(线索转移,线索转客户)
客户(客户分配,客户领取,客户编辑、客户添加、客户跟进)

附近的客户(定位信息。地图展示)

联系人管理(联系人添加、联系人转移)
商机管理(商机添加、商机推进、商机组设置)
合同(合同审核、合同录入、合同转移)
回款(回款添加、回款审核)
产品(产品规格配置、产品添加、产品上下架)
回款计划(回款计划设定、定时提醒)
自定义字段(所有模块字段均可以自定义配置)

 

OA审批工作日志(日报、周报、月报)
OA审批流(请假、报销、差旅、费用、自定义设置审批)
自定义审批流(配置审核节点和规则)
项目管理(项目添加、项目统计、项目归档、项目权限)
任务管理(任务分配、任务提醒、任务评论、任务完成)
企业通讯录(员工管理、员工基本信息录入)

 

BI商业智能

客户分析(客户增量、跟进次数、客户转化率、成交周期、跟进方式)

销售漏斗(新增商机分析、商机转化率分析)

客户画像分析(地区分部、行业分部、年龄分部、)

产品分析(产品销量、产品分类销量)

销售排行榜(合同排行榜、回款排行榜、客户总量排行榜、客户增量排行榜)
财务分析(回款分析、合同金额分析)

审批分析(日志完成情况分析、请假、报销等审批分析)

业绩分析(业绩目标、合同总量、回款总量)

 

悟空CRM开源版一共包含了100多种模块应用

 

前端VUE

 

  • 动态的权限路由

  • axios封装,统一处理请求核心信息

  • 组件的封装和复用

  • 登录信息的缓存

  • 常用指令和过滤器的封装

  • iconfont图标的使用

 

VUE项目结构

├── 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                          // 路由处理│   ├── router                                // 路由│   │   ├── index.js│   │   └── modules│   │       ├── business.js│   │       ├── customer.js│   │       ├── manager.js│   │       ├── person.js│   │       └── workbench.js│   ├── store                                  // vuex│   │   ├── getters.js│   │   ├── index.js│   │   └── modules│   │       ├── app.js│   │       ├── permission.js│   │       └── user.js│   ├── styles                                   // 样式│   ├── utils                                    // 工具类│   │   ├── auth.js│   │   ├── cache.js│   │   ├── emoji-data.js│   │   ├── emoji.js│   │   ├── index.js│   │   ├── request.js                           // 请求│   │   └── validate.js│   └── views                                   // 页面│       ├── 401.vue│       ├── 404.vue│       ├── OAManagement                       // 办公│       │   ├── addressBook                      // 通讯录│       │   ├── components│       │   ├── examine                          // 审批│       │   ├── journal                           // 日志│       │   ├── notice                            // 公告│       │   ├── schedule                          // 日程│       │   ├── styles│       │   ├── task                              // 任务│       │   └── workbench                        // 工作台│       ├── PersonCenter                          // 个人中心│       ├── SystemManagement                    // 系统管理│       │   ├── RoleAuthorization.vue              // 角色权限│       │   ├── SystemConfig                     // 系统配置│       │   ├── SystemCustomer                  // 客户管理│       │   ├── SystemEmployee                  // 员工部门│       │   ├── SystemExamine                   // 审批流管理│       │   ├── SystemWorkbench                // 工作台│       │   ├── components│       │   └── styles│       ├── businessIntelligence                   // 商业智能│       │   ├── customer                        // 员工客户分析│       │   ├── business                         // 销售漏斗分析│       │   ├── achievement                     // 员工业绩分析│       │   ├── product                         // 产品分析│       │   ├── TaskCompleteStatistics.vue         // 业绩目标完成情况│       │   ├── customerPortrayal                 // 客户画像分析│       │   └── ranking                          // 排行榜│       ├── customermanagement                  // 客户管理│       │   ├── business                          // 商机│       │   ├── clue                              // 线索│       │   ├── components│       │   ├── contacts                           // 联系人│       │   ├── contract                           // 合同│       │   ├── customer                          // 客户│       │   ├── mixins│       │   ├── model│       │   ├── money                            // 回款│       │   ├── product                           // 产品│       │   ├── seas                              // 公海│       │   ├── styles│       │   └── workbench                        // 工作台│       ├── layout                                // 框架│       │   ├── businessLayout.vue                 // 框架│       │   ├── components│       │   ├── customerLayout.vue│       │   ├── managerLayout.vue│       │   ├── personCenterLayout.vue│       │   ├── styles│       │   └── workbenchLayout.vue│       └── login                                 // 登录│           └── index.vue└── static                                         // 静态文件    ├── client.js                                   // 判断浏览器    ├── client.min.js    ├── img    │   ├── emoji                                 // 表情图片    ├── index.css    └── index.html                                 // 提示页面

 

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
数据交互Axios
UI框架Element-UI 2.6.3

 

JAVA后台目录结构

 

 

 

通过权限注解在拦截器判断用户是否拥有访问权限

@Overridepublic 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工厂,实现对数据的个性化解析返回,如实现将数据返回时将数据转成驼峰规则,自定义某种类型的对象的返回格式等。

可以自定义错误处理模板,在出现错误或者其他异常的情况下,可以给予用户一个清晰的提示,避免用户看到一些无用的错误信息等功能

文件可以上传到项目目录之外,避免了重新打包项目后文件的丢失

@Overridepublic 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进入不同路由和controller

Controller->对参数进行组装,将数据传入到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
数据交互Axios
UI框架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            前端展示入口文件

产品图片部分展示

 

仪表盘:

 

商机页面:

 

客户详情页面:

 

任务列表页面:

 

任务详情页面:

 

审批流页面:

 

用户权限页面:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值