magic 发布的文章

这套广告管理系统可能用到5-8年了,很多操作效率非常低,系统逻辑性也存在一些问题,导致刚入职的新员工学习成本很高,准备给把这套系统重写,改善体验。

1.原系统截图

2.整理出系统所有栏目以及页面

3.为了操作便捷,引入了概况,桌面概念。

  1. 桌面:类似windows桌面,是所有总栏目的导航。
  2. 概况:最近的一些消息,类似于通知。比如广告客户达到客户效果,提醒去停掉广告。客户需要的留言今天未达到等等通知。


4.原来页面统计数据显示非常乱,一次性字段显示过多(精简),改为鼠标经过显示 。

5.替换添加填入ID,用搜索选择替换

6.系统查询、添加、列表操作、按钮统一位置,形象。

7.每条数据不超出2行,多出用鼠标经过显示,或点击看详情


8.简化操作按钮,系统全选反选改为一个按钮,搜索框可以输入多个值。


图片太多,分第二篇编写。

Scrum是敏捷过程中比较著名的一个过程框架,被很多团队采用。

Scrum使用迭代的开发方式,每一次迭代中,都会经历一个“计划->实施->验证->反思”的过程。这是一个开发过程,同时也是一个对项目的认识过程,这样的设计其实也是遵循了哲学的认知论.

何谓Backlog?

本文所说的Backlog是Scrum中的一个专用名词,大约意思是待办的工作事项。

那它是用来干什么的呢?

Backlog里面放的是需要实现的所有任务,包括功能性的和非功能性的任务,换句话说,就是咱们已经把客户的需求提炼出来并且已经完成了设计的部分,现在这些已经完成设计的用户需求被放在一个地方,持续添加新的进来并且随时可以分配出去进行开发,这个地方就叫做Backlog。


知道它是干什么了,那你知道为什么我们需要Backlog吗,不就是一个地方可以放需要去完成的任务吗?有那么重要吗?

首先,是的,它很重要。

怎么个重要法呢?

  1. 首先从Scrum的过程看,我们可以发现Backlog是其起源,俗话说,好的开始是成功的一半,其实Backlog也是同样的一个道理,因为它为敏捷开发的成功开了一个好头,有了Backlog,我们就随时可以知道我们需要做什么。
  1. 当然知道要做什么还不够,因为我们是在做产品,而不是只做一个功能,要做的东西太多了,就有可能会乱,最后甚至导致“不”敏捷了。

所以Backlog专门提供了优先级的功能,我们可以根据商业上的需要给每个待做任务设置优先级,而开发时就可以根据优先级来进行开发安排。

不过,设置优先级是一件很重要又很严肃的事情,因为一旦设置了优先级以后,意味着你的产品开发就会按照某个方向上进行了,一旦设置错误,可能会导致很坏的后果,比如某个功能你觉得优先级不高,不看重它,所以一开始不开发,但是竞争对手很快做出来了,然后占领了市场,这个时候你再去更改优先级就有点晚了。

所以对于优先级的设置,我们就会有很多的考虑,

1) 比如根据冒泡法来设置优先级,通过两两比较来确定两个任务的优先级别,然后再去跟第三个比较,再来确定,这样循环下去,得到所有任务的优先级别。

2)
当然冒泡法只是一个设置的优先级的方法,但是得出优先级的方式还是得根据产品的实际情况来决定,比如说优先考虑客户看重的功能,而对于我们自己想出来的优先级就低一点。

3) 再比如,可以根据工作量情况,先完成那些比较容易的,而对于改动很大的放到后面去。

4) 当然,还可以考虑市场投放顺序,根据我们自己想让客户看到的功能的顺序去决定优先级别,毕竟敏捷里是要定期交付给客户Build的。

所以Backlog里优先级的设置是一件相当重要的事情,只有正确的优先级,正确的开发与交付次序才有可能得到最高的产品价值。

3.
除了优先级外,还有一个设置也是非常重要的,就是对于每个任务,你需要做工作量预估,预估什么呢,预估该任务开发完成所需的时间和人力等,敏捷里把这个预估叫做Story
Point,故事点。

故事点这个概念现在争议很多,究竟以怎么样的方式来预估工作量呢?

(1) 有人说用小时,但是我们知道能力强的人跟能力弱的人所用的小时数必然是两样的,所以通过小时来得到故事点并且进而得到Velocity数据是不正确的。

(2) 也有人说按照困难度,但是困难度只能比较每个迭代中完成功能的困难程度,而无法去预判开发的速率趋势。

