diff --git a/cache/cache.go b/cache/cache.go
new file mode 100644
index 00000000..968a8932
--- /dev/null
+++ b/cache/cache.go
@@ -0,0 +1,46 @@
+package cache
+
+import (
+	"github.com/astaxie/beego/cache"
+	"time"
+)
+
+var bm cache.Cache
+
+func Get(key string) interface{} {
+
+	return bm.Get(key)
+}
+
+func GetMulti(keys []string) []interface{} {
+
+	return bm.GetMulti(keys)
+}
+
+func Put(key string, val interface{}, timeout time.Duration) error {
+
+	return bm.Put(key, val, timeout)
+}
+func Delete(key string) error {
+	return bm.Delete(key)
+}
+func Incr(key string) error {
+	return bm.Incr(key)
+}
+func Decr(key string) error {
+	return bm.Decr(key)
+}
+func IsExist(key string) bool {
+	return bm.IsExist(key)
+}
+func ClearAll() error{
+	return bm.ClearAll()
+}
+
+func StartAndGC(config string) error {
+	return bm.StartAndGC(config)
+}
+//初始化缓存
+func Init(c cache.Cache)  {
+	bm = c
+}
\ No newline at end of file
diff --git a/cache/cache_null.go b/cache/cache_null.go
new file mode 100644
index 00000000..7abf55f0
--- /dev/null
+++ b/cache/cache_null.go
@@ -0,0 +1,39 @@
+package cache
+
+import "time"
+
+type NullCache struct {
+
+}
+
+
+func (bm *NullCache)Get(key string) interface{} {
+	return nil
+}
+
+func (bm *NullCache)GetMulti(keys []string) []interface{} {
+	return nil
+}
+
+func (bm *NullCache)Put(key string, val interface{}, timeout time.Duration) error {
+	return nil
+}
+func (bm *NullCache)Delete(key string) error {
+	return nil
+}
+func (bm *NullCache)Incr(key string) error {
+	return nil
+}
+func (bm *NullCache)Decr(key string) error {
+	return nil
+}
+func (bm *NullCache)IsExist(key string) bool {
+	return false
+}
+func (bm *NullCache)ClearAll() error{
+	return nil
+}
+
+func (bm *NullCache)StartAndGC(config string) error {
+	return nil
+}
diff --git a/commands/command.go b/commands/command.go
index 2fcbc4b8..f3bc772c 100644
--- a/commands/command.go
+++ b/commands/command.go
@@ -20,10 +20,15 @@ import (
 	"github.com/lifei6671/mindoc/conf"
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
+	"github.com/lifei6671/mindoc/cache"
+	beegoCache "github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/memcache"
+	_ "github.com/astaxie/beego/cache/redis"
 )
 
 // RegisterDataBase 注册数据库
 func RegisterDataBase() {
+	beego.Info("正在初始化数据库配置.")
 	adapter := beego.AppConfig.String("db_adapter")
 
 	if adapter == "mysql" {
@@ -37,13 +42,18 @@ func RegisterDataBase() {
 
 		dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=%s", username, password, host, port, database, url.QueryEscape(timezone))
 
-		orm.RegisterDataBase("default", "mysql", dataSource)
+		err := orm.RegisterDataBase("default", "mysql", dataSource)
+		if err != nil {
+			beego.Error("注册默认数据库失败:",err)
+			os.Exit(1)
+		}
 
 		location, err := time.LoadLocation(timezone)
 		if err == nil {
 			orm.DefaultTimeLoc = location
 		} else {
-			log.Fatalln(err)
+			beego.Error("加载时区配置信息失败,请检查是否存在ZONEINFO环境变量:",err)
+			os.Exit(1)
 		}
 	} else if adapter == "sqlite3" {
 		database := beego.AppConfig.String("db_database")
@@ -54,8 +64,17 @@ func RegisterDataBase() {
 		dbPath := filepath.Dir(database)
 		os.MkdirAll(dbPath, 0777)
 
-		orm.RegisterDataBase("default", "sqlite3", database)
+		err := orm.RegisterDataBase("default", "sqlite3", database)
+
+		if err != nil {
+			beego.Error("注册默认数据库失败:",err)
+			os.Exit(1)
+		}
+	}else{
+		beego.Error("不支持的数据库类型.")
+		os.Exit(1)
 	}
+	beego.Info("数据库初始化完成.")
 }
 
 // RegisterModel 注册Model
@@ -120,7 +139,7 @@ func RegisterCommand() {
 		migrate.RunMigration()
 	}
 }
-
+//注册模板函数
 func RegisterFunction() {
 	beego.AddFuncMap("config", models.GetOptionValue)
 
@@ -179,6 +198,7 @@ func RegisterFunction() {
 	})
 }
 
