版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
OpenBook.me建设OpenBook.meMarch10,2013未完稿作者:、校对:于嘉、由开源书籍社区发 前关关于这本手记,我并不希望写成一本纯粹的技术手册或者某一门技术的专著,而是将我们在建设OpenBook过程中的所想、所做,一点一滴的记录下来而已。可能看起来很乱,有策划的东西,有写代码的东西,有日常的东西,甚至有系统运维的东西……看起来有点像大杂烩,但这些就是OpenBook.me建设过程中的点点滴缘OpenBook.me,可以说是我的一个理想的载体,她是我去实现理想的一个非常重要的平台。自己和开源有不解之缘。从09年就开始接触和使用开源技术,深受《教堂与市集》(作者:EricStevenRaymond就开始写开源程序。之后还有组织了开源团队——实易中国(后来更名“实易数码”),开发了当时风靡一时的“CTB文本”。后来工作太紧张,没有直接写开源软件了,但是仍然有参与开源软件测试、提交问题等,更甚至从06年底开始直接在自己笔记本电脑上使用FreeBSD系统做桌面工作,一直离不开开源。但是就个人力量而言,这些做起来贡献都是很微薄。那做点什么才更有意义呢?一直在想、一直在想……终于有一天突然想到,推广开源,比自己直接写开源软件更有意义。推广最有力的方法,莫过于写书。一个人写书,不如一群人写书。一群人写书,最好就是有一个公共创作的平台。于是行动起来,搭建一个公共创作平台openbook.me,让大家一起来做好这件有意义的事!开源书籍社区就是为了推广开源而设,但不仅仅限于开源技术。选自己做WEB开发都有10来年的时间了,学习过Java,C#,PHP,Lua,C,GolangPHPC。Lua2008但是到了2011年才着重学习,并且用Lua开发了日志分析系统。后来还有有结合Lua开发了进程服务、服务器引擎等等Golang是2012年年中才开始真正学习和使用。学习了一段时间后,才发现GolangGo在着手准备开发openbook.me时,其实是有些思想的。是用PHP呢,还是Golang呢,还是Lua呢?的观点是Golang太新不成熟,以后比较,他建议用PHP开发。而在我的心底里,开始抗拒PHP了。想找一个更轻快的语言来开发些东西,GolangLua。Lua最后还是下定决心采用Golang来开发openbook.me。其实我对于用Golang开发,也不熟悉,心里也很没底。反正就边做边学习吧——纠结不如开发openbook.me过程中,学习了很多新东西。而且这次是边设计,边开享,那么将整理好的文档放出去,也是——从自身开始。感首先感谢多姿多彩的开源世界,感谢所有为开源做贡献的人们。感谢、谢孟军等国内Go技术推广贡献者;感谢小米科技的、二毛、爱明等兄弟。感谢我们公司同事,感谢我们的客户、合作伙伴,感谢我们家人。前i11111222规.34.2452.4.1用户35设置个人信息....... 用户首页............. 导出模块.................... 导出为PDF文件...... 导出为epub文件........ 导出为mobile文件..... URL规划..................... 书籍相关URL........... 用户相关URL........... 3技术架83.1.2Web83.1.383.1.4NoSQL83.283.2.1为什么选择Golang93.2.2PHP93.2.3C9Lua的优缺 Golang的优缺 依赖 数据库的选 为什么选择 为什么选择 结 项 结构划 这样划分的好 书籍相关数据结构设 数据库规 部分用 用户表基本信息 用户个人信息 书籍信息 章节信息 缓存部分用 用户部分缓 收藏列 书籍部分缓 章节部分缓 开发框架设 概 设计思 MVC架 路由设 路由规则 执行路 Controller设 Model设 View设 应用方 常用方 发送邮 裁 功能模块开 总体计 用户功能模 用户用户登 用户设 书籍功能模 创建书 获取书籍列 查看书 文章功能模 6.4.1文 获取文章列 阅读文 其他功能模 编译、运行和测 概 编 环境准 编 使用goxc交叉编 运 环境配 服务配 运行服 测 小 日常 67附录A附 类似附录BMarkDown语法说 概 B.1.1兼容 特殊字符自动转 区块元 段落和换 标 区块 列 代码区 分隔 区段元 B.3.1强 代 81其 自动反斜 感 Markdown免费编辑 附录C相关资 1设1.1概这里谈及最初建设OpenBook.me的初衷,和需要考虑的方方面1.2设我们的设想,就是构建一个公共创作开源书籍的社区。供大家将自己的经验写出来,写成笔记,写成书籍。然后通过写作的方式,将自己的经验出去,这对开源技术的作用是巨大的。这个类似,用户可以自己创建书籍,也可以参与别人写的书籍,还可以fork出来写了再提交。可以发动有能力的人写写作客户端。创建公开和开源的书籍是免费的,如果要创建私人书籍则要日常运一个日常运营工作涉及方方面面,可大可小。大的需要几十人甚至几的团队来,小的只需要一个人就可以。而openbook.me的日常运营,是希望我们自己搭建一个平台,由大家来共同。我们只需要做好主要的工作,涉及如下几方面程序程序是计划分成几个步骤来逐步完善。刚开始时,先由自己完成基础功能,比如框架,用户登录、创建书籍、文章等。上线后再一步一步完善。未来的新东西,再由大家共同。服务器自然是需要运行在服务器上的,目前规模小,只需要使用自己的服务器就能运行起来。如果日后发展庞大了,需要服务器支撑时,就需要设立一个运维团队来负责了。现在,自然是就能搞定的了。服务器的最基本准则:需要保证系统稳定运行,保证服务正常推广西。好文章越来越多时,就能吸引用户。也需要通过一些子来帮忙推广,让人知道和使用。如果将来有必要,也可以投入做些网络推广。作者一个共同创作的平台,最的不是程序,而是在上面写作的作者。作者才是这个的,。需要与作者保持联系,有机会也安排些交流会等活动,节日时寄送些小等内容内容才是的。需要做好内容,需要靠大家如何营也许我们应该回避这个问题——做开源谈钱太俗了。但是一个更现实的问题摆对于任何一个的运营,都需要开支很大的成本。建设一个这样的开源书网,同样涉及到服务器、带宽,和推广等方面的成本。考虑营收,是为了能更好的为openbook.me的发展提供保障,才能将继续运营下去,为的作者、读者服务。因此需要考虑如何营收的问题——不用,能维持成本就好。另外在考虑网站的营收同时,需要做好共赢。让作者、读者、三者能互利互惠,都能做好有,有回报。如何共除了需要考虑自身的营收,也需要考虑到开源书籍作者的收入。参考收入及分成方式:1.分2.分3.分2规栏目导主体首页书籍关于用户用户名设置退点击“用户点击“”,进入创建书籍页点击“设置点 “退出”,退出登陆,然后回到个人首功能模块划用户功能11.用户22334455书籍功能11223344556677用户操作的书籍、章节的删除只是回收,并不是彻其他功能4.书籍模文章时,如果不选择书籍,则为笔记。书籍新建书新建书籍。界面部分主要体现如下内容:11.22.书籍的静态33.修改书修改书籍时,只能修改标题和介绍 不能修改11.书籍标题可写22.书籍URL33.书籍介绍可写删除书删除书籍时,只放到回收站,不能直接从数据库删除。文章文章相文文章时112233书籍(可选修改文修改文章的界面跟文章一样。修改时可以选择书籍删除文删除文章时,不会直接删除,只放到用户模用户部分,主要是前台会员部分。用户用户第一次,只需要填写最简单的信息1122.33.44.用户用户后,将给用户发生验证邮件。只有验证通过后才能登陆使用信箱和登陆。登陆后显示退出按钮,用户可以注销登录状态设置个人用户可以修改完善个人信息。11.23 和用户用户个人首页,显示如下信左侧信11.22.33.日44.55.6右侧展tab4.导出模供登陆用 PDFPDFepubepubmobilemobileURL规通过友好的URL,有利于用户以及SEO优化已经实现:1/books2/notes34/username/book/bookname5/username/book/bookname/16/username/note/178/create910/create/book?act=edit&id=?11/create/article?act=edit&id=?12/create/book?act=del&id=?13/create/article?act=del&id=?1/username个人首页,tab2/settings/profile3/register用户4 用户登5 用户退3技术架采用go语言开发,采用postgresql来数据,采用redis来缓存。采用wiki方式编辑书籍,采用[markdown][1]临时保存到redis;发布时保存到pgsql,同时生成html,可以导出PDF文件运行环操作我们很多产品和应用服务器都是采用FreeBSD,目前OpenBook也是运行FreeBSDGolangLinuxWindows就不考虑了。1Web虽然Golang可以直接运行Web服务器模式,不过还会涉及很多静态资源文件,比如js、等。这些交给Apache或者Nginx来完成会更好些。1WEBSERVER:数据库分别采用PostgreSQL来,使用Redis来缓存1Database:NoSQL1Cache:开发语Golang可以运行WEBserver服务模式或者FastCGI模式,可以灵活部署。量小时,可以直接部署为Web服务器模式,将来量大了,可以部署为FastCGI模式,面架设如Nginx这类高性能Web服务器来完成前端。我学习过多门语言,虽然都谈不上什么熟悉、精通,但是确实是有在用来写了些程序。之前用的最多的是PHPSCLua,也稍微接触过JavaC#Erlang的书看了两章,实在理解不了函数编程的精髓,放弃了。从2001年就开始用PHP,开发过多个产品。从04年开始用S,这个不适合做WEB程序开发,更适合在系统层做控制,这里就不深入讨论。从2005年开始用C语言,开发有系统、Nginx模块DNS服务等。从2011年开始学习和使用Lua,开发有日志分析数据挖掘系统,搭配C开发了进程、服务器监控等。其他的Java、C#虽然做过小项目,但是时间太短,没有深入使用。因此也最终决定采用Golang来完成开发。下面简单介绍下我熟悉的几门语言的优缺点。这里不是做各门编程语言的优缺点学术研究,仅仅是个人喜好结合自身工作、产品需要出发,没有刻意偏颇的意思。PHPPHP 跨平台,编写后可以到处运行,只要支持PHPPHPC、LuaGolangC C运行效率高CLuaLuaLuaWEB段。甚至有Nginx+Lua的组合。LuaCLua比较GolangGolangPHPWebServerGolang这些依赖包分PostgreSQL库database/sqlPostgreSQL MarkDownGolangMarkdown 第3.3节数据库的选 1Redis1
/alphazero/Go-配置文件 日志操作库 ORM 生成缩略图 /jonsen/simple-image-数据库的选说到数据库,大家反应就是MySQL,LAMP实在太深入人心了。不过近几年已经发生了变化。自从SUN把MySQL收购,而SUN又被Oracle收购后,MySQL就开始走上不归路了。先是把MySQL的创始团队走,后来更是做了很多限制开源社区的小动作。自然是抑制MySQL的发展,为Oracle自己的商业产品让路。MySQLMySQLMariaDB点,还做了很多的创新。但是,MySQL并不是开源数据库里唯一的选择。MySQLLAMPOpenBookPostgreSQL,单机因此在OpenBook中,我们还是毫不犹豫的选择了PostgreSQL早在2009年就开始使用Redis做统计的数据。采用C开发的模块数据,使用Redis来结项 结构划 1/- 源2 |main.go3 |-doc.go4 | 基础5 |-controllers6 | 数据操作层 |docs |publish9 | 缓10 | 配置文11 | 日志文12 | 静态文13 |templates14 | 临时文源码与文 、发 完全分 书籍相关prefacechapterappendixglossary 4数据结构数据库数据库部分,大致分成两块,一块是关系数据库,使用PostgreSQL;一块是缓存,使用Redis。4.2部分用目前这个版本只涉及最基本的功能,因此与之相关的数据表也很有限。根据前面规划的1.0版本,目前只涉及用户信息的两个表和书籍表、文章表。用户基本信息表只记录最基本的信息,最主要的作用是和登录字段1 用户自增2 名 信箱,用于登录,不456 时7 IP8 1CREATETABLEuser_base idintegerNOT namecharacter charactervarying(255), passwordcharacter urlcharacter addtime status9用户的详细个人信息表,是基本表的扩展,记录一些个人的详细信息。字段1userid用户2avatar3 4address567intro1CREATETABLEuser_profile useridintegerNOT avatarcharacter sex addresscharacter postcode mobile intro9书籍信书籍表记录书籍的信字段1id2userid3 书籍名4 英文别名,用于5 书籍分6 书籍7authour8 书籍介9version10license书籍11addtime12uptime13pubtime14status15 是否已16ispublish017isprivate是否私人书籍,需要1CREATETABLEbook_chapter idintegerDEFAULTnextval('book_chapter_id_seq'::regclass)NOT345678915
bookidintegerNOTNULL,useridintegerNOTNULL,titlecharactervarying(255),namecharactervarying(100),contenttext,tagstext,updatetext,addtimeinteger,modtimestatussmallintDEFAULT0,ispublishsmallintDEFAULT1,isdelsmallintDEFAULT0章节信字段1 章节2bookid书籍3userid4 章节标5 英文别名,用于6content7 章节8update9addtime10modtime11status12 是否已13ispublish01CREATETABLEbook_chapter idintegerDEFAULTnextval('book_chapter_id_seq'::regclass)NOT bookidintegerNOT useridintegerNOT titlecharacter namecharacter content tags update addtime modtime statussmallintDEFAULT ispublishsmallintDEFAULT isdelsmallintDEFAULT15缓存部分用为了减轻PostgreSQL数据库的压力,并且提高用户速度,将大部分数据都缓存在Redis里。SQLRedis用户部分用户登录、加载个人信息时URLURLIDID。这块主要用在浏览用户首页1key:2value:intMail根据用户信箱地址来获取用户 ID,这块主要用在用户登录时使用1key:2value:intIDIDkeyUser_basejson1key:2value:{json根据ID获取用户信这里记录的是user_base和user_profile两个表的数据。用于用户个人1key:2value:{json1key:2value:code用户被1key:2value:1key:2value:首页1key:2value:用户活1key:2value:收藏1key:书籍部分1key:2value:目前用在用户添加、修改文章时列出书籍列表时使用。 key: value:书籍信IDURL1key:2value:书籍1key:2value:书籍1key:2value:1key:2value:1key:2value1key:2value书籍次1key:2value:章节部分1key:2value:1key:2value章节1key:2value这部分需要思考下如1key:2value31key:2value:1key:2value:日志队1key:邮件队1key:5开发框架概这里主要介绍当前使用的框架的设计以及实现。因为刚使用Golang,还没有能设计思框架的目的就是将的东西聚合起来,简化项目开发。因此我们是本着简洁,够用的思路来实现整个框架。MVC架MVCWEBB/S我们也是采用MVC架构,不过我们设计时侧重点可能有视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并能接收用户的输入数据,但是它并不进行任何实际的业务处理。视图可以向模型查询业务状态,但不能改变模型。视图还能接受模型发出的数据更新事件,从而对用户界面进行同步更新。控制器是应用程序的主体部分。逻辑处理、控制实体数据在视图上展示、调用模型处理业务请求。模型是负责数据库操作,完成数据增删改和等。模型代表了业务数据和业务逻辑;由于同一个模型可以被多个视图重用,所以提高了应用的可重用性。 第5.4节路由设 路由设 //executemiddleware for_,filter:=rangep.filters filter(w, ifw.started 8 //Invoketherequest vc:= //callthecontrollerinit init:= in:=make([]reflect.Value, ct:=&Context{ResponseWriter:w,Request:r,Params: in[0]= in[1]= //callprepare in=make([]reflect.Value, method:= //ifresponsehaswritten,yesdon'trun if!w.started ifr.Method=="GET" method= }elseifr.Method=="POST" method= }elseifr.Method=="HEAD" method= }elseifr.Method=="DELETE" method= }elseifr.Method=="PUT" method= }elseifr.Method=="PATCH" method= }elseifr.Method=="OPTIONS" method=
if!w.startedifAppConfig.isAutoRendermethod=vc.MethodByName("Render")}if!w.startedmethod=vc.MethodByName("Finish")}}路由1params:=2fori,part:=rangeparts ifstrings.HasPrefix(part,":") expr:= //ausermaychoosetooverridethedefult //similartoexpressjs:‘/user/:id([0- ifindex:=strings.Index(part,"(");index!=-1 expr= part= params[j]= parts[i]= 1516ifj==0 //nowcreatethe t:= route:= route.pattern= route.controllerType= p.fixrouters=append(p.fixrouters,24}else{//addregexp //recreatetheurlpattern,withparameters //byregularexpressions.thencompilethe pattern=strings.Join(parts, regex,regexErr ifregexErr!=nil //nowcreatethe t:= route:= route.regex= route.params= 第5.5节Controller设 route.pattern= route.controllerType=43
p.routers=append(p.routers,执行Controller设1typeControllerstruct ChildName TplNames Session Validation //Datavalidation1012typeControllerInterfaceinterface Init(ct*Context,cn Render()2426func(c*Controller)Init(ctx*Context,cnstring) c.Data= c.Tpl=template.New(cn+ c.Tpl= c.Layout= c.TplNames= c.ChildName= c.Ctx= c.Session=GlobalSessions.SessionStart(c.Ctx.ResponseWriter, c.Validation=37Model设1funcOpenDb()(ormbeedb.Model,errerror) dsn:= driver:=4 ifdriver=="mysql" //7 }elseifdriver=="postgres" dsn=fmt.Sprintf("dbname=%shost=%suser=%spassword=%sport=%d AppConfig.dbName,AppConfig.dbHost,AppConfig.dbAuth, AppConfig.dbPort,AppConfig.dbSsl dsn="user=pgsqlpassword=dbname=postgres }elseifdriver=="3" // }else returnorm,errors.New("UnspportDatabase db,err:=sql.Open(driver,dsn iferr!=nil fmt.Println("Opendatabaseerror:",err returnorm, _,err=db.Exec("SETsearch_path="+AppConfig.dbPath+",public, iferr!=nil fmt.Println("Exedatabaseerror:",err returnorm, 40
beedb.OnDebug=AppConfig.dbDebugifdriver=="postgres"{orm=beedb.New(db,"pg"}elseorm=beedb.New(db}returnorm,44funcredisConnect()(rdredis.Client) spec:= ifAppConfig.redisHost!="" spec.Host(AppConfig.redisHost ifAppConfig.redisAuth!=""
spec.Password(AppConfig.redisAuth port:= ifport>0&&port<65535 spec.Port(port varerr rd,err= iferr!=nil Log.Errorf("ConnectRedis:%s",err 65
return67funcRedisSaveJson(expireint64,mpinterface{},erface{})(errerror) encoder,err:= iferr!=nil Log.Errorf("JsonMarshalerr%s",err key:=GetRedisKey(keys... err=Redis.Set(key,encoder iferr!=nil Log.Errorf("Redisseterr%s",err ifexpire>0 Redis.Expire(key,expire 86
return89funcRedisGetJson(erface{})(mpmap[string]interface{}) key:=GetRedisKey(keys... ifvalue,err:=Redis.Get(key);err==nil err:=json.Unmarshal(value,&mp iferr!=nil Log.Errorf("JsonUnmarshalerr%s",err 102104funcRedisSet(expireint64,valueinterface{},erface{})(errerror) vardata switchvalue.(type) case data=[]byte(GetIntStr(value.(int)) case data=[]byte(value.(string) case data= key:=GetRedisKey(keys... err=Redis.Set(key,data iferr!=nil Log.Errorf("Redisseterr%s",err ifexpire>0 Redis.Expire(key,expire return130132funcRedisGet(erface{})(value[]byte,errerror) key:=GetRedisKey(keys... returnRedis.Get(key135137funcRedisExists(erface{})(bool,error) key:=GetRedisKey(keys... returnRedis.Exists(key141144funcRedisIncr(erface{})(int64,error) key:=GetRedisKey(keys... returnRedis.Incr(key149151funcRedisDelete(erface{})(bool,error) key:=GetRedisKey(keys...returnRedis.Del(key}returnRedis.Del(key}ncGetRedisKey(keyskey=for_,k:=rangekeysswitchk.(type) case key+=":"+ case key+=":"+ 171View设 ifc.TplNames=="" ifc.Data["json"]!=nil //fmt.Println("outputwhit return }elseifc.Data["xml"]!=nil //fmt.Println("outputwhit return c.TplNames=c.ChildName+"/"+c.Ctx.Request.Method+"." //return t,err:=c.Tpl.ParseFiles(path.Join(AppConfig.tplPath,c.TplNames path.Join(AppConfig.tplPath,AppConfig.tplHeader path.Join(AppConfig.tplPath,AppConfig.tplFooter path.Join(AppConfig.tplPath,"userProfile.tpl" iferr!=nil Log.Tracef("templateParseFileserr:%s", _,file:= err=t.ExecuteTemplate(c.Ctx.ResponseWriter,file, iferr!=nil Log.Trace("templateExecuteerr:", 32func(c*Controller)SetTemplateHeader() userName:= ifuserName!=nil c.Data["Username"]= userUrl:= ifuserUrl!=nil c.Data["Userurl"]= 43
"]=46func(c*Controller)LoadTemplateBlock(tplFilestring)template.HTML t,err:=c.Tpl.ParseFiles(path.Join(AppConfig.tplPath,tplFile) iferr!=nil Log.Trace("templateParseFileserr:", return _,file:= newbytes:= t.ExecuteTemplate(newbytes,file, tplcontent,_:= return5759func(c*Controller)LoadMarkDownTemplate(tplFilestring)template.HTML t,err:=c.Tpl.ParseFiles(path.Join(AppConfig.tplPath,tplFile) iferr!=nil Log.Trace("templateParseFileserr:", return _,file:= newbytes:= t.ExecuteTemplate(newbytes,file, tplcontent,_:= returnMarkDown(string(tplcontent)72
//return74func(c*Controller)SetExportJson(message,urlstring,statusbool) ifmessage=="" json:= json["message"]= json["status"]= json["url"]=85
c.Data["json"]=87func(c*Controller)ShowMessage(title,alert,bodystring,erface{}) c.TplNames= msg:= msg["title"]= msg["alert"]= iflen(args)==0 msg["body"]= }elsemsg["body"]=msg["body"]=}c.Data["Message"]=}应用方常用1funcMd5(keystring)string h:= h.Write([]byte(key) returnhex.EncodeToString(h.Sum(nil)567funcGetStrUint(dstring)uint a,_:= return1013funcGetStrInt(dstring)int a,_:= return161819*将字符串转换成uint642021funcGetStrUint64(dstring)(uint64) ifd=="" return ret,err:=strconv.ParseUint(d,10,32 iferr!=nil //log.Println("getStrUint64err", 33
return35funcGetStrInt64(dstring)(int64) ifd=="" return ret,err:=strconv.ParseInt(d,10,32 iferr!=nil //log.Println("getStrUint64err", 47
return4950*将字符串转换成uint325152funcGetStrUint32(dstring)uint32 a,_:= return5558funcGetUint64Str(duint64)string returnstrconv.FormatUint(d,106062funcGetUintStr(duint)string returnstrconv.Itoa(int(d)6466funcGetIntStr(dint)string returnstrconv.Itoa(d68发送1varmailChan=make(chan23//运行一个goroutin发生邮件任4funcMailSendServer()5 mailChan=make(chan Log.Info("RunningMailSendServer..."7 for9 mail:=<- ifmail==nil
m:=NewMailMessage(mail.Subject,mail.Content,mail.To)iferr:=m.Send();err!=nil{Log.Errorf("sendmailto"+mail.To+"error%s",err}2224funcMailSender(subject,content,tostring)(errerror) ifsubject!=""&&content!=""&&to!="" ifAppConfig.smtpDaemon mailChan<-&Mailer{Subject:subject,Content:content,To:to }elsem:=NewMailMessage(subject,content,toiferr=m.Send();err!=nilLog.Errorf("sendmailto"+to+"error%s",errreturn}}return}41
returnerrors.New("inputis44funcNewMailMessage(subject,content,tostring)*MailMessage tos:=strings.Split(to,"," message:=&MailMessage{Subject:subject,Content: To:make([]mail.Address, fork,v:=rangetos message.To[k].Address= 55
//fmt.Println(message.To)returnmessage57funcNewMailMessageFrom(subject,content,from,tostring)*MailMessage message:=NewMailMessage(subject,content, message.From.Address=61
return63constcrlf=65typeMailMessagestruct//ifFrom.Address72
SubjectstringContent74//75//76func(self*MailMessage)String()string varbuf write:=func(whatstring,recipients[]mail.Address) iflen(recipients)==0 fori:=rangerecipients ifi==0 }elsebuf.WriteString(",}}
from:= iffrom.Address=="" from=&mail.Address{SiteName,AppConfig.smtpUser ifAppConfig.adminMail!="" self.Bcc=make([]mail.Address, self.Bcc[0]=mail.Address{"adminer",AppConfig.adminMail fmt.Fprintf(&buf,"From:%s%s",from.String(), write("To:", write("Cc:", write("Bcc:", fmt.Fprintf(&buf,"Date:%s%s",time.Now().UTC().Format(time.RFC822), fmt.Fprintf(&buf,"Subject:%s%s%s",self.Subject,crlf, return112114//Returnsthefirst115func(self*MailMessage)Validate()error iflen(self.To)==0 return recipient return120122typefakeAuthstruct 124126func(afakeAuth)Start(server*smtp.ServerInfo)(string,[]byte,error) server.TLS= return129132func(self*MailMessage)Send()error varauth iferr:=self.Validate();err!=nil return to:=make([]string, fori:=rangeself.Toto[i]to[i]=}from:=iffrom=="" from=AppConfig.smtpUser// addr:=fmt.Sprintf("%s:%d",AppConfig.smtpHost, ifAppConfig.smtpTLS auth=fakeAuth{smtp.PlainAuth("", AppConfig.smtpPassword, }else auth=smtp.PlainAuth("",AppConfig.smtpUser, returnsmtp.SendMail(addr,auth,from,to,1605.8.3裁裁剪部分又用到第库 /jonsen/simple-image-下面是具体的裁剪代1funcNewImageFile(filestring)(img*imageFile.ImageFile) returnimageFile.NewImageFile(file345funcImgInfo(i*imageFile.ImageFile) jsonbytes,_:= 8910funcImgCrop(i*imageFile.ImageFile,geo,outstring)(errerror) varg iferr:=g.Set(geo);err!=nil return 25
iflen(g)!=4Log.Error("-geoargumentsmalformed(usage:'x0y0x1//fmt.Println("-geoargumentsmalformed(usage:'x0y0x1y1')")returnerrors.New("-geoargumentsmalformed(usage:'x0y0x1y1')")}cropedImg:=i.Crop(g[0],g[1],g[2],g[3])fmt.Println(writeImage(cropedImg,out,i.Format))returnnil27funcImgResize(i*imageFile.ImageFile,geo,outstring) varg iferr:=g.Set(geo);err!=nil 39
iflen(g)!=2//fmt.Println("-geoargumentsmalformed(usage:'xy')")}resizedImage:=i.Resize(g[0],g[1])fmt.Println(writeImage(resizedImage,out,i.Format))41typegeometry43func(g*geometry)String()string return4547func(g*geometry)Set(valuestring)error for_,str:=rangestrings.Split(value,"") i,err:=strconv.ParseInt(str,10, iferr!=nil return *g=append(*g, return5659funcnewFile(pathstring)(file*os.File) file,err:= iferr!=nil 6567funcnewTmpFile()(tmpFile*os.File) tmpFile,err:=ioutil.TempFile("", iferr!=nil 7375funcwriteImage(imgimage.Image,out,formatstring)string varfile ifout=="" file= }else file= imageFile.Encode(file,img, return856功能模块总体计计划用一个月时间构建完毕1.0版本。只需要完成主体功能,能,能新建用户功能模用户用户时,最关键的是对数据做好校验,其次是要做邮件验证。下面代码前面部分主要是接收和判断数据,后面主要是写入数据库,并且判断写入成功或者失败。写入成功则发送验证邮件。12userMail:=3userUrl:=strings.ToLower(ctl.GetVar("userUrl")4userName:=5userPwd:=6userPwd2:=789ifuserName==""||len(userName)>100 ctl.SetExportJson用户名不正确false 121415ckMail:=ctl.Validation.16if!ckMail.Ok ctl.SetExportJson信箱地址不正确false 1921//验证个人地址是否合22if!ctl.Validation.FilterString(userUrl,"^[a-z0-9-]{5,32}$")25
ctl.SetExportJsonuserUrl5~32位false27//验证个人地址是否跟系统关键字28for_,v:=rangeprotectName ifv==userUrl ctl.SetExportJson("真抱歉,"+userUrl+"可能会和系统,请更换其他的吧。","",false 3335验证是否已经36modelUserInfo.GetUserInfoForReg(userMail,userUrl,userName,false37ifmodelUserInfo.Id>0 ifmodelUserInfo.Url==userUrl ctl.SetExportJson("地址"+userUrl+"已经被","",false }elseifmodelUserInfo.==userMail ctl.SetExportJson("电子邮箱"+userMail+"已经被","",false }elseifmodelUserInfo.Name==userName ctl.SetExportJson("会员名称"+userName+"已经被","",false ctl.SetExportJson("已经被","",false 5052//验证长53//if!web.FilterString(userPwd,"^[a-zA-Z0-9]{6,16}$")54ifuserPwd==""||len(userPwd)>32||len(userPwd)<6 ctl.SetExportJson("不正确。长度在6~32位","",false 5759ifuserPwd!=userPwd2 ctl.SetExportJson("前后不一致","",false 626465modelUserInfo.Name=66modelUserInfo.=67modelUserInfo.Password=web.Md5(userPwd68modelUserInfo.Url=69modelUserInfo.Addtime=7172err:=73iferr!=nil //recorderror ctl.SetExportJson("很抱歉,时发生意外错误,失败了。","",false76}else ctl.SetExportJson("成功了!加入!",userUrl,true //设置邮件校 activeCode:=web.Md5( +userMail+modelUserInfo.Password err:=web.RedisSet(0,activeCode,"user:inactive", iferr!=nil web.Log.Errorf("setupmail"+modelUserInfo.+",code"+activeCode "error%s",err ctl.Data["Name"]= ctl.Data["ActiveCodeAddr"]="http://"+web.Site+"/login/active/" userMail+"/"+ ctl.Data["SendDate"]=web.Date(time.Now(),"Y-m-d"98
mailContent:=string(ctl.LoadTemplateBlock("mailRegister.tpl")//发生验证邮web.MailSender(userName+",加入OpenBook.me,请先验证!",mailContent,userMail)用户1ckMail:= (userName).Message("请输入合法的信箱2if!ckMail.Ok ctl.SetExportJson(ckMail.Error.String(),"",false 567ckPass:=ctl.Validation.MinSize(userPass,6).Message("长度不能小于6位8if!ckPass.Ok ctl.SetExportJson(ckPass.Error.String(),"",false 1114varmodleUserInfo16modleUserInfo.GetUserInfoByMail(userName17ifmodleUserInfo.Id>0 //register ifmodleUserInfo.Status!=1 ctl.SetExportJson("请先通过验证再登录false ifmodleUserInfo.Password==web.Md5(userPass) //登录成功设置 //sess:= ctl.Session.Set("uid", ctl.Session.Set("uname", //ifurlisnullthanredirecttosetup ifmodleUserInfo.Url=="" }else ctl.Session.Set("uurl",
//ctl.Ctx.Redirect(302,"/"+userInfo.Url ctl.SetExportJson("登录成功modleUserInfo.Url,true }else ctl.SetExportJson("错误","",false 45}else //UserNot ctl.SetExportJson("登录信箱记错了吧?false 4951ctl.SetExportJson("登录失败false用户1func(ctl*SettingsController)Get() ctl.Data["SiteName"]=web.SiteName用户设置 ctl.Data["ActiveSetup"]=45 var userBase userProfile //action:= userInfo:=userBase.GetUserInfoForWeb(ctl.userid,"" ifuserInfo.Id<1 ctl.Ctx.NotFound("PageNotFound!" ctl.Data["UserInfo"]= switchctl.action case ctl.Data["ActiveAvatar"]= case ctl.Data["Activ"]= case ctl.Data["ActivePassword"]= //case //fmt.Println("On ctl.Data["ActiveProfile"]= userBase.GetUserInfoById(ctl.userid ctl.Data["UserBase"]=
userProfile.GetUserProfile(ctl.userid)ctl.Data["UserProfile"]=userProfile //ctl.Data["Username"]="Jonsen //web.SiteName=43
//fmt.Fprintf(ctl.Ctx.ResponseWriter,"llll:"+web.SiteName)ctl.TplNames="settings.tpl"46func(ctl*SettingsController)Post() ctl.Data["SiteName"]=web.SiteName用户设置 switchctl.action case ctl.Data["ActiveAvatar"]= case ctl.Data["Activ"]= case ctl.Data["ActivePassword"]= //case ctl.Data["ActiveProfile"]= ctl.SetExportJson设置错误咯ctl.actionfalse //ctl.TplNames= 7074func(ctl*SettingsController)setProfile() userName:= sex:= mobile:= address:= postcode:= intro:= ifuserName==""||len(userName)>100 ctl.SetExportJson用户名不正确false ifmobile!=""&&!ctl.Validation.FilterString(mobile,"^[0-9]{11}$") ctl.SetExportJson("号码不正确","",false ifpostcode!=""&&!ctl.Validation.FilterString(postcode,"^[0-9]{6}$") ctl.SetExportJson("不正确","",false var userBase,modelUserInfo userProfile userBase.GetUserInfoById(ctl.userid ifuserBase.Id<1 //ctl.ShowMsg很抱歉,保存错误,保存失败了。false ctl.SetExportJson很抱歉,保存错误,保存失败了。false //判断是否需要修改用户 ifuserName!=userBase.Name modelUserInfo.GetUserInfoForReg("null","null",userName,false ifmodelUserInfo.Id>0&&modelUserInfo.Id!=ctl.userid modelUserInfo.Name==userName ctl.SetExportJson("会员名称"+userName+"已经被,请不要重复","",false userBase.Name= userProfile.GetUserProfile(ctl.userid //save//userProfile.Userid=userProfile.Sex=userProfile.Mobile=userProfile.Address=userProfile.Postcode=userProfile.Intro=err:=iferr!=nil////recorderrorweb.Log.Error(err ctl.SetExportJson很抱歉,保存时发生意外错误,保存失败了。false }else ctl.SetExportJson保存成功!true 145147func(ctl*SettingsController)setAvatar() ctl.Ctx.Request.ParseMultipartForm(32<< geoString:=ctl.GetVar("geo" //fmt.Println("geoString",geoString,ctl.Input() //||!ctl.Validation.FilterString(geoString,"^[0-9]$" ifgeoString=="" ctl.SetExportJson("获取数据失败了。","",false file,handler,err:= iferr!=nil ctl.SetExportJson获取上传文件失败了。false defer f,err:=os.OpenFile("./tmp/"+handler.Filename,os.O_WRONLY|os.O_CREATE, iferr!=nil ctl.SetExportJson获取上传文件失败了。false defer io.Copy(f, deferos.Remove("./tmp/"+handler.Filename img:=web.NewImageFile("./tmp/"+handler.Filename ifimg.Format!="jpeg"&&img.Format!="png" ctl.SetExportJson("不支持的文件格式,只支持jpeg,png。","",false }ifimg.Size>512*1024ctl.SetExportJson("文件太大,只能支持最大512K。false}web.ImgInfo(imguname:=avatarPath:="upl
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 栀子豉汤治疗抑郁症的实验研究:疗效与机制探究
- 柴油发动机EGR冷却器:结构设计、性能优化与应用实践
- 查干湖:水质与微生物群落结构的交互关系解析
- 柔性电子结构与柔性传感器:从基础到前沿应用的深度剖析
- 染色体缺失综合征:分子细胞遗传学解析与表型定位探究
- 某市经济技术开发区城市管理信息系统:精准分析与创新设计
- 枸杞、茯苓、罗汉果:药用植物的成分剖析与生物活性探究
- 果园开沟施肥混肥回填装置的创新设计与应用研究
- 2023年上半年广东省统计师考试专业知识消费者行为模式考试试卷
- 2026云南红河州个旧市疾病预防控制中心(个旧市卫生监督所)合同制人员招聘3人备考题库及参考答案详解(研优卷)
- 氧气瓶安全培训知识
- 足球传球与跑位配合技巧:传跑结合破解对手防线
- 15D502 等电位联结安装
- 就业指导-简历制作课件
- NB/T 11108-2023选煤用起泡剂性能要求
- 子女抚养权协议书
- 情志养生的方法
- 2022年全国青少年人工智能创新挑战赛考试题库(含答案)
- (完整)抗菌药物培训试题库及答案
- 葫芦岛连石化工有限责任公司年产3.5万吨苯二胺项目环评报告
- 部编人教版二年级语文下册《寓言二则》精美课件
评论
0/150
提交评论