(3)
也有人说,与其花时间讨论这些,还不如直接分任务做了,这样才敏捷了。那么这样真的是敏捷吗?这样当然不是敏捷了,敏捷还需要一定的预判与分析,不然就不是敏捷,而叫做无序了。

那究竟何种方式能正确的呢?我是觉得还是按照各个公司的实际情况,比如,

(1)你们公司团队。而如果你们团队水平比较均衡,就可以时间,因为水平差不多,对于任何一个功能完成的时间是差不多的。

(2)当然,如果你们公司水平相差比较多,这个时候就可以用困难度了,就像跳水比赛一样,有很多标准的困难度。

假设我们现在已经选好了适合我们的方式,那怎么才能正确地得到这个故事点数的数值呢?

既然这个是估值,那我们就需要评估,谁来参与评估呢?答案是有直接负责这些功能的团队来负责估值,在估值中水平高的人和水平低的人需要进行一些讨论,在确定这个功能的数值。

而对于已经讨论产生的数值,开发人员就得承诺在讨论出来的时间内完成,因为既然是自己讨论出来的,当然能够按时完成,不然就太没面子了,而Backlog恰恰通过这个故事点估值来让大家产生按时按量完成的动力,所以很多时间自愿加班就变得很正常。

当然自愿加班是对估值的一种损害,所以下一次的估值需要对这种损害进行修复,也就是需要对之后的估值多更加仔细的考虑。

通过这种估值以及其不断修复而得到速率分析与预判是相对比较正确的。

介绍了上面三个Backlog的重要性,大家应该能感觉到在敏捷中,Backlog的确是非常重要的一个概念。下面就来具体介绍一下Backlog的几个分类:

由于不同的公司、不同的专家对Backlog的分类总是有点区别,所以我们就以业界最知名的敏捷开发工具TechExcel
DevSuite中的Backlog来举例子吧,

在DevSuite中, 你可以自定义把Backlog被分成几类, 如果按照最大分法的话,我们可以分成三类,一类叫做Product
Backlog,第二类称为Release Backlog,最后一类名为Sprint
Backlog,当然很多其它主流的分类只有第一种和第三种的分类,不过由于DevSuite这个是可以自定义让你自己根据实际需要启用几类的,所以今天还是以DevSuite的方式为准。

对于这三类分法,下面来解释一下:

  1. Product Backlog (产品待办事项)是条目化/量化的用户需求,它将需求文档中需要实际开发的需求条目化地表达出来。

在这个Backlog里,存放着所有已经设计完成需要完成的用户需求,当然只是需要完成,不需要指定时间与负责人,只要分门别类就行了,未来会通过产品地不同版本来一一去实现,就像微软的Windows系列那样,也许微软早就已经设计好Windows
2020的功能了,只是现在还不去开发,只是先放在Product Backlog里罢了。

另外,这个Backlog还可以保存之前准备做但是又被取消或者延迟的一些用户需求等等。

  1. Release Backlog是本次发布需要完成的任务

这里所谓的Release,是指一次大的发布,比如说微软的Windows
8发布。每次发布,我们必然有大量任务需要去完成,而这些任务,即使在敏捷中,也是会事先选择好这次发布需要完成哪些的,当然中间有变更,敏捷还是很欢迎的,但是我相信大部分应该没啥变化。

所以Release Backlog就保存了所有这个发布需要完成的工作,所以这个就意义重大。而且跟分配任务相关的估值、优先级的设置也是在这个地方完成的。

  1. Sprint Backlog是本次迭代需要完成的任务

Sprint Backlog是开发过程用得最多的Backlog,因为每次Release会建立大量的Sprint,而每个Sprint都有一个Sprint
Backlog。

在Release
Backlog中已经设置好了Story的优先级与故事点数,所以根据这两个的值,我们就会通过分解生成更多小的任务的方式去分配到当前Sprint中去完成,开发组长只需要在Sprint
Backlog中将任务根据员工的技术水平与可用时间进行合理分配就行了。

当分配的小任务无法在当前Sprint中完成的时候,可以根据需要在下个Sprint分配任务时分配到该Sprint中继续完成,当然估值方面就需要下次注意调整了。

名词解释:

Sprint:每一次迭代称为一个Sprint。

Backlog:其实就是需求列表。

SM:Scrum Master,Scrum过程的管理者。

PO:Product Owner,需求他说了算。

TEAM:架构师、开发人员、测试人员等。

Chicken:其他相关的人,如老总、客户。 ^_^

故事点:一个相对独立的功能点。

度量:在过程中的一些度量值。如当前系统的BUG数。

事件:过程中出现的一切事件。如小王今天早上在大家在全身心投入站会时时,放了一个屁……

角色

Scrum中只有三类角色:SM、PO、Team。

