所有系统的开发都受限于当时的技术积累、人力资源、设计方案,没有完美的系统,
我们只是在自己的技术能力内做到100分。           -- fuyuan

没有完美的系统,只有适合的系统。我不是大神,本文结合我以前在一个企业的CRM管理系统和后来开发维护的一个系统来说介绍下我踩过的坑。
  

业务背景

先说下业务背景吧,我当时接手重构改造任务的时候,面临如下情况:

  1. 老系统部署环境比较陈旧,还是apache+php, 比较老的环境了,经过若干年人员更替,部门分拆,已经不能说清楚上面运行的各种服务了,运维部署起来也比较困难,哪天死了,基本上无法复制,单点情况比较严重。
  2. 所用框架也比较老旧,自有框架,不仅包含后台服务代码,还包含管理系统代码,开发维护也比较困难。
  3. 界面风格也比较旧,还是十几年前的风格,操作起来比较怪异。
  4. 领导授权说重构吧。这个是最关键的,领导授权,是一个项目启动的旗帜。
     
    系统从开发到后来的优化都是遵循的原则PDCA。 
      
     

好,汇总各方面需求总结的情况如下:

  1. 新作一个管理后台系统,最好与主业务一样的结构,这样便于开发。即linux + nginx + php + yaf + memcache + yaconf + mysql等等。
  2. 迁移旧系统功能,按时完成,实现新的需要的功能。
  3. 找个流行的响应式模版。
  4. 技术栈升级,这个是最近才加入的内容。

系统整体架构设计

开始的时候因为对yaf不太熟,所以没有注意到application.dispatcher.defaultRoute.controllerPrefer=true 这个配置。以至于 每次一个接口,都得重建一个文件,写一个controller。
后来我想了一方法:对于a/b/c_d 这种路由,自己写了个规则,映射到A_B_C controller的d action方法中。
这样 对于简单的增删改查方法,就可以放在一个文件里了。

model设计

访问数据库的model,因为yaf框架比较简单,所以我参照了thinkphp的访问数据库部分自己写了一个简单的模块。
后来一个兄弟引入了laravel的ORM模块Eloquent,不过没有迁移所有的业务,所以存在了两套内容。

账号

这个比较简单,跟常用的系统相同。

角色

角色管理是确定角色具备哪些权限的一个过程,他是一个集合的概念,是众多最小权限颗粒的组成。我们通过把权限给这个角色,再把角色给账号,从而实现账号的权限,这是网上摘抄的,我在系统中并没有实现这么复杂。没用用到 超级管理员,管理员,普通用户角色区分,这个要看系统应用场景吧。

权限的定义

网上有各种个样的帖子介绍管理系统的,我也介绍下我的这个系统里面权限的部分。

权限可以分为三种:页面权限,操作权限,数据权限  
页面权限:即入口,用户可以看到哪个页面,看不到哪个页面。
操作权限:进入同样的页面,有些人看到这个按钮可点击,有些人只能浏览这个页面的数据。
数据权限:则是控制你可以看到哪些数据,比如会员的人A只能看到或者修改A部创建的数据,他看不到或者不能修改B部的数据。

我这边实现是限定一个一个常量列表和一个权限菜单。可以是增删改查的,我这边考虑到这个变化有限,所以只在代码中写成了固定的内容,增加一项菜单就改回代码就行了。

// 常量定义
define('P_USER_MANAGE', 1); // 后台用户管理
define('P_USER', 2); // 用户管理列表
define('P_GROUP', 3); // 组管理列表
... ...

// 权限树
P_USER_MANAGE => [
            'title' => '后台管理',
            'link' => '',
            'class' => 'fa fa-user',
            'child' => [
                P_USER => [
                    'title' => '用户列表',
                    'link' => '/user/list',
                    'class' => '',
                    'child' => array()
                ],
                P_GROUP => [
                    'title' => '组列表',
                    'link' => '/group/list',
                    'class' => '',
                    'child' => array()
                ]
             ]
]

这样 每次用户进入页面 再根据一定的算法获得用户 应该有的权限,相关代码可以参考:
https://github.com/netbird/permission

这个是以前实现的一个类,是将权限由低到高排列,保存的时候每4位转变成1个十六进制的字符,然后合并。
例如: 用户权限是 '', 把权限 12加上,就是1000 0000 0000, 保存就是,800。
主要实现了以下方法:

Permission::setuserPermission($permission_index, $user_perm)
    // 这个是设置权限,比如用户的现在权限是'', 如果把12这个权限号码加上则执行:
    // $user_permission = Permission::setuserPermission(12, $user_permission)

Permission::deleteUserPermission($permission_index, $user_perm)
    // 删除权限
