2014年10月14日 星期二

项目管理

一、解决难题 1、项目进度失控,人员压力过大 2、代码共享困难,项目集成复杂 3、项目合作困难,人员交流不足 4、知识共享困难,学习成本过高 二、目标 1、目标可视,进度有序 2、每日迭代,提升成就 3、团队协作,减少压力

2014年10月09日 星期四

golang 实现导出带复杂样式图片的Excel

导出二维数据excel,其实很简单,使用cvs就可以了。但是如果导出格式复杂带样式还带图片的怎么办?客户的要求有时就是这么变态。呵呵。如果使用.net,微软提供的有库,使用php好像也有现成的有库。我大致对这些库进行了解,都可以实现我们的需求。可惜我现在使用的golang没有那么库支持,所以只好裸搞了。 Excel格式分为两种,第一种是封闭式,不知道使用啥格式,比如office 2003使用的格式,扩展名为*.xsl。另一种是开放式的,使用的是Open XML技术,比如office 2007 以后的版本,好在现在已经2014了,7年过去,大部分人都已经用到office 2012,即使国产的wps也早已经完美支持Open XML 了。 所以不用考虑兼容问题了。 这次我的解决方案就是从Open XML 入手。通过对Open XML学习,解决思路大致可分为3步骤: 第一步:使用支持Open XML 的软件,比如 office 2010制作一个我们想要的Excel,保存扩展名名为xlsx。 第二步:把xlsx修改扩展名为 zip,解压后使用占位符,修改里面相应的XML文件,然后压缩,再把扩展名修改为xlsx。这个压缩文件我们可称为导出模板。 第三步:使用程序动态解压,替换占位符,再压缩。其中对图片的处理,是把图片保存到相应,千言万语,不如代码更加直接,实现代码如下:
func (c Order) Excel(pageIndex int, pageSize int, sortField string, sortOrder string, customId int64, state string, orderTime string) revel.Result {
 sql := "select a.name A,b.name B,e.name C,d.name D,c.name E,a.order_time F,a.money G,a.state H,a.image I,a.width J,a.height K,a.area L,a.unit M,a.amount N,a.price O,f.alias P,a.remarks Q from ad_order a,ad_custom b,ad_product c,ad_stuff d,ad_stuff_cat e,ad_user f where a.product_id=c.id and c.stuff_id=d.id and d.cat_id=e.id and a.custom_id=b.id and a.user_id=f.id and a.del_state='未删' %s %s %s order by a.id desc"
 sql = fmt.Sprintf(sql, fmt.Sprintf("and a.custom_id=%d", customId), "%s", "%s")
 if state != "" {
 sql = fmt.Sprintf(sql, fmt.Sprintf("and a.state='%s'", state), "%s")
 } else {
 sql = fmt.Sprintf(sql, "", "%s")
 }
 if orderTime != "" {
 sql = fmt.Sprintf(sql, fmt.Sprintf("and a.order_time='%s'", orderTime))
 } else {
 sql = fmt.Sprintf(sql, "")
 }
 orders, err := Orm.Query(sql)
 if err != nil {
 return c.RenderJson(models.Message{State: "failure", Msg: err.Error()})
 }
 rows := make([]Rows, 0)
 col := 17
 for i := 1; i <= len(orders); i++ {
 row := Rows{}
 row.RowId = i + 1
 for k := 0; k < col; k++ {
 row.Columns = append(row.Columns, Columns{R: fmt.Sprintf("%s%d", string(ABC[k]), i+1), V: i*col + k})
 }
 rows = append(rows, row)
 }
 vmlDrawings := make([]VmlDrawing, 0)
 sharedStrings := make([]string, 0)
 sharedStrings = append(sharedStrings, "订单名称")
 sharedStrings = append(sharedStrings, "客户名称")
 sharedStrings = append(sharedStrings, "产品目录")
 sharedStrings = append(sharedStrings, "产品材料")
 sharedStrings = append(sharedStrings, "产品名称")
 sharedStrings = append(sharedStrings, "订单日期")
 sharedStrings = append(sharedStrings, "金额")
 sharedStrings = append(sharedStrings, "收费状态")
 sharedStrings = append(sharedStrings, "产品图片")
 sharedStrings = append(sharedStrings, "宽度(米)")
 sharedStrings = append(sharedStrings, "高度(米)")
 sharedStrings = append(sharedStrings, "面积")
 sharedStrings = append(sharedStrings, "单位")
 sharedStrings = append(sharedStrings, "数量")
 sharedStrings = append(sharedStrings, "单价")
 sharedStrings = append(sharedStrings, "经手人")
 sharedStrings = append(sharedStrings, "备注")
 for i, row := range orders {
 if string(row["I"]) != "" {
 img := string(row["I"])
 vmlDrawing := VmlDrawing{}
 vmlDrawing.Index = i
 vmlDrawing.Id = img[:strings.Index(img, ".")]
 vmlDrawing.Name = fmt.Sprintf("S%s", img)
 vmlDrawing.RowBegin = i + 1
 vmlDrawing.RowEnd = i + 2
 vmlDrawings = append(vmlDrawings, vmlDrawing)
 }
 sharedStrings = append(sharedStrings, string(row["A"])) //订单名称
 sharedStrings = append(sharedStrings, string(row["B"])) //客户名称
 sharedStrings = append(sharedStrings, string(row["C"])) //产品目录
 sharedStrings = append(sharedStrings, string(row["D"])) //产品材料
 sharedStrings = append(sharedStrings, string(row["E"])) //产品名称
 sharedStrings = append(sharedStrings, string(row["F"])) //订单日期

 f1, _ := strconv.ParseFloat(string(row["G"]), 32)
 sharedStrings = append(sharedStrings, fmt.Sprintf("%.2f", f1)) //金额
 sharedStrings = append(sharedStrings, string(row["H"])) //收费状态
 sharedStrings = append(sharedStrings, "") //产品图片

 f2, _ := strconv.ParseFloat(string(row["J"]), 32)
 sharedStrings = append(sharedStrings, fmt.Sprintf("%.2f", f2)) //宽度(米)

 f3, _ := strconv.ParseFloat(string(row["K"]), 32)
 sharedStrings = append(sharedStrings, fmt.Sprintf("%.2f", f3)) //高度(米)

 f4, _ := strconv.ParseFloat(string(row["L"]), 32)
 sharedStrings = append(sharedStrings, fmt.Sprintf("%.4f", f4)) //面积
 sharedStrings = append(sharedStrings, string(row["M"])) //单位
 sharedStrings = append(sharedStrings, string(row["N"])) //数量

 f5, _ := strconv.ParseFloat(string(row["O"]), 32)
 sharedStrings = append(sharedStrings, fmt.Sprintf("%.2f", f5)) //单价
 sharedStrings = append(sharedStrings, string(row["P"])) //经手人
 sharedStrings = append(sharedStrings, string(row["Q"])) //备注
 }
 basePath := revel.BasePath
 basePathPrefix := fpath.Join(basePath, fpath.FromSlash("app/templates"))

 file, _ := os.Create(fpath.Join(basePathPrefix, fpath.FromSlash(fmt.Sprintf("%s.xlsx", "orders"))))
 w := zip.NewWriter(file)
 defer w.Close()
 r, _ := zip.OpenReader(fpath.Join(basePathPrefix, fpath.FromSlash(fmt.Sprintf("%s.xlsx", "order"))))
 defer r.Close()
 for _, f := range r.File {
 switch f.Name {
 case "xl/worksheets/sheet1.xml":
 buf := new(bytes.Buffer)
 rc, _ := f.Open()
 data, _ := ioutil.ReadAll(rc)
 rc.Close()
 tmpl, _ := template.New("sheet").Parse(string(data))
 tmpl.Execute(buf, rows)
 ff, _ := w.Create(f.Name)
 ff.Write(buf.Bytes())
 break
 case "xl/sharedStrings.xml":
 buf := new(bytes.Buffer)
 rc, _ := f.Open()
 data, _ := ioutil.ReadAll(rc)
 rc.Close()
 tmpl, _ := template.New("sharedStrings").Parse(string(data))
 tmpl.Execute(buf, sharedStrings)
 ff, _ := w.Create(f.Name)
 ff.Write(buf.Bytes())
 break
 case "xl/drawings/_rels/vmlDrawing1.vml.rels":
 buf := new(bytes.Buffer)
 rc, _ := f.Open()
 data, _ := ioutil.ReadAll(rc)
 rc.Close()
 tmpl, _ := template.New("vmlDrawing1.vml").Parse(string(data))
 tmpl.Execute(buf, vmlDrawings)
 ff, _ := w.Create(f.Name)
 ff.Write(buf.Bytes())
 break
 case "xl/drawings/vmlDrawing1.vml":
 buf := new(bytes.Buffer)
 rc, _ := f.Open()
 data, _ := ioutil.ReadAll(rc)
 rc.Close()
 tmpl, _ := template.New("vmlDrawing1").Parse(string(data))
 tmpl.Execute(buf, vmlDrawings)
 ff, _ := w.Create(f.Name)
 ff.Write(buf.Bytes())
 basePath := revel.BasePath
 basePathPrefix := fpath.Join(basePath, fpath.FromSlash("public/upload"))
 for _, v := range vmlDrawings {
 fsmall := fpath.Join(basePathPrefix, fpath.FromSlash(v.Name))
 file, _ := os.Open(fsmall)
 data, _ := ioutil.ReadAll(file)
 ff, _ := w.Create(fmt.Sprintf("xl/media/%s", v.Name))
 ff.Write(data)
 }
 break
 default:
 ff, _ := w.Create(f.Name)
 rc, _ := f.Open()
 data, _ := ioutil.ReadAll(rc)
 ff.Write(data)
 rc.Close()
 }
 }
 return c.RenderFile(file, revel.Attachment)
}