今天遇到了mysql 提示错误 doesn't have a default value.

点击(此处)折叠或打开



<h1>CDbException</h1>
<p>CDbCommand 无法执行 SQL 语句:SQLSTATE[HY000]:Generalerror:1364 Field'sms_percent_amount'doesn't have a default value.The SQL statement executed was:INSERT INTO t_dev(`mail`,`passwd`,`name`,`reg_ip`,`git_id`,`status`)VALUES(:mail,:passwd,:name,:reg_ip,:git_id,:status);.Bound with mail='safFD@jj.dk',passwd='e99a18c428cb38d5f260853678922e03',name='magic007',status=1,reg_ip='2130706433',git_id='2484137'(/Users/magic/Applications/web/cctv/yii/framework/db/CDbCommand.php:358)</p><pre>#0/Users/magic/Applications/web/cctv/cctv-web/www.cctv.cn/protected/models/User.php(329):CDbCommand->execute(Array)
#1/Users/magic/Applications/web/cctv/cctv-web/www.cctv.cn/protected/models/User.php(37):User->addRow(Array)
#2/Users/magic/Applications/web/cctv/cctv-web/www.cctv.cn/protected/controllers/SiteController.php(398):User->register(Array)
#3/Users/magic/Applications/web/cctv/yii/framework/web/actions/CInlineAction.php(49):SiteController->actionAuthRegister()
#4/Users/magic/Applications/web/cctv/yii/framework/web/CController.php(308):CInlineAction->runWithParams(Array)
#5/Users/magic/Applications/web/cctv/yii/framework/web/filters/CFilterChain.php(133):CController->runAction(Object(CInlineAction))
#6/Users/magic/Applications/web/cctv/yii/framework/web/filters/CFilter.php(40):CFilterChain->run()
#7/Users/magic/Applications/web/cctv/yii/framework/web/CController.php(1145):CFilter->filter(Object(CFilterChain))
#8/Users/magic/Applications/web/cctv/yii/framework/web/filters/CInlineFilter.php(58):CController->filterAccessControl(Object(CFilterChain))
#9/Users/magic/Applications/web/cctv/yii/framework/web/filters/CFilterChain.php(130):CInlineFilter->filter(Object(CFilterChain))
#10/Users/magic/Applications/web/cctv/yii/framework/web/CController.php(291):CFilterChain->run()
#11/Users/magic/Applications/web/cctv/yii/framework/web/CController.php(265):CController->runActionWithFilters(Object(CInlineAction),Array)
#12/Users/magic/Applications/web/cctv/yii/framework/web/CWebApplication.php(282):CController->run('AuthRegister')
#13/Users/magic/Applications/web/cctv/yii/framework/web/CWebApplication.php(141):CWebApplication->runController('site/AuthRegist...')
#14/Users/magic/Applications/web/cctv/yii/framework/base/CApplication.php(185):CWebApplication->processRequest()
#15/Users/magic/Applications/web/cctv/cctv-web/www.cctv.cn/index.php(24):CApplication->run()
#16{main}</pre>


查看目前mysql使用模式

mysql> select @@sql_mode;

| @@sql_mode |

| STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |

修改MySQL 模式
命令行修改 SET sql_mode = "NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";

如果需要重启后修改,请修改my.conf

sql_mode= NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

关于sql_mode 介绍
1. sql_mode模式
mysql数据库的中有一个环境变量sql_mode,定义了mysql应该支持的sql语法,数据校验等!我们可以通过以下方式查看当前数据库使用的sql_mode:

