聊聊这个倾注10年的开源项目,如何一步步火爆GitHub!-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

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

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

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: