Skip to content
Permalink
Browse files

update #31

  • Loading branch information...
sunface committed Sep 12, 2019
1 parent 636082f commit ca863975d0203675af7d32e810531db05e62fa72
@@ -18,6 +18,7 @@ func apiHandler(e *echo.Echo) {
e.GET("/web/article/detail", post.GetArticleDetail)
e.GET("/web/article/beforeEdit", post.BeforeEditAr, user.CheckSignIn)
e.POST("/web/article/saveChanges", post.SaveArticleChanges, user.CheckSignIn)
e.POST("/article/like", post.ArticleLike, user.CheckSignIn)

// comment apis
e.POST("/web/comment/create", post.Comment, user.CheckSignIn)
@@ -3,8 +3,10 @@ package post
import (
"encoding/json"
"net/http"
"strconv"
"time"

"github.com/gocql/gocql"
"github.com/labstack/echo"
"github.com/thinkindev/im.dev/internal/ecode"
"github.com/thinkindev/im.dev/internal/misc"
@@ -26,8 +28,8 @@ type ArContent struct {

// NewArticle create a new article
func NewArticle(c echo.Context) error {
opType := c.FormValue("type")
if opType != "1" && opType != "2" {
opType, _ := strconv.Atoi(c.FormValue("type"))
if opType != PostDraft && opType != PostPublished {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
@@ -52,8 +54,16 @@ func NewArticle(c echo.Context) error {
// modify render
ar.Render = modify(ar.Render)

err = misc.CQL.Query(`INSERT INTO article (id,uid,title,tags,md,render,status,edit_date,lang)
VALUES (?,?,?,?,?,?,?,?,?)`, ar.ID, sess.ID, ar.Title, ar.Tags, ar.MD, ar.Render, opType, time.Now().Unix(), ar.Lang).Exec()
words := countWords(ar.MD)
var q *gocql.Query
if opType == PostDraft {
q = misc.CQL.Query(`INSERT INTO article (id,uid,title,tags,md,render,words,status,edit_date,lang)
VALUES (?,?,?,?,?,?,?,?,?,?)`, ar.ID, sess.ID, ar.Title, ar.Tags, ar.MD, ar.Render, words, PostDraft, time.Now().Unix(), ar.Lang)
} else { // publish
q = misc.CQL.Query(`INSERT INTO article (id,uid,title,tags,md,render,words,status,publish_date,edit_date,lang)
VALUES (?,?,?,?,?,?,?,?,?,?,?)`, ar.ID, sess.ID, ar.Title, ar.Tags, ar.MD, ar.Render, words, PostPublished, time.Now().Unix(), 0, ar.Lang)
}
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
@@ -79,6 +89,9 @@ type ArticleDetail struct {
PublishDate string `json:"publish_date"`
EditDate string `json:"edit_date"`
Lang string `json:"lang"`
Words int `json:"words"`
Likes int `json:"likes"` // all likes of this article
Liked bool `json:"liked"` // current user liked this article
pubDate int64
editDate int64
}
@@ -94,8 +107,8 @@ func GetArticleDetail(c echo.Context) error {
}

detail := &ArticleDetail{ID: arID}
err := misc.CQL.Query(`SELECT uid,title,tags,render,status,publish_date,edit_date,lang FROM article WHERE id=?`, arID).Scan(
&detail.UID, &detail.Title, &detail.Tags, &detail.Render, &detail.Status, &detail.pubDate, &detail.editDate, &detail.Lang,
err := misc.CQL.Query(`SELECT uid,title,tags,render,words,status,publish_date,edit_date,lang FROM article WHERE id=?`, arID).Scan(
&detail.UID, &detail.Title, &detail.Tags, &detail.Render, &detail.Words, &detail.Status, &detail.pubDate, &detail.editDate, &detail.Lang,
)
if err != nil {
if err.Error() == misc.CQLNotFound {
@@ -112,11 +125,26 @@ func GetArticleDetail(c echo.Context) error {
}

if detail.pubDate != 0 {
detail.PublishDate = utils.Time2ReadableString(time.Unix(detail.pubDate, 0))
detail.PublishDate = utils.Time2EnglishString(time.Unix(detail.pubDate, 0))
}
if detail.editDate != 0 {
detail.EditDate = utils.Time2ReadableString(time.Unix(detail.editDate, 0))
detail.EditDate = utils.Time2EnglishString(time.Unix(detail.editDate, 0))
}

// if user signin, get his liked about this article
sess := user.GetSession(c)
if sess != nil {
var date int64
q := misc.CQL.Query("SELECT input_date FROM post_like WHERE post_id=? and uid=?", arID, sess.ID)
q.Scan(&date)
if date != 0 {
detail.Liked = true
}
}

// get how many user like this article
misc.CQL.Query("SELECT likes FROM post_counter WHERE id=?", arID).Scan(&detail.Likes)

return c.JSON(http.StatusOK, misc.HTTPResp{
Data: detail,
})
@@ -215,8 +243,9 @@ func SaveArticleChanges(c echo.Context) error {
// modify render
ar.Render = modify(ar.Render)

err = misc.CQL.Query(`UPDATE article SET title=?,tags=?,md=?,render=?,edit_date=?,lang=? WHERE id=?`,
ar.Title, ar.Tags, ar.MD, ar.Render, time.Now().Unix(), ar.Lang, ar.ID).Exec()
words := countWords(ar.MD)
err = misc.CQL.Query(`UPDATE article SET title=?,tags=?,md=?,render=?,words=?,edit_date=?,lang=? WHERE id=?`,
ar.Title, ar.Tags, ar.MD, ar.Render, words, time.Now().Unix(), ar.Lang, ar.ID).Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
@@ -239,3 +268,85 @@ func saveTags(arID string, tags []string) {
}
}
}

// ArticleLike means a user like this article
func ArticleLike(c echo.Context) error {
id := c.FormValue("id")
tp := c.FormValue("type")
if id == "" || (tp != "1" && tp != "2") {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
})
}

sess := user.GetSession(c)

// check already liked
var pid string
q := misc.CQL.Query("SELECT post_id FROM post_like WHERE post_id=? and uid=?", id, sess.ID)
err := q.Scan(&pid)
if err != nil {
if err.Error() != misc.CQLNotFound {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}
}

if tp == "1" { // like
if pid == id {
misc.Log.Info("someone is try to attack imdev server", zap.String("remote_ip", c.RealIP()))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

q = misc.CQL.Query("INSERT INTO post_like (post_id,uid,type,input_date) VALUES (?,?,?,?)",
id, sess.ID, OpPostLike, time.Now().Unix())
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

q = misc.CQL.Query("UPDATE post_counter SET likes=likes + 1 WHERE id=?", id)
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
}
} else { // cancel like
if pid != id {
misc.Log.Info("someone is try to attack imdev server", zap.String("remote_ip", c.RealIP()))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

q = misc.CQL.Query("DELETE FROM post_like WHERE post_id=? and uid=?",
id, sess.ID)
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

q = misc.CQL.Query("UPDATE post_counter SET likes=likes - 1 WHERE id=?", id)
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
}
}

return c.JSON(http.StatusOK, misc.HTTPResp{})
}
@@ -15,10 +15,15 @@ const (
)

const (
// OpCommentLike means a user like a post
// OpCommentLike means a user like a comment
OpCommentLike = 1
// OpCommentDislike means a user dislike a post
// OpCommentDislike means a user dislike a comment
OpCommentDislike = 2

// OpPostLike means a user like a post
OpPostLike = 1
// OpPostDislike means a user dislike a post
OpPostDislike = 2
)

const (
@@ -1 +1,74 @@
package post

import "strings"

// 空格先行分割
//1.数字和英文字母 遇到2类字符或者空格计数一
//2.其余字符计数一

// 这段代码因为考虑各种情况,略复杂,后续我会整理下
// @todo
func countWords(md string) int {
mds := strings.Split(md, " ")

count := 0
//上一个字符是否是特殊字符(非英文字母和数字)
special := false
lastCountSpecial := false
var last rune
var old rune
isfirst := false
for _, words := range mds {
last = rune(0)
special = false
lastCountSpecial = false
isfirst = true
for _, r := range words {
if special {
//如果上一个字符是特殊字符,那么当前字符无论是什么,都计数+1
count++
lastCountSpecial = true
} else {
//如果是首字符,+1
if isfirst {
count++
} else {
//如果上一个字符不是特殊字符,那么当前字符必须是特殊字符才能计数+1,否则认为是连续的
if !isNumber(r) && !isAlpha(r) {
count++
}
}

if isNumber(r) || isAlpha(r) {
lastCountSpecial = false
}
}
//判断当前字符是否是特殊字符
special = !isNumber(r) && !isAlpha(r)
old = last
last = r
isfirst = false
}
//如果当前非特殊字符是最后一个字符,那么计数+1
if !special && lastCountSpecial && (isNumber(old) || isAlpha(old)) {
count++
}

}

return count
}

func isNumber(c rune) bool {
if c >= 48 && c <= 57 {
return true
}
return false
}

func isAlpha(c rune) bool {
if (c >= 65 && c <= 90) || (c >= 97 && c <= 122) {
return true
}
return false
}
@@ -6,14 +6,6 @@ import (
"time"
)

// Time2ReadableString converts time to readable string
// 1分钟之内,显示xx秒前
// 1小时之内,显示XX分钟前
// 24小时之内,显示xx小时前
// 昨天 x:x
// 前天 x:x
// 同一年,显示x月xx
// 不同年,显示xx.xx.xx
func Time2ReadableString(t time.Time) string {
now := time.Now().Local()
intv := now.Unix() - t.Unix()
@@ -36,9 +28,35 @@ func Time2ReadableString(t time.Time) string {
return fmt.Sprintf("yestoday %02d:%02d", h, m)
}

if y1 == y2 {
return fmt.Sprintf("%02d.%02d", m2, d2)
return Time2EnglishString(t)
}

func Time2String(t time.Time) string {
return t.Format("2006-01-02 15:04:05.999")
}

var months = map[int]string{
1: "Jan",
2: "Feb",
3: "Mar",
4: "Apr",
5: "May",
6: "Jun",
7: "Jul",
8: "Aug",
9: "Sep",
10: "Oct",
11: "Nov",
12: "Dec",
}

func Time2EnglishString(t time.Time) string {
now := time.Now()
// 检查是否是同一年
y, m, d := t.Date()
if now.Year() == y {
return fmt.Sprintf("%s %d", months[int(m)], d)
}

return t.Format("06.1.2")
return fmt.Sprintf("%s %d,%d", months[int(m)], d, y)
}
@@ -40,6 +40,7 @@ CREATE TABLE IF NOT EXISTS article (
md text, -- markdown
render text, -- rendered html
status tinyint, -- 1. draft 2. published 3. delete
words int, -- word count
publish_date bigint,
edit_date bigint,

@@ -80,17 +81,6 @@ CREATE CUSTOM INDEX IF NOT EXISTS ON comment (post_id)
USING 'org.apache.cassandra.index.sasi.SASIIndex' ;



CREATE TABLE IF NOT EXISTS post_counter (
id text, -- post id

comments counter,
likes counter,
recommands counter,

PRIMARY KEY (id)
) WITH gc_grace_seconds = 10800;

CREATE TABLE IF NOT EXISTS comment_counter (
id text, -- comment id
likes counter,
@@ -107,3 +97,25 @@ CREATE TABLE IF NOT EXISTS comment_like (
) WITH gc_grace_seconds = 10800;
CREATE CUSTOM INDEX IF NOT EXISTS ON comment_like (uid)
USING 'org.apache.cassandra.index.sasi.SASIIndex' ;


CREATE TABLE IF NOT EXISTS post_counter (
id text, -- post id

comments counter,
likes counter,
recommands counter,

PRIMARY KEY (id)
) WITH gc_grace_seconds = 10800;

-- record whether a user likes a post
CREATE TABLE IF NOT EXISTS post_like (
post_id text, -- post id
uid text, -- user id
type tinyint, -- 1: like 2: dislike
input_date bigint,
PRIMARY KEY (post_id,uid)
) WITH gc_grace_seconds = 10800;
CREATE CUSTOM INDEX IF NOT EXISTS ON post_like (uid)
USING 'org.apache.cassandra.index.sasi.SASIIndex' ;

This file was deleted.

0 comments on commit ca86397

Please sign in to comment.
You can’t perform that action at this time.