[java] [view
plain](http://blog.csdn.net/wulantian/article/details/8905573# "view plain")
copy

  1. [mysql]()> select @@sql_mode;
  2. +----------------------------------------------------------------+
  3. | @@sql_mode |
  4. +----------------------------------------------------------------+
  5. | STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
  6. +----------------------------------------------------------------+

或者通过查看系统变量方式:

[java] [view
plain](http://blog.csdn.net/wulantian/article/details/8905573# "view plain")
copy

  1. mysql> show variables like 'sql_mode%'\G;
  2. 1. row **
  3. Variable_name: sql_mode
  4. Value: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

mysql5.0以上版本支持三种sql_mode模式:ANSI、TRADITIONAL和STRICT_TRANS_TABLES。
ANSI模式:宽松模式,对插入数据进行校验,如果不符合定义类型或长度,对数据类型调整或截断保存,报warning警告。
TRADITIONAL模式:严格模式,当向mysql数据库插入数据时,进行数据的严格校验,保证错误数据不能插入,报error错误。用于事物时,会进行事物的回滚。
STRICT_TRANS_TABLES模式:严格模式,进行数据的严格校验,错误数据不能插入,报error错误。

1.2 ANSI模式

[java] [view
plain](http://blog.csdn.net/wulantian/article/details/8905573# "view plain")
copy

  1. mysql> set @@sql_mode=ANSI;
  2. Query OK, 0 rows affected (0.00 sec)
    1. mysql> create table test(name varchar(4), pass varchar(4));
  3. Query OK, 0 rows affected (0.03 sec)
    1. mysql> insert into test values('aaaaa','aaaaa'),('bbbb','bbbb');
  4. Query OK, 2 rows affected, 2 warnings (0.02 sec)
  5. Records: 2 Duplicates: 0 Warnings: 2
    1. mysql> show warnings;
  6. +---------+------+-------------------------------------------+
  7. | Level | Code | Message |
  8. +---------+------+-------------------------------------------+
  9. | Warning | 1265 | Data truncated for column 'name' at row 1 |
  10. | Warning | 1265 | Data truncated for column 'pass' at row 1 |
  11. +---------+------+-------------------------------------------+
  12. 2 rows in set (0.00 sec)
    1. mysql> select * from test;
  13. +------+------+
  14. | name | pass |
  15. +------+------+
  16. | aaaa | aaaa |
  17. | bbbb | bbbb |
  18. +------+------+
  19. 2 rows in set (0.00 sec)

我们可以看到,在ANSI模式下,当我们插入数据时,未满足列长度要求时,数据同样会插入成功,但是对超出列长度的字段进行截断,同时报告warning警告。

1.3 STRICT_TRANS_TABLES模式

[java] [view
plain](http://blog.csdn.net/wulantian/article/details/8905573# "view plain")
copy

  1. mysql> set @@sql_mode=STRICT_TRANS_TABLES;
  2. Query OK, 0 rows affected (0.00 sec)
    1. mysql> create table test(name varchar(4), pass varchar(4));
  3. Query OK, 0 rows affected (0.02 sec)
    1. mysql> insert into test values('aaaaa','aaaaa'),('bbbb','bbbb');
  4. ERROR 1406 (22001): Data too long for column 'name' at row 1
    1. mysql> show errors;
  5. +-------+------+------------------------------------------+
  6. | Level | Code | Message |
  7. +-------+------+------------------------------------------+
  8. | Error | 1406 | Data too long for column 'name' at row 1 |
  9. +-------+------+------------------------------------------+
  10. 1 row in set (0.00 sec)
    1. mysql> select * from test;
  11. Empty set (0.00 sec)

我们可以看到,在STRICT_TRANS_TABLES模式下,当我们插入数据时,mysql会严格的进行数据的校验,当发现插入列值未满足要求,直接报告error错误,保证了错误数据无法插入到数据库中。

1.3 TRADITIONAL模式

[java] [view
plain](http://blog.csdn.net/wulantian/article/details/8905573# "view plain")
copy

  1. mysql> set @@sql_mode=TRADITIONAL;
  2. Query OK, 0 rows affected (0.00 sec)
    1. mysql> create table test(name varchar(4), pass varchar(4));
  3. Query OK, 0 rows affected (0.02 sec)
    1. mysql> insert into test values('aaaaa','aaaaa'),('bbbb','bbbb');
  4. ERROR 1406 (22001): Data too long for column 'name' at row 1
    1. mysql> show errors;
  5. +-------+------+------------------------------------------+
  6. | Level | Code | Message |
  7. +-------+------+------------------------------------------+
  8. | Error | 1406 | Data too long for column 'name' at row 1 |
  9. +-------+------+------------------------------------------+
  10. 1 row in set (0.00 sec)
    1. mysql> select * from test;
  11. Empty set (0.00 sec)

TRADITIONAL模式与STRICT_TRANS_TABLES模式执行的结果,在这种情况下一致。

[java] [view
plain](http://blog.csdn.net/wulantian/article/details/8905573# "view plain")
copy

  1. mysql> select @@sql_mode\G;
  2. 1. row **
  3. @@sql_mode: STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,E
  4. RROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER
  5. 1 row in set (0.00 sec)

看一下TRADITIONAL模式,我们发现在TRADITIONAL模式下,对所有的事务存储引擎,非事务存储引擎检查,日期类型中的月和日部分不能包含0,不能有0这样的日期(0000-00-00),数据不能除0,禁止grant自动创建新用户等一些校验。

注意:我们这里设置的sql_mode都是session级别的。另外,可以直接修改my.ini文件,找到sql_mode,然后设置新的模式即可!

例如:

vi /etc/my.cnf

在[mysqld]下面添加如下列:

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES