实现文档历史

pull/39/head
Minho 2017-05-20 15:27:03 +08:00
parent 79a464352e
commit 056edb71fc
8 changed files with 243 additions and 9 deletions

View File

@ -18,6 +18,7 @@ type BaseController struct {
Member *models.Member
Option map[string]string
EnableAnonymous bool
EnableDocumentHistory bool
}
// Prepare 预处理.
@ -25,7 +26,7 @@ func (c *BaseController) Prepare (){
c.Data["SiteName"] = "MinDoc"
c.Data["Member"] = models.Member{}
c.EnableAnonymous = false
c.EnableDocumentHistory = false
if member,ok := c.GetSession(conf.LoginSessionName).(models.Member); ok && member.MemberId > 0{
c.Member = &member
@ -45,6 +46,9 @@ func (c *BaseController) Prepare (){
if strings.EqualFold(item.OptionName,"ENABLE_ANONYMOUS") && item.OptionValue == "true" {
c.EnableAnonymous = true
}
if strings.EqualFold(item.OptionName,"ENABLE_DOCUMENT_HISTORY") && item.OptionValue == "true" {
c.EnableDocumentHistory = true
}
}
}
}

View File

@ -21,6 +21,7 @@ import (
"github.com/lifei6671/godoc/conf"
"github.com/lifei6671/godoc/models"
"github.com/lifei6671/godoc/utils/wkhtmltopdf"
"github.com/lifei6671/godoc/utils"
)
//DocumentController struct.
@ -674,6 +675,18 @@ func (c *DocumentController) Content() {
beego.Info("%d|", version, doc.Version)
c.JsonResult(6005, "文档已被修改确定要覆盖吗?")
}
history := models.NewDocumentHistory()
history.DocumentId = doc_id
history.Content = doc.Content
history.Markdown = doc.Markdown
history.DocumentName = doc.DocumentName
history.ModifyAt = c.Member.MemberId
history.MemberId = doc.MemberId
history.ParentId = doc.ParentId
history.Version = time.Now().Unix()
history.Action = "modify"
history.ActionName = "修改文档"
if markdown == "" && content != "" {
doc.Markdown = content
} else {
@ -685,6 +698,13 @@ func (c *DocumentController) Content() {
beego.Error("InsertOrUpdate => ", err)
c.JsonResult(6006, "保存失败")
}
//如果启用了文档历史,则添加历史文档
if c.EnableDocumentHistory {
_,err = history.InsertOrUpdate()
if err != nil {
beego.Error("DocumentHistory InsertOrUpdate => ",err)
}
}
c.JsonResult(0, "ok", doc)
}
@ -868,6 +888,69 @@ func (c *DocumentController) Search() {
c.JsonResult(0,"ok",docs)
}
//文档历史列表.
func (c *DocumentController) History() {
c.Prepare()
identify := c.GetString("identify")
doc_id, err := c.GetInt("doc_id", 0)
pageIndex, _ := c.GetInt("page", 1)
book_id := 0
//如果是超级管理员则忽略权限判断
if c.Member.Role == conf.MemberSuperRole {
book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil {
beego.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足")
}
book_id = book.BookId
} else {
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver {
beego.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足")
}
book_id = bookResult.BookId
}
if doc_id <= 0 {
c.JsonResult(6001, "参数错误")
}
doc, err := models.NewDocument().Find(doc_id)
if err != nil {
beego.Error("Delete => ", err)
c.JsonResult(6003, "获取历史失败")
}
//如果文档所属项目错误
if doc.BookId != book_id {
c.JsonResult(6004, "参数错误")
}
historis,totalCount,err := models.NewDocumentHistory().FindToPager(doc_id,pageIndex,conf.PageSize)
if err != nil {
c.JsonResult(6005,"获取历史失败")
}
var data struct {
PageHtml string `json:"page_html"`
List []*models.DocumentHistorySimpleResult `json:"lists"`
}
data.List = historis
if totalCount > 0 {
html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount)
data.PageHtml = string(html)
}else {
data.PageHtml = ""
}
c.JsonResult(0,"ok",data)
}
//递归生成文档序列数组.
func RecursiveFun(parent_id int, prefix, dpath string, c *DocumentController, book *models.BookResult, docs []*models.Document, paths *list.List) {
for _, item := range docs {

View File

@ -98,6 +98,7 @@ func (m *Document) RecursiveDocument(doc_id int) error {
if doc, err := m.Find(doc_id); err == nil {
o.Delete(doc)
NewDocumentHistory().Clear(doc.DocumentId)
}
var docs []*Document

View File

@ -2,12 +2,15 @@ package models
import (
"time"
"github.com/lifei6671/godoc/conf"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/godoc/conf"
)
type DocumentHistory struct {
HistoryId int `orm:"column(history_id);pk;auto;unique" json:"history_id"`
Action string `orm:"column(action);size(255)" json:"action"`
ActionName string `orm:"column(action_name);size(255)" json:"action_name"`
DocumentId int `orm:"column(document_id);type(int);index" json:"doc_id"`
DocumentName string `orm:"column(document_name);size(500)" json:"doc_name"`
ParentId int `orm:"column(parent_id);type(int);index;default(0)" json:"parent_id"`
@ -19,6 +22,17 @@ type DocumentHistory struct {
Version int64 `orm:"type(bigint);column(version)" json:"version"`
}
type DocumentHistorySimpleResult struct {
HistoryId int `json:"history_id"`
ActionName string `json:"action_name"`
MemberId int `json:"member_id"`
Account string `json:"account"`
ModifyAt int `json:"modify_at"`
ModifyName string `json:"modify_name"`
ModifyTime time.Time `json:"modify_time"`
Version int64 `json:"version"`
}
// TableName 获取对应数据库表名.
func (m *DocumentHistory) TableName() string {
return "document_history"
@ -33,21 +47,83 @@ func (m *DocumentHistory) TableNameWithPrefix() string {
return conf.GetDatabasePrefix() + m.TableName()
}
func NewDocumentHistory() *DocumentHistory {
return &DocumentHistory{}
}
//清空指定文档的历史.
func (m *DocumentHistory) Clear(doc_id int) error {
o := orm.NewOrm()
func (m *DocumentHistory) FindToPager(doc_id,page_index,page_size int) (docs []*DocumentHistory,totalCount int,err error) {
_, err := o.Raw("DELETE md_document_history WHERE document_id = ?", doc_id).Exec()
return err
}
//删除历史.
func (m *DocumentHistory) Delete(history_id int) error {
o := orm.NewOrm()
_, err := o.Raw("DELETE md_document_history WHERE history_id = ?", history_id).Exec()
return err
}
//恢复指定历史的文档.
func (m *DocumentHistory) Restore(history_id int) error {
o := orm.NewOrm()
err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id", history_id).One(m)
if err != nil {
return err
}
doc, err := NewDocument().Find(m.DocumentId)
if err != nil {
return err
}
doc.DocumentName = m.DocumentName
doc.Content = m.Content
doc.Markdown = m.Markdown
doc.Release = m.Content
_, err = o.Update(doc)
return err
}
func (m *DocumentHistory) InsertOrUpdate() (history *DocumentHistory,err error) {
o := orm.NewOrm()
history = m
if m.HistoryId > 0 {
_,err = o.Update(m)
}else{
_,err = o.Insert(m)
}
return
}
//分页查询指定文档的历史.
func (m *DocumentHistory) FindToPager(doc_id, page_index, page_size int) (docs []*DocumentHistorySimpleResult, totalCount int, err error) {
o := orm.NewOrm()
offset := (page_index - 1) * page_size
totalCount = 0
_,err = o.QueryTable(m.TableNameWithPrefix()).Filter("document_id",doc_id).Offset(offset).Limit(page_size).All(docs)
sql := `SELECT history.*,m1.account,m2.account as ModifyName
FROM md_document_history AS history
LEFT JOIN md_members AS m1 ON history.member_id = m1.member_id
LEFT JOIN md_members AS m2 ON history.member_id = m2.member_id
WHERE history.document_id = ? ORDER BY history.history_id DESC LIMIT ?,?;`
_, err = o.Raw(sql,doc_id,offset,page_size).QueryRows(&docs)
if err != nil {
return
}
var count int64
count,err = o.QueryTable(m.TableNameWithPrefix()).Filter("document_id",doc_id).Count()
count, err = o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", doc_id).Count()
if err != nil {
return
@ -55,4 +131,4 @@ func (m *DocumentHistory) FindToPager(doc_id,page_index,page_size int) (docs []*
totalCount = int(count)
return
}
}

View File

@ -57,6 +57,7 @@ func init() {
beego.Router("/api/:key/create",&controllers.DocumentController{},"post:Create")
beego.Router("/api/:key/delete", &controllers.DocumentController{},"post:Delete")
beego.Router("/api/:key/content/?:id",&controllers.DocumentController{},"*:Content")
beego.Router("/api/history", &controllers.DocumentController{},"get:History")
beego.Router("/docs/:key", &controllers.DocumentController{},"*:Index")

View File

@ -199,3 +199,31 @@ function showSuccess($msg,$id) {
$($id).addClass("success-message").removeClass("error-message").text($msg);
return true;
}
$(function () {
$("#documentHistoryModal").on("shown.bs.modal",function () {
var historyVue = new Vue({
el : "#documentHistoryModal",
data : {
lists : []
},
delimiters : ['${','}'],
methods : {
}
});
$.ajax({
url : window.historyURL,
data : { "identify" : window.book.identify,"doc_id" : window.selectNode.id },
dataType :"json",
success : function (res) {
if(res.errcode === 0){
historyVue.lists = res.data.lists;
}else{
alert(res.message);
}
}
});
});
});

View File

@ -61,7 +61,7 @@ $(function () {
if(name === "attachment"){
$("#uploadAttachModal").modal("show");
}else if(name === "history"){
$("#documentHistoryModal").modal("show");
}else if(name === "save"){
saveDocument(false);

View File

@ -17,6 +17,7 @@
window.editURL = "{{urlfor "DocumentController.Content" ":key" .Model.Identify ":id" ""}}";
window.releaseURL = "{{urlfor "BookController.Release" ":key" .Model.Identify}}";
window.sortURL = "{{urlfor "BookController.SaveSort" ":key" .Model.Identify}}";
window.historyURL = "{{urlfor "DocumentController.History"}}";
</script>
<!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -80,7 +81,7 @@
<div class="editormd-group pull-right">
<a href="javascript:;" data-toggle="tooltip" data-title="关闭实时预览"><i class="fa fa-eye-slash first" name="watch" unselectable="on"></i></a>
{{/*<a href="javascript:;" data-toggle="tooltip" data-title="修改历史"><i class="fa fa-history item" name="history" aria-hidden="true"></i></a>*/}}
<a href="javascript:;" data-toggle="tooltip" data-title="修改历史"><i class="fa fa-history item" name="history" aria-hidden="true"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="边栏"><i class="fa fa-columns item" aria-hidden="true" name="sidebar"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="使用帮助"><i class="fa fa-question-circle-o last" aria-hidden="true" name="help"></i></a>
</div>
@ -205,6 +206,46 @@
</form>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="documentHistoryModal" tabindex="-1" role="dialog" aria-labelledby="documentHistoryModalModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">文档历史记录</h4>
</div>
<div class="modal-body text-center">
<template v-if="lists.length <= 0">
暂无数据
</template>
<template v-else>
<table class="table">
<thead>
<tr>
<th>#</th><th>名称</th><th>修改时间</th><th>修改人</th><th>版本</th><th>操作</th>
</tr>
</thead>
<tbody>
<template v-for="item in lists">
<tr>
<td>${item.history_id}</td>
<td>${item.action_name}</td>
<td>${item.modify_time}</td>
<td>${item.modify_name}</td>
<td>${item.version}</td>
<td></td>
</tr>
</template>
</tbody>
</table>
</template>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<script src="{{cdnjs "/static/jquery/1.12.4/jquery.min.js"}}"></script>
<script src="{{cdnjs "/static/vuejs/vue.min.js"}}" type="text/javascript"></script>
@ -212,7 +253,7 @@
<script src="{{cdnjs "/static/webuploader/webuploader.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/jstree/3.3.4/jstree.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/editor.md/editormd.js"}}" type="text/javascript"></script>
<script type="text/javascript" src="{{cdnjs "/static/layer/layer.js"}}"></script>
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script>
<script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script>
<script type="text/javascript">
window.vueApp = new Vue({