Permission::isAllowUserPermission($permission_index, $user_perm)
     // 判断用户是否有权限

至于按钮权限,这个也可以参照这个进行,定义一个按钮的设置常量,判断有那个权限,则展示按钮。
数据访问,这个具体场景 具体分析了,不过每个接口入口的地方 至少需要判定访问权限。
 

响应式模版

技术讲究厚积薄发,只有平时多积累多实践,关键的时候才能发挥用途。因为以前做了类似的系统,所以利用了以下一个模版框架。然后进行改造。
 
我不愿意每次都写表格页面,所以使用jquery table这个第三方的插件,采用异步加载数据的方式。在这个插件上面进行了简单的二次开发。后来的大多数功能都是采用的这个插件。
 
bootstrap是个好东西,以前用的少,这次大规模使用,每天就是调页面,调前端。在笔记本上看着不错,到了显示器上就走样,很苦恼。不过我经手的页面普遍没啥问题。
 

功能改进

之前每次新建功能都得新建个表,然后写编辑页面,好烦啊。能不能搞个公用的机制,简单的引用就生成个简单的编辑页面。于是,我对编辑部分进行了封装改进。
先说下数据库:建造个公共表:主要包含 main_key, second_key, config_value,等内容,config_value是参照key-value格式数据库的value字段使用,当然 text类型最高65535个字节把,如果太大,会造成bug。

 
就是将关系型数据库作为key-value用了,一些加单的配置信息,经过json转成字符串然后保存下来。
 

自定义通用配置

有没有一种方案,能够不编写代码,仅靠后台点点就能满足配置? 于是先出设计方案,后加工。
一般的项目配置分成单页和列表的配置。可参照如下需求:

  1. 进入设置页面,创建1个item,类型分成单页和列表。
  2. 选择 编辑页面是否包含日期,发布权限的人员,可修改字段的人员,发布的文件名称等等吧。
  3. 单页面或者列表的单页面编辑页支持自定义字段编辑(可添加或减少字段)。

于是这个项目就产生了。虽然体验差了一点,但是基本满足需求,节约开发时间还是没有问题的。

模版

关于模版是在列表页面提出的,在自定义列表页,每次创建单页面的时候都需要重建数据很麻烦。于是改进了下,在配置页面有个模版设置页面,模版页面可以添加删除定义字段。这样在创建新页面的时候就可以继承模版了。
 

ini配置文件

关于生成的配置信息,我们还是采用了yaconf的php扩展插件,所以大部分配置以ini文件的形式推到线上。yaconf的用户可以参考线上说明。下面博客也总结了一些yaconf使用的坑。
一些使用Yaconf的经验分享

日志及备份

一开始记录mysql,每条都记录,后来发现不太好,需要的时候什么也查不出来。还是应该从产品的角度看这个日志吧。
这个模块一直在开发改进中。大致的方向是给使用者一个友好的查询界面,去查询相关的内容。
因为一些隐患,后来我加入了配置信息备份功能,每次配置更新的时候保存已有的线上这个文件配置,并且记录到数据库。然后做个后台,可以查看,比对配置并且上线。
这个地方引用了一个公共类库:https://github.com/chrisboulton/php-diff
 

vue

一次偶然的机会接触到vue基础知识,在系统的某些页面决定尝试以下,没有深入使用,只是引入了 vue.js。进行了一些页面处理,感觉确实方便了很多。当时只是看了vue的一些基础知识,有很多问题还停留在表面层,所以没有深入应用。

类似下面的代码,很舒服的感觉。

new Vue({
        el: '#app',
        data: {
            source_url:"",
            count_result_text: "",
            uid: ""
        },
        methods: {
            count_result: function () {
                var that = this;

                if (this.source_url == '') {
                    bootbox.alert('url不能为空');
                    return;
                }
                var param = {
                    "source_url": this.source_url,
                    "uid": this.uid
                };

                $.post('/api/counts', param, function(return_data){
                    that.count_result_text = return_data.s;
                }, 'json');
            }
        }
    });

Vue.js 组件与路由

ant design

年初,想对系统升级改造。增加几个模块,朋友推荐ant design。感觉挺好,使用各种入坑。


下面是一些问题的总结, 我在另外的两篇文章中提到的:
ant design form表单的时间处理
碰到一个ant design跨域问题

全新的开发模式,我很喜欢。

 
技术栈:react,ant-design,dva,Mock
关于dva,有个图,我想大家应该学习下:

本次学会使用了npm,webpack, mock.js, node.js等相关内容。
最后学以致用,技术永远为业务服务。这个是历史以来的道理。多运用,多思考。