+//解析命令
 func ResolveCommand(args []string) {
 	flagSet := flag.NewFlagSet("MinDoc command: ", flag.ExitOnError)
 	flagSet.StringVar(&conf.ConfigurationFile, "config", "", "MinDoc configuration file.")
@@ -226,10 +246,95 @@ func ResolveCommand(args []string) {
 	gocaptcha.ReadFonts(filepath.Join(conf.WorkingDirectory, "static", "fonts"), ".ttf")
 
 	RegisterDataBase()
+	RegisterCache()
 	RegisterModel()
 	RegisterLogger(conf.LogFile)
 }
 
+//注册缓存管道
+func RegisterCache()  {
+	isOpenCache := beego.AppConfig.DefaultBool("cache",false)
+	if !isOpenCache {
+		cache.Init(&cache.NullCache{})
+	}
+	beego.Info("正常初始化缓存配置.")
+	cacheProvider := beego.AppConfig.String("cache_provider")
+	if cacheProvider == "file" {
+		cacheFilePath := beego.AppConfig.DefaultString("cache_file_path","./runtime/")
+		if strings.HasPrefix(cacheFilePath, "./") {
+			cacheFilePath = filepath.Join(conf.WorkingDirectory, string(cacheFilePath[1:]))
+		}
+		fileCache := beegoCache.NewFileCache()
+		beegoCache.FileCachePath = cacheFilePath
+		beegoCache.FileCacheDirectoryLevel = beego.AppConfig.DefaultInt("cache_file_dir_level",2)
+		beegoCache.FileCacheEmbedExpiry = time.Duration(beego.AppConfig.DefaultInt64("cache_file_expiry",120))
+		beegoCache.FileCacheFileSuffix = beego.AppConfig.DefaultString("cache_file_suffix",".bin")
+		fileCache.StartAndGC("")
+
+		cache.Init(fileCache)
+	}else if cacheProvider == "memory" {
+		cacheInterval := beego.AppConfig.DefaultInt("cache_memory_interval",60)
+		memory := beegoCache.NewMemoryCache()
+		beegoCache.DefaultEvery = cacheInterval
+		cache.Init(memory)
+	}else if cacheProvider == "redis"{
+		var redisConfig struct{
+			Conn string `json:"conn"`
+			Password string `json:"password"`
+			DbNum int `json:"dbNum"`
+		}
+		redisConfig.DbNum = 0
+		redisConfig.Conn = beego.AppConfig.DefaultString("cache_redis_host","")
+		if pwd := beego.AppConfig.DefaultString("cache_redis_password","");pwd != "" {
+			redisConfig.Password = pwd
+		}
+		if dbNum := beego.AppConfig.DefaultInt("cache_redis_db",0); dbNum > 0 {
+			redisConfig.DbNum = dbNum
+		}
+
+		bc,err := json.Marshal(&redisConfig)
+		if err != nil {
+			beego.Error("初始化Redis缓存失败:",err)
+			os.Exit(1)
+		}
+		beego.Info(string(bc))
+		redisCache,err := beegoCache.NewCache("redis",string(bc))
+
+		if err != nil {
+			beego.Error("初始化Redis缓存失败:",err)
+			os.Exit(1)
+		}
+
+		cache.Init(redisCache)
+	}else if cacheProvider == "memcache" {
+
+		var memcacheConfig struct{
+			Conn string `json:"conn"`
+		}
+		memcacheConfig.Conn = beego.AppConfig.DefaultString("cache_memcache_host","")
+
+		bc,err := json.Marshal(&memcacheConfig)
+		if err != nil {
+			beego.Error("初始化Redis缓存失败:",err)
+			os.Exit(1)
+		}
+		memcache,err := beegoCache.NewCache("memcache",string(bc))
+
+		if err != nil {
+			beego.Error("初始化Memcache缓存失败:",err)
+			os.Exit(1)
+		}
+
+		cache.Init(memcache)
+
+	}else {
+		cache.Init(&cache.NullCache{})
+		beego.Warn("不支持的缓存管道,缓存将禁用.")
+		return
+	}
+	beego.Info("缓存初始化完成.")
+}
+
 func init() {
 
 	if configPath ,err := filepath.Abs(conf.ConfigurationFile); err == nil {
diff --git a/conf/app.conf.example b/conf/app.conf.example
index 0f9626cd..94d95567 100644
--- a/conf/app.conf.example
+++ b/conf/app.conf.example
@@ -105,7 +105,29 @@ ldap_user_role=2
 ldap_filter=objectClass=posixAccount
 
 
-
+######################缓存配置###############################
+#是否开启缓存,true 开启/false 不开启
+cache=false
+#缓存方式:memory/memcache/redis/file
+cache_provider=memory
+#当配置缓存方式为memory时,内存回收时间,单位是秒
+cache_memory_interval=120
+#当缓存方式配置为file时,缓存的储存目录
+cache_file_path=./runtime/
+#缓存文件后缀
+cache_file_suffix=
+#文件缓存目录层级
+cache_file_dir_level=2
+#文件缓存的默认过期时间
+cache_file_expiry=3600
+#memcache缓存服务器地址
+cache_memcache_host=127.0.0.1:11211
+#redis服务器地址
+cache_redis_host=127.0.0.1:6379
+#redis数据库索引
+cache_redis_db=0
+#redis服务器密码
+cache_redis_password=
 
 
 
diff --git a/controllers/document.go b/controllers/document.go
index 08988a1c..a8cfdd96 100644
--- a/controllers/document.go
+++ b/controllers/document.go
@@ -834,23 +834,23 @@ func (c *DocumentController) Content() {
 
 	c.JsonResult(0, "ok", doc)
 }
-
-func (c *DocumentController) GetDocumentById(id string) (doc *models.Document, err error) {
-	doc = models.NewDocument()
-	if doc_id, err := strconv.Atoi(id); err == nil {
-		doc, err = doc.Find(doc_id)
-		if err != nil {
-			return nil, err
-		}
-	} else {
-		doc, err = doc.FindByFieldFirst("identify", id)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return doc, nil
-}
+//
+//func (c *DocumentController) GetDocumentById(id string) (doc *models.Document, err error) {
+//	doc = models.NewDocument()
+//	if doc_id, err := strconv.Atoi(id); err == nil {
+//		doc, err = doc.Find(doc_id)
+//		if err != nil {
+//			return nil, err
+//		}
+//	} else {
+//		doc, err = doc.FindByFieldFirst("identify", id)
+//		if err != nil {
+//			return nil, err
+//		}
+//	}
+//
+//	return doc, nil
+//}
 
 // 导出
 func (c *DocumentController) Export() {
diff --git a/models/document.go b/models/document.go
index 2b3f035f..1f351d09 100644
--- a/models/document.go
+++ b/models/document.go
@@ -2,7 +2,6 @@ package models
 
 import (
 	"time"
-
 	"bytes"
 	"fmt"
 	"github.com/astaxie/beego"
@@ -13,6 +12,9 @@ import (
 	"path/filepath"
 	"strconv"
 	"github.com/PuerkitoBio/goquery"
+	"github.com/lifei6671/mindoc/cache"
+	"encoding/json"
+	"qiniupkg.com/x/errors.v7"
 )
 
 // Document struct.
@@ -63,6 +65,9 @@ func (m *Document) Find(id int) (*Document, error) {
 	if id <= 0 {
 		return m, ErrInvalidParameter
 	}
+	if m,err := m.FromCacheById(id); err == nil {
+		return m,nil
+	}
 	o := orm.NewOrm()
 
 	err := o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", id).One(m)
@@ -70,55 +75,85 @@ func (m *Document) Find(id int) (*Document, error) {
 	if err == orm.ErrNoRows {
 		return m, ErrDataNotExist
 	}
+	m.PutToCache()
 	return m, nil
 }
 
 //插入和更新文档.
 func (m *Document) InsertOrUpdate(cols ...string) error {
 	o := orm.NewOrm()
-
+	var err error
 	if m.DocumentId > 0 {
-		_, err := o.Update(m)
-		return err
+		_, err = o.Update(m)
 	} else {
-		_, err := o.Insert(m)
+		_, err = o.Insert(m)
 		NewBook().ResetDocumentNumber(m.BookId)
+	}
+	if err != nil {
 		return err
 	}
+
+	m.PutToCache()
+
+	return nil
 }
 
 //根据指定字段查询一条文档.
 func (m *Document) FindByFieldFirst(field string, v interface{}) (*Document, error) {
+
+	if field == "identify" {
+		if identify,ok := v.(string);ok {
+			if m,err := m.FromCacheByIdentify(identify);err == nil{
+				return m,nil
+			}
+		}
+	}else if field == "document_id" {
+		if id,ok := v.(int); ok && id > 0 {
+			if m,err := m.FromCacheById(id);err == nil{
+				return m,nil
+			}
+		}
+	}
+
 	o := orm.NewOrm()
 
 	err := o.QueryTable(m.TableNameWithPrefix()).Filter(field, v).One(m)
 
+	if err == nil {
+		m.PutToCache()
+	}
+
 	return m, err
 }
 
 //递归删除一个文档.
-func (m *Document) RecursiveDocument(doc_id int) error {
+func (m *Document) RecursiveDocument(docId int) error {
 
 	o := orm.NewOrm()
 
-	if doc, err := m.Find(doc_id); err == nil {
+	if doc, err := m.Find(docId); err == nil {
 		o.Delete(doc)
 		NewDocumentHistory().Clear(doc.DocumentId)
 	}
+	//
+	//var docs []*Document
+	//
+	//_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("parent_id", doc_id).All(&docs)
 
-	var docs []*Document
-
-	_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("parent_id", doc_id).All(&docs)
+	var maps []orm.Params
 
+	_, err := o.Raw("SELECT document_id FROM " + m.TableNameWithPrefix() + " WHERE parent_id=" + strconv.Itoa(docId)).Values(&maps)
 	if err != nil {
 		beego.Error("RecursiveDocument => ", err)
 		return err
 	}
 
-	for _, item := range docs {
-		doc_id := item.DocumentId
-		o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", doc_id).Delete()
-		m.RecursiveDocument(doc_id)
+	for _, item := range maps {
+		if docId,ok := item["document_id"].(string); ok{
+			id,_ := strconv.Atoi(docId)
+			o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", id).Delete()
+			m.RecursiveDocument(id)
+		}
 	}
 
 	return nil
@@ -178,16 +213,54 @@ func (m *Document) ReleaseContent(bookId int) {
 		if err != nil {
 			beego.Error(fmt.Sprintf("发布失败 => %+v", item), err)
 		}else {
+			//当文档发布后,需要清除已缓存的转换文档和文档缓存
+			item.PutToCache()
 			os.RemoveAll(filepath.Join(conf.WorkingDirectory,"uploads","books",strconv.Itoa(bookId)))
 		}
 	}
 }
 
+//将文档写入缓存
+func (m *Document) PutToCache(){
+	if v,err := json.Marshal(m);err == nil {
+		if m.Identify == "" {
+			if err := cache.Put("Document.Id."+strconv.Itoa(m.DocumentId), v, time.Second*3600); err != nil {
+				beego.Info("文档缓存失败:", m)
+			}
+		}else{
+			if err := cache.Put("Document.Identify."+ m.Identify, v, time.Second*3600); err != nil {
+				beego.Info("文档缓存失败:", m)
+			}
+		}
+	}
+}
+//从缓存获取
+func (m *Document) FromCacheById(id int) (*Document,error) {
+	b := cache.Get("Document.Id." + strconv.Itoa(id))
+	if v,ok := b.([]byte); ok {
+		beego.Info("从缓存中获取文档信息成功")
+		if err := json.Unmarshal(v,m);err == nil{
+			return m,nil
+		}
+	}
+	return nil,errors.New("Cache not exists")
+}
+func (m *Document) FromCacheByIdentify(identify string) (*Document,error) {
+	b := cache.Get("Document.Identify." + identify)
+	if v,ok := b.([]byte); ok {
+		beego.Info("从缓存中获取文档信息成功")
+		if err := json.Unmarshal(v,m);err == nil{
+			return m,nil
+		}
+	}
+	return nil,errors.New("Cache not exists")
+}
+
 //根据项目ID查询文档列表.
-func (m *Document) FindListByBookId(book_id int) (docs []*Document, err error) {
+func (m *Document) FindListByBookId(bookId int) (docs []*Document, err error) {
 	o := orm.NewOrm()
 
-	_, err = o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).OrderBy("order_sort").All(&docs)
+	_, err = o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", bookId).OrderBy("order_sort").All(&docs)
 
 	return
 }