2014年09月28日 星期天

golang build -tags 作用

目前可以想到的两个作用是:
  1. 编译的时候动态给变量赋值,比如版本号 昨天在查询怎么生成一个小体积的golang程序的时候,无意中发现这个文章. 对于固定的代码,及固定的golang版本,下面的命令总是得到一模一样的程序
    go build
    有时候需要为每个编译都打上标记,不然真的很乱啊 演示用的golang代码
    package main
    
    var _VERSION_ = "unknown"
    
    func main() {
        print("http_su ver=" + _VERSION_ + "\n")
    }
    编译时,加入需要的版本号信息,而不是直接去改main.go的源码
    export TAG=v1.b.50
    go build -ldflags "-X main._VERSION_ '$TAG'"
    运行结果:
    > go build
    > ./demo
    http_su ver=unknown
    > export TAG=v.1.b.50
    > go build -ldflags "-X main._VERSION_ '$TAG'"
    > ./demo
    http_su ver=v.1.b.50
    可以看到, 版本号根据编译参数的变化而变化了. 关键点是, 必须是 $package.$varName 本demo在linux/macos/windows/arm下测试通过.
  2. 编译的时候忽略文件,比如忽略dubug 文件 比如,项目中需要在测试环境输出Debug信息,一般通过一个变量(或常量)来控制是测试环境还是生产环境,比如:if DEBUG {},这样在生产环境每次也会进行这样的判断。在golang-nuts邮件列表中有人问过这样的问题,貌似没有讨论出更好的方法(想要跟C中条件编译一样的效果)。下面我们采用Build constraints来实现。
    1)文件列表:main.go logger_debug.go logger_product.go 2)在main.go中简单的调用Debug()方法。 3)在logger_product.go中的Debug()是空实现,但是在文件开始加上// + build !debug 4)在logger_debug.go中的Debug()是需要输出的调试信息,同时在文件开始加上// + build debug
    这样,在测试环境编译的时传递-tags参数:go build/install -tags “debug” logger。生产环境:go build/install logger就行了。 对于生产环境,不传递-tags时,为什么会编译logger_product.go呢?因为在go/build/build.go中的match方法中有这么一句:
    1 if strings.HasPrefix(name, "!") { // negation
    2     return len(name) > 1 && !ctxt.match(name[1:])
    3 }
    也就是说,只要有!(不能只是!),tag不在BuildTags中时,总是会编译。 完整源码在github

