Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f55bc9f

Browse files
author
studygolang
committed
增加消息功能;
使用WebSocket进行在线人数统计; bugfix
1 parent 8392f10 commit f55bc9f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2114
-139
lines changed

websites/code/studygolang/conf/config.json.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"domain": "dev.studygolang.com",
33
"host": "127.0.0.1:8080",
4+
"wshost": "127.0.0.1:6090",
45
"drive_name": "mysql",
56
"dsn": "root:@tcp(localhost:3306)/studygolang?charset=utf8",
67
"cookie_secret": "2fwefefwf",
@@ -9,5 +10,6 @@
910
"smtp_host": "smtp.example.com",
1011
"smtp_addr": "smtp.example.com:25",
1112
"from_email": "[email protected]",
13+
"data": "data/max_online_num",
1214
"pid": "pid/studygolang.pid"
1315
}

websites/code/studygolang/src/controller/ajax.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,47 @@ import (
2323
func OtherTopicsHandler(rw http.ResponseWriter, req *http.Request) {
2424
vars := mux.Vars(req)
2525
topics := service.FindTopicsByNid(vars["nid"], vars["tid"])
26+
topics = service.JSEscape(topics)
2627
data, err := json.Marshal(topics)
2728
if err != nil {
2829
logger.Errorln("[OtherTopicsHandler] json.marshal error:", err)
2930
fmt.Fprint(rw, `{"errno": 1, "error":"解析json出错"}`)
3031
return
3132
}
32-
fmt.Fprint(rw, `{"errno": 0, "data":`+string(data)+`}`)
33+
fmt.Fprint(rw, `{"errno": 0, "topics":`+string(data)+`}`)
3334
}
3435

36+
// 网站统计信息
3537
func StatHandler(rw http.ResponseWriter, req *http.Request) {
3638
topicTotal := service.TopicsTotal()
3739
replyTotal := service.CommentsTotal(model.TYPE_TOPIC)
40+
resourceTotal := service.ResourcesTotal()
3841
userTotal := service.CountUsers()
39-
fmt.Fprint(rw, `{"errno": 0, "data":{"topic":`+strconv.Itoa(topicTotal)+`,"reply":`+strconv.Itoa(replyTotal)+`,"user":`+strconv.Itoa(userTotal)+`}}`)
42+
fmt.Fprint(rw, `{"errno": 0, "topic":`+strconv.Itoa(topicTotal)+`,"resource":`+strconv.Itoa(resourceTotal)+`,"reply":`+strconv.Itoa(replyTotal)+`,"user":`+strconv.Itoa(userTotal)+`}`)
43+
}
44+
45+
// 社区最新公告
46+
// uri: /topics/notice.json
47+
func NoticeHandler(rw http.ResponseWriter, req *http.Request) {
48+
topic := service.FindNoticeTopic()
49+
newNotice, err := json.Marshal(topic)
50+
if err != nil {
51+
logger.Errorln("[NoticeHandler] json.marshal error:", err)
52+
fmt.Fprint(rw, `{"errno": 1, "error":"解析json出错"}`)
53+
return
54+
}
55+
fmt.Fprint(rw, `{"errno": 0, "notice":`+string(newNotice)+`}`)
56+
}
57+
58+
// 社区热门节点
59+
// uri: /nodes/hot.json
60+
func HotNodesHandler(rw http.ResponseWriter, req *http.Request) {
61+
nodes := service.FindHotNodes()
62+
hotNodes, err := json.Marshal(nodes)
63+
if err != nil {
64+
logger.Errorln("[HotNodesHandler] json.marshal error:", err)
65+
fmt.Fprint(rw, `{"errno": 1, "error":"解析json出错"}`)
66+
return
67+
}
68+
fmt.Fprint(rw, `{"errno": 0, "nodes":`+string(hotNodes)+`}`)
4069
}

websites/code/studygolang/src/controller/index.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ func IndexHandler(rw http.ResponseWriter, req *http.Request) {
2323
articles := service.FindNewBlogs()
2424
// 获得最新资源
2525
resources := service.FindRecentResources()
26+
// 活跃会员
27+
activeUsers := service.FindActiveUsers(0, 9)
2628
// 设置内容模板
2729
req.Form.Set(filter.CONTENT_TPL_KEY, "/template/index.html")
2830
// 设置模板数据
29-
filter.SetData(req, map[string]interface{}{"news": newTopics, "resources": resources, "articles": articles, "nodes": nodes})
31+
filter.SetData(req, map[string]interface{}{"news": newTopics, "resources": resources, "articles": articles, "actives": activeUsers, "nodes": nodes})
3032
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2013 The StudyGolang Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
// http://studygolang.com
5+
// Author:polaris [email protected]
6+
7+
package controller
8+
9+
import (
10+
"filter"
11+
"fmt"
12+
"github.com/studygolang/mux"
13+
"net/http"
14+
"service"
15+
"strconv"
16+
"util"
17+
)
18+
19+
// 发短消息
20+
// uri: /message/send{json:(|.json)}
21+
func SendMessageHandler(rw http.ResponseWriter, req *http.Request) {
22+
vars := mux.Vars(req)
23+
content := req.FormValue("content")
24+
// 请求新建帖子页面
25+
if content == "" || req.Method != "POST" || vars["json"] == "" {
26+
user := service.FindUserByUsername(req.FormValue("username"))
27+
filter.SetData(req, map[string]interface{}{"user": user})
28+
req.Form.Set(filter.CONTENT_TPL_KEY, "/template/messages/send.html")
29+
return
30+
}
31+
32+
user, _ := filter.CurrentUser(req)
33+
to := util.MustInt(req.FormValue("to"))
34+
success := service.SendMessageTo(user["uid"].(int), to, content)
35+
if !success {
36+
fmt.Fprint(rw, `{"errno": 1, "error":"对不起,发送失败,请稍候再试!"}`)
37+
return
38+
}
39+
fmt.Fprint(rw, `{"errno": 0, "error":""}`)
40+
}
41+
42+
// 消息列表
43+
// uri: /message/{msgtype:(system|inbox|outbox)}
44+
func MessageHandler(rw http.ResponseWriter, req *http.Request) {
45+
user, _ := filter.CurrentUser(req)
46+
uid := user["uid"].(int)
47+
vars := mux.Vars(req)
48+
msgtype := vars["msgtype"]
49+
var messages []map[string]interface{}
50+
if msgtype == "system" {
51+
messages = service.FindSysMsgsByUid(strconv.Itoa(uid))
52+
} else if msgtype == "inbox" {
53+
messages = service.FindToMsgsByUid(strconv.Itoa(uid))
54+
} else {
55+
messages = service.FindFromMsgsByUid(strconv.Itoa(uid))
56+
}
57+
req.Form.Set(filter.CONTENT_TPL_KEY, "/template/messages/list.html")
58+
// 设置模板数据
59+
filter.SetData(req, map[string]interface{}{"messages": messages, "msgtype": msgtype})
60+
}
61+
62+
// 删除消息
63+
// uri: /message/delete.json
64+
func DeleteMessageHandler(rw http.ResponseWriter, req *http.Request) {
65+
if req.Method != "POST" {
66+
fmt.Fprint(rw, `{"errno": 1, "error":"非法请求!"}`)
67+
return
68+
}
69+
id := req.FormValue("id")
70+
msgtype := req.FormValue("msgtype")
71+
if !service.DeleteMessage(id, msgtype) {
72+
fmt.Fprint(rw, `{"errno": 1, "error":"对不起,删除失败,请稍候再试!"}`)
73+
return
74+
}
75+
fmt.Fprint(rw, `{"errno": 0, "error":""}`)
76+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2013 The StudyGolang Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
// http://studygolang.com
5+
// Author:polaris [email protected]
6+
7+
package controller
8+
9+
import (
10+
"go.net/websocket"
11+
"logger"
12+
"service"
13+
"strconv"
14+
"strings"
15+
"sync"
16+
"time"
17+
"util"
18+
)
19+
20+
var ServerId int
21+
var mutex sync.Mutex
22+
23+
// websocket,统计在线用户数
24+
// uri: /ws
25+
func WsHandler(wsConn *websocket.Conn) {
26+
mutex.Lock()
27+
ServerId++
28+
serverId := ServerId
29+
mutex.Unlock()
30+
req := wsConn.Request()
31+
user, err := strconv.Atoi(req.FormValue("uid"))
32+
if err != nil || user == 0 {
33+
pos := strings.LastIndex(req.RemoteAddr, ":")
34+
ip := req.RemoteAddr[:pos]
35+
user = int(util.Ip2long(ip))
36+
}
37+
userData := service.Book.AddUser(user, serverId)
38+
// 给自己发送消息,告诉当前在线用户数、历史最高在线人数
39+
onlineInfo := map[string]int{"online": service.Book.Len(), "maxonline": service.MaxOnlineNum()}
40+
message := service.NewMessage(service.WsMsgOnline, onlineInfo)
41+
err = websocket.JSON.Send(wsConn, message)
42+
if err != nil {
43+
logger.Errorln("Sending onlineusers error:", err)
44+
}
45+
var clientClosed = false
46+
for {
47+
select {
48+
case message := <-userData.MessageQueue(serverId):
49+
if err := websocket.JSON.Send(wsConn, message); err != nil {
50+
clientClosed = true
51+
}
52+
// 心跳
53+
case <-time.After(30e9):
54+
if err := websocket.JSON.Send(wsConn, ""); err != nil {
55+
clientClosed = true
56+
}
57+
}
58+
if clientClosed {
59+
service.Book.DelUser(user, serverId)
60+
break
61+
}
62+
}
63+
// 用户退出时需要变更其他用户看到的在线用户数
64+
if !service.Book.UserIsOnline(user) {
65+
message := service.NewMessage(service.WsMsgOnline, map[string]int{"online": service.Book.Len()})
66+
go service.Book.BroadcastAllUsersMessage(message)
67+
}
68+
}

websites/code/studygolang/src/filter/rules.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,42 +52,60 @@ var rules = map[string]map[string]map[string]map[string]string{
5252
},
5353
"title": {
5454
"require": {"error": "标题不能为空"},
55-
"length": {"range": "3,", "error": "话题标题长度必不能少于%d个字符"},
55+
"length": {"range": "3,", "error": "话题标题长度不能少于%d个字符"},
5656
},
5757
"content": {
5858
"require": {"error": "内容不能为空!"},
59-
"length": {"range": "2,", "error": "话题内容长度必不能少于%d个字符"},
59+
"length": {"range": "2,", "error": "话题内容长度不能少于%d个字符"},
6060
},
6161
},
6262
// 发回复
6363
`/comment/\d+\.json`: {
6464
"content": {
6565
"require": {"error": "内容不能为空!"},
66-
"length": {"range": "2,", "error": "回复内容长度必不能少于%d个字符"},
66+
"length": {"range": "2,", "error": "回复内容长度不能少于%d个字符"},
6767
},
6868
},
6969
// 发wiki
7070
"/wiki/new.json": {
7171
"title": {
7272
"require": {"error": "标题不能为空"},
73-
"length": {"range": "3,", "error": "标题长度必不能少于%d个字符"},
73+
"length": {"range": "3,", "error": "标题长度不能少于%d个字符"},
7474
},
7575
"uri": {
7676
"require": {"error": "URL不能为空"},
7777
},
7878
"content": {
7979
"require": {"error": "内容不能为空!"},
80-
"length": {"range": "2,", "error": "内容长度必不能少于%d个字符"},
80+
"length": {"range": "2,", "error": "内容长度不能少于%d个字符"},
8181
},
8282
},
8383
// 发资源
8484
"/resources/new.json": {
8585
"title": {
8686
"require": {"error": "标题不能为空"},
87-
"length": {"range": "3,", "error": "标题长度必不能少于%d个字符"},
87+
"length": {"range": "3,", "error": "标题长度不能少于%d个字符"},
8888
},
8989
"catid": {
9090
"int": {"range": "1,", "error": "请选择类别:%d"},
9191
},
9292
},
93+
// 发消息
94+
"/message/send.json": {
95+
"to": {
96+
"require": {"error": "必须指定发给谁"},
97+
},
98+
"content": {
99+
"require": {"error": "消息内容不能为空"},
100+
},
101+
},
102+
// 删除消息
103+
"/message/delete.json": {
104+
"id": {
105+
"require": {"error": "必须指定id"},
106+
},
107+
"msgtype": {
108+
"require": {"error": "必须指定消息类型"},
109+
},
110+
},
93111
}

websites/code/studygolang/src/filter/validate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (this *FormValidateFilter) PreFilter(rw http.ResponseWriter, req *http.Requ
5050
// 校验表单数据
5151
func Validate(data url.Values, rules map[string]map[string]map[string]string) (errMsg string) {
5252
for field, rule := range rules {
53-
val := data.Get(field)
53+
val := strings.TrimSpace(data.Get(field))
5454
// 检查【必填】
5555
if requireInfo, ok := rule["require"]; ok {
5656
if val == "" {

websites/code/studygolang/src/filter/view.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ var funcMap = template.FuncMap{
5050
}
5151
return utf8Str.Slice(0, length) + suffix
5252
},
53+
// if 比较
54+
"eq": func(a, b string) bool {
55+
if a == b {
56+
return true
57+
}
58+
return false
59+
},
60+
"noteq": func(a, b string) bool {
61+
if a == b {
62+
return false
63+
}
64+
return true
65+
},
5366
}
5467

5568
// 保存模板路径的key
@@ -111,6 +124,8 @@ func (this *ViewFilter) PostFilter(rw http.ResponseWriter, req *http.Request) bo
111124
// 当前用户信息
112125
me, _ := CurrentUser(req)
113126
data["me"] = me
127+
// websocket主机
128+
data["wshost"] = config.Config["wshost"]
114129
err = tpl.Execute(rw, data)
115130
if err != nil {
116131
logger.Errorf("执行模板出错(Execute):[%q] %s\n", req.RequestURI, err)

websites/code/studygolang/src/model/comment.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const (
1515
TYPE_TOPIC = iota // 帖子
1616
TYPE_BLOG // 博客
1717
TYPE_RESOURCE // 资源
18-
TYPE_SITES // 酷站
18+
TYPE_WIKI // WIKI
1919
)
2020

2121
// 评论信息(通用)

0 commit comments

Comments
 (0)