現在來接上procedure,在database/main.go寫入
// get blog with owner super project and category data
func GetBlog(path string) (*BlogProj, error) {
blogData := &BlogProj{}
has, err := db.SQL("call get_blog(?)", path).Get(blogData)
if err != nil {
return nil, err
} else if !has {
return nil, nil
}
return blogData, nil
}
// Update an blog
func CreateBlog(oid, superid, blog, descript, typeid, superUrl string) error {
return checkAffect(db.Exec("call create_blog(?, ?, ?, ?, ?, ?)", oid, superid, blog, descript, typeid, superUrl))
}
// Update an owner if no need to update uniquename, newuniname should be ""
func UpdateBlog(oid, superid, newsuperid, bid, blog, newblog, descript, originUrl, newsuperUrl string) error {
return checkResult(db.Exec("call update_blog(?, ?, ?, ?, ?, ?, ?, ?, ?)", oid, superid, newsuperid, bid, blog, newblog, descript, originUrl, newsuperUrl))
}
// delete a blog
func DelBlog(oid, bid, url string) error {
return checkAffect(db.Exec("call del_blog(?, ?, ?)", oid, bid, url))
}
在serve/main.go我們還需要處理檔案,現在先假設使用者寫完文章會已檔案的形式上傳過來,假設從前端傳來的形式是multipart/form,在gin裡面有專門處理的function可以用,建立common packeage來放一些通用function,創建form.go寫入
package common
import (
"app/apperr"
"app/logger"
"errors"
"github.com/gin-gonic/gin"
"mime/multipart"
)
// check form key match or not
func checkParam(param []string, form *multipart.Form) string {
for _, v := range param {
if len(form.Value[v]) == 0 {
return v
}
}
return ""
}
func BindMultipartForm(c *gin.Context, param []string) (*multipart.Form, error) {
form, err := c.MultipartForm()
if err != nil {
log.Warn(c, apperr.ErrWrongArgument, err, "binding error of put multipart form", "binding error of put multipart form")
return nil, err
}
if v := checkParam(param, form); v != "" {
var errStr = "multi part form miss match key "+v
log.Warn(c, apperr.ErrWrongArgument, nil, errStr)
return nil, errors.New(errStr)
}
return form, nil
}
解釋:
先來處理寫檔,在util/file/file.go寫入
package file
import (
"html/template"
"io"
"io/ioutil"
"mime/multipart"
"os"
"path/filepath"
)
func Checkdir(dir string) error {
var err error
if _, err = os.Stat(dir); os.IsNotExist(err) {
err = os.Mkdir(dir, 0700)
}
return err
}
// SaveUploadedFile uploads the form file to specific dst.
func SaveFile(file *multipart.FileHeader, dir, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
srcByte, err := ioutil.ReadAll(src)
if err != nil {
return err
}
out, err := os.OpenFile(dir+"/"+dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer out.Close()
_, err = out.WriteString("{{define \"content\"}}\n")
if err != nil {
return err
}
_, err = out.Write(srcByte)
_, err = out.WriteString("{{end}}")
if err != nil {
return err
}
return err
}
解釋:
{{define "content"}}{{end}}
作為template現在來規劃一下檔案的路徑,我們把先建立owner id的資料夾,底下再建立blog id的資料夾,在blog id底下就可以放文件與圖片,先在config/app/app.yaml的serve/main補上
FilePath: path_of_user_file
然後到setting/setting.go的ServerStruct補上
FilePath string `yaml:"FilePath,omitempty"`
之後就能讀設定的檔案路徑了
在common package下創建file.go寫入
package common
import (
"app/apperr"
"app/log"
"app/setting"
"app/util/file"
"github.com/gin-gonic/gin"
"mime/multipart"
)
// write file and parse to html
func WriteFormFile(c *gin.Context, form *multipart.Form, fileName string) {
fileHeader := form.File["content"][0]
filePath := setting.Servers["main"].FilePath + "/" + form.Value["oid"][0] + "/" + fileName
// check exist and create
if err := file.Checkdir(filePath); err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "something error in write file", "something error in create folder")
}
if err := file.SaveFile(fileHeader, filePath, fileName+".md"); err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "something error in write file")
return
}
}
現在來寫服務,在serve/main.go寫入
var (
createBlogParam = []string{"oid", "superid", "descript", "blogType"}
blogType = map[string]string{"project": "1", "article": "2"}
)
func CreateBlog(c *gin.Context) {
// check form
form, err := common.BindMultipartForm(c, createBlogParam)
if err != nil {
return
}
if form.Value["blogType"][0] != "project" && len(form.File["content"]) == 0 {
log.Warn(c, apperr.ErrWrongArgument, nil, "multy part form miss match key content")
return
}
// create to database
superUrl, blog := splitWork(c.Request.URL.Path)
err = database.CreateBlog(form.Value["oid"][0], form.Value["superid"][0], blog, form.Value["descript"][0], blogType[form.Value["blogType"][0]], strings.Join(superUrl, "/"))
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error. please try again", "database error of update blog")
return
}
// get data
blogData, err := database.GetBlog(c.Request.URL.Path)
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error", "database error")
return
}
// write file
if form.Value["blogType"][0] == "article" {
common.WriteFormFile(c, form, strconv.Itoa(blogData.Bid))
}
c.Header("Location", "/.")
c.Status(http.StatusCreated)
}
解釋:
gin的context.HTML沒有提供合併檔案,要將檔案合併需要自己來,在serve/main.go寫入
func GetBlog(c *gin.Context) {
blogData, err := database.GetBlog(c.Request.URL.Path)
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error", "database error")
return
} else if blogData == nil {
log.Warn(c, apperr.ErrWrongArgument, nil, "parmeter error", "parmeter error")
return
}
meta, err := json.Marshal(blogData)
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error", "parse json error")
return
}
data := gin.H{
"title": blogData.Name,
"meta": string(meta),
"description": blogData.Description,
"owner": blogData.OUniquename,
"nickname": blogData.ONickname,
"updatetime": blogData.Updatetime,
}
if blogData.Type == 1 {
data["list"] = true
err = file.ParseTmpl(c.Writer, data)
} else {
data["content"] = true
//get file
fileName := strconv.Itoa(blogData.Bid)
err = file.ParseTmpl(c.Writer, data, setting.Servers["main"].FilePath+"/"+strconv.Itoa(blogData.Oid)+"/"+fileName+"/"+fileName+".md")
}
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error", "read file error")
return
}
c.Status(http.StatusOK)
}
解釋:
在view/html/component創建blogContainer.html寫入
{{ define "container/blog"}}
<div>
<!-- Title -->
<h1>{{ .title }}</h1>
<div>
<!-- Author -->
<h5>
<a href="/{{ .owner }}">
{{ .nickname }}
<span class="text-muted">
@{{ .owner }}
</span>
</a>
</h5>
<!-- Date/Time -->
<p>Update on {{ .updatetime }}</p>
<!-- description -->
<p class="lead">{{.description}}</p>
<!-- Post Content -->
{{if .content}}
<div id="content">
{{template "content"}}
</div>
{{end}}
</div>
</div>
{{ end }}
解釋:
{{if .content}}
如果有傳content才會有裡面的片段{{template "content"}}
就是blog的檔案創建blogList.html寫入
{{ define "list/blog" }}
<div class="blog-list"></div>
<!-- Blog Post template-->
<template id="blog-list-tmpl">
<div>
<div>
<a class="blog-href" href="#">
<h2 class="blog-list-title"></h2>
</a>
<p class="blog-list-description"></p>
</div>
<div class="">
<a href="#" class="owner-href blog-list-owner"></a>
‧
<a href="#" class="blog-list-createtime"></a>
</div>
</div>
</template>
{{ end }}
解釋:
在view/html/meta/index.html的body內補上
<!-- Blog Post -->
{{if not .root}}
{{template "container/blog" . }}
<hr>
{{end}}
<!-- Blog List -->
{{if .list}}
{{ template "list/blog" . }}
{{end}}
解釋:
{{if not .root}}
update就把資料跟檔案覆寫上去
var (
updateBlogParam = append(createBlogParam, "bid", "newsuperid", "newname", "newsuperUrl")
)
func UpdateBlog(c *gin.Context) {
// check form
form, err := common.BindMultipartForm(c, updateBlogParam)
if err != nil {
return
}
// check param update to database
_, blog := splitWork(c.Request.URL.Path)
// new super should be -1 if no update super
// new name should be "" if no update name
err = database.UpdateBlog(form.Value["oid"][0], form.Value["superid"][0], form.Value["newsuperid"][0],
form.Value["bid"][0], blog, form.Value["newname"][0], form.Value["descript"][0], c.Request.URL.Path,
form.Value["newsuperUrl"][0])
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error. please try again", "database error of update blog")
return
}
// write file
if form.Value["blogType"][0] == "article" && len(form.File["content"]) != 0 {
common.WriteFormFile(c, form, form.Value["bid"][0])
}
c.Header("Location", c.Request.URL.Path)
c.Status(http.StatusCreated)
}
delete把整個資料夾與資料都刪掉
func DelBlog(c *gin.Context) {
if err := database.DelBlog(c.PostForm("oid"), c.PostForm("bid"), c.Request.URL.Path); err != nil {
if err == database.ERR_TASK_FAIL {
log.Warn(c, apperr.ErrWrongArgument, err, "delete owner fail, please check oid and owner name correct")
} else {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error. please try again", "database error of delete owner")
}
return
}
if err := os.RemoveAll(setting.Servers["main"].FilePath + "/" + c.PostForm("oid") + "/" + c.PostForm("bid")); err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "something error in delete file")
return
}
c.Status(http.StatusResetContent)
}
在database/scheme.go裡補上
type BlogList struct {
OUniquename string `json:"ouniquename" xorm:"not null comment('id of User-defined') VARCHAR(50) 'uniquename'"`
ONickname string `json:"onickname" xorm:"not null VARCHAR(50) 'nickname'"`
Blog `xorm:"extends"`
}
database/main.go補上
func GetRoot(page string) ([]BlogList, error) {
err = db.SQL("call get_root(?)", page).Find(&blogs)
if err != nil {
return nil, err
} else if len(blogs) == 0 {
return nil, nil
}
return blogs, nil
}
serve/main.go補上
// get root
func GetRoot(c *gin.Context) {
blogDatas, err := database.GetRoot(c.DefaultQuery("p", "1"))
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error", "database error of getting root page")
return
}
meta, err := json.Marshal(blogDatas)
if err != nil {
log.Warn(c, apperr.ErrPermissionDenied, err, "Sorry, something error", "parse json error")
return
}
// return root page data
c.HTML(http.StatusOK, "index", gin.H{
"meta": string(meta),
"title": "DCreater",
"description": "create your blog",
"author": "林彥賓, https://github.com/Yan-Bin-Lin",
"root": true,
"list": true,
})
}
解釋:
我們把blog的讀寫搞定了,再處理前端就能看到畫面了
目前的工作環境
.
├── app
│ ├── apperr
│ │ ├── error.go
│ │ └── handle.go
│ ├── common
│ │ └── cookie.go
│ ├── config
│ │ └── app
│ │ ├── app.yaml
│ │ └── error.yaml
│ ├── database
│ │ ├── connect.go
│ │ ├── error.go
│ │ ├── main.go
│ │ └── scheme.go
│ ├── go.mod
│ ├── go.sum
│ ├── log
│ │ ├── logger.go
│ │ └── logging.go
│ ├── main.go
│ ├── middleware
│ │ ├── error.go
│ │ └── log.go
│ ├── router
│ │ ├── host_switch.go
│ │ └── main.go
│ ├── serve
│ │ ├── main.go
│ │ └── main_test.go
│ ├── setting
│ │ └── setting.go
│ ├── util
│ │ ├── debug
│ │ │ ├── stack.go
│ │ │ └── stack_test.go
│ │ └── file
│ │ └── file.go
│ └── view
│ ├── css
│ │ └── style.css
│ ├── html
│ │ ├── component
│ │ │ ├── blogContainer.html
│ │ │ └── blogList.html
│ │ └── meta
│ │ ├── head.html
│ │ └── index.html
│ └── js
├── config
│ └── app
│ ├── app.yaml
│ └── error.yaml
└── database
└── maindata