2014年09月25日 星期四

Bash远程代码执行漏洞预警

一、概述 2014年9月24日bash被公布存在远程代码执行漏洞,漏洞会影响目前主流的操作系统平台,包括但不限于redhat、CentOS、ubuntu等平台,此漏洞目前虽然有部分系统给出了补丁,但因为漏洞修补的时效性,及漏洞的范围之大,且还存在一些没有发布补丁的问题,所以仍被定义为高危漏洞。 bash引自维基百科的描述为:"bash,Unix shell的一种。1989年发布第一个正式版本,原先是计划用在GNU操作系统上,但能运行于大多数类Unix系统的操作系统之上,包括Linux与Mac OS X v10.4都将它作为默认shell。它也被移植到Microsoft Windows上的Cygwin与MinGW,或是可以在MS-DOS上使用的DJGPP项目。在Novell NetWare与Andriod在上也有移植。" 二、漏洞CVE编号 CVE-2014-6271 三、发布时间 NVD发布时间:2014年9月24日 14时48分04秒 四、部分主要漏洞影响平台及版本
操作系统 版本 解决方案
Red Hat Enterprise Linux 4 (ELS) Red Hat Enterprise Linux 4 Extended Lifecycle Support - bash-3.0-27.el4.2
5 Red Hat Enterprise Linux 5 - bash-3.2-33.el5.1 Red Hat Enterprise Linux 5.6 Long Life - bash-3.2-24.el5_6.1 Red Hat Enterprise Linux 5.9 Extended Update Support - bash-3.2-32.el5_9.2
6 Red Hat Enterprise Linux 6 - bash-4.1.2-15.el6_5.1 Red Hat Enterprise Linux 6.2 Advanced Update Support - bash-4.1.2-9.el6_2.1 Red Hat Enterprise Linux 6.4 Extended Update Support - bash-4.1.2-15.el6_4.1
7 Red Hat Enterprise Linux 7 - bash-4.2.45-5.el7_0.2
CentOS 5 bash-3.2-33.el5.1
6 bash-4.1.2-15.el6_5.1
7 bash-4.2.45-5.el7_0.2
Ubuntu 10.04 bash 4.1-2ubuntu3.1
12.04 bash 4.2-2ubuntu2.2
14.04 bash 4.3-7ubuntu1.1
  五、漏洞原理 目前的bash使用的环境变量是通过函数名称来调用的,以“(){”开头通过环境变量来定义的。漏洞的出现是因为Bash在执行完成函数定义之后并未退出,而是继续解析并执行shell命令。 六、漏洞可能会带来的影响
  1. 此漏洞可以绕过ForceCommand在sshd中的配置,从而执行任意命令。
  2. 如果CGI脚本用bash或subshell编写,则使用mod_cgi或mod_cgid的Apache服务器会受到影响。
  3. DHCP客户端调用shell脚本来配置系统,可能存在允许任意命令执行,尤其作为根命令的形式,在DHCP客户端的机器上运行。
  4. 各种daemon和SUID/privileged的程序都可能执行shell脚本,通过用户设置或影响环境变数值,允许任意命令运行。
七、 参考链接:

 

2014年09月22日 星期一

埋下的酒

十几年前,大概还在上初中的时候吧,心血来潮在自己院子里埋了一瓶白酒,扬言等结婚的时候喝.真到结婚的时候去挖,可是没挖到,这是多么遗憾的一件事啊.难道要等待儿子结婚的时候喝?

2014年08月29日 星期五

golang 字符串 转各时区时间

时间有世界标准时间和时区时间.要小心处理. 通过 time.Parse方法,获取的是UTC时间.一般不使用这个方法.而应该使用time.ParseInLocation.其中里面有一个Localtion参数,通过下面方法可以得到本地localtion.
loc, _ := time.LoadLocation("Local")
Local代表本机器区域,里面的更多参数可以查看$GOROOT/lib/time/timeinfo.zip文件. 下面是实例代码:
package main

import (
 "fmt"
 "time"
)

func main() {
 st := "2006-01-02 08:00"
 fmt.Println(st[5 : len(st)-1-5])
 time3, _ := time.Parse("2006-01-02 15:04", st)
 loc, _ := time.LoadLocation("Local")
 time2, _ := time.ParseInLocation("2006-01-02 15:04", st, loc)
 fmt.Println(time3)
 fmt.Println(time2)
}

2014年08月25日 星期一

关于男人

有时候我会欺瞒我自己 或者迷失在无谓的欢愉游戏中 有天我老去 在个陌生的地方 还要回味昨日冒险的旅程 其实我也经常讨厌我自己 或者我怪罪我生存的时代 努力的找理由 解释男人的驿动 也常常一个人躲藏起来 我听说男人是用土做的 身子里少了块骨头 他们用脑子来思考 有颗漂移的心 你知道男人是大一点的孩子 永远都管不了自己 张着眼睛来说谎 也心慌的哭泣 面对着不言不语的脸孔 谁也不知道男人是怎么了 慢慢的旅程路途还遥远 偶尔也怀疑自己是否该向前 欲望的门已开 梦的草原没有尽头 梦里忧郁的花香漂浮在风中 你知道男人是用土做的 掉眼泪就溶化一些 所以是残缺的躯体 没有绝对完美。。。 没有玩具的孩子最落寞 可是没有梦的男人是什么 欲望的门已开 梦的草原没有尽头 风里有些雨丝沾上了眼眸 告别的汽笛声轻轻的又响起了 生命的列车滑过了你心田 Wine,WomanandWar是男人永远的最爱 我只想静静的躺在你身边 慢慢的旅程终点在哪里

2014年08月21日 星期四

坐月子随想

本以为结完婚就可以一心一意搞事业,没想到结婚后繁事琐事跟多。稍不留心,就会出现这没关心到,那没照顾照,各种抱怨接踵而来。 对待照顾儿子上,我,老婆,岳母,都存在分歧。虽然我也没什么经验,但是我会上网搜索,会对信息的筛选,懂得平衡,而她们只凭经验,一旦出问题就极端化处理。热了,一点东西都不盖,冷了,包的像个粽子。任何的建议,就会反驳说我又不是医生。 儿子在那趟的好好的,虽然没睡着,但是也没哭,岳母老喜欢抱他,用胳膊不断的抖,逗他,才一二十天的婴儿,看不请,听不懂,你逗他有用吗?这难道就是关心吗?就是爱吗?喜欢就会纵容,爱就会克制。对待儿子也应该这样,不能凭自己的感觉行事。 争宠这种事,不但发生我妈跟我岳母直接,而且也发生在我和岳母之间。儿子才生下了几天,我妈也在,我岳母就处处不让我妈照顾宝宝,说我妈照顾不好宝宝,后来还在热和冷上产生了分歧。后来我妈走了。 平时上班,周六周天我说晚上我陪老婆照顾儿子,让岳母休息在另一间房子休息。而岳母大半夜有把我赶回另一个房间睡了,她来照顾。我搞不懂,这完全为了你好,而她却认为我照顾不好。 老婆做月子,可以不干活,但至少你自己在房间里走动走动,恢复一下身体,学习一下照顾儿子的方法,等岳母走了也可以懂得怎么照顾儿子,免得以后着急。做月子一半是恢复体力,一半不就学习照顾儿子吗?这是一个过度期。她说手腕疼,我也问我同学老婆了,说她也疼,等过完月子就好了。这也就只能等了,她却说我不关心她。 房间里很拥挤,儿子,老婆在床上睡着,岳母坐在床尾,玩斗地主,电脑风扇声,吱吱的刺耳。我在房间里,坐没坐处,站没站处,只好到另一个房间了。这无意间就把我排除在外,好像我不关心儿子似的。岳母老说她为了两个小孩操碎了心,而她不懂的,什么事该自己做,什么事该小孩做。一味的把什么都拦在自己身上,给自己增加了负担,也没把小孩教育好。

2014年08月19日 星期二

董卓性格的老板

大家都知道董卓生性多疑,带有少数民族的蛮横无理,刚愎自用,心狠手辣。最后被吕布一刀毙命。董卓性格的老板不太少数,这样的老板多少都会有一点才能。对下属鄙视,对客户漠视。跟随这样的老板,得处处小心,严谨甚微,听不得下属的反对意见。即使按照这样的老板做事,他那种不信任,不可一世的作风,也让人心不甘,情不愿。 这样的老板做不大公司,在创业初期可能,凭借自己的才能取得一定成就,但是公司一大,就会暴露很多管理上的问题。 如果你感觉更老板性格合不来,而你又没有决心改变性格,不妨换一家公司。没有不合适的人,只有不合适的岗位。即使你是关羽,在董卓手下干也只有起义的份了。

2014年08月13日 星期三

没有完美的设计

无论你做什么,最终都会以一款无法令你满意的软件而告终。正如 Hunt and Thomas在《The Pragmatic Programmer》中写道的:
“完美的软件是不存在的。在计算机历史上,没人设计出完美的软件。这是不可能的,也许你是第一个,除非你不接受这个事实,而浪费时间或者精力去追逐一个不可能实现的梦想。”
我很抱歉,但他们是对的。不要去追逐一个不可能实现的梦想,试着让你的软件“足够好”就好!