// 某个wiki页面详细信息 func FindWiki(uri string) map[string]interface{} { wiki := model.NewWiki() if err := wiki.Where("uri=" + uri).Find(); err != nil { logger.Errorln("wiki service FindWiki error:", err) return nil } uids := make(map[int]int) uids[wiki.Uid] = wiki.Uid if wiki.Cuid != "" { cuids := strings.Split(wiki.Cuid, ",") for _, cuid := range cuids { tmpUid := util.MustInt(cuid) uids[tmpUid] = tmpUid } } userMap := getUserInfos(uids) result := make(map[string]interface{}) util.Struct2Map(result, wiki) result["user"] = userMap[wiki.Uid] if wiki.Cuid != "" { cuids := strings.Split(wiki.Cuid, ",") cusers := make([]*model.User, len(cuids)) for i, cuid := range cuids { cusers[i] = userMap[util.MustInt(cuid)] } result["cuser"] = cusers } return result }
// 收藏(取消收藏) // uri: /favorite/{objid:[0-9]+}.json func FavoriteHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) user, _ := filter.CurrentUser(req) if !util.CheckInt(req.PostForm, "objtype") { fmt.Fprint(rw, `{"ok": 0, "error":"参数错误"}`) return } var err error objtype := util.MustInt(req.PostFormValue("objtype")) collect := util.MustInt(req.PostFormValue("collect")) if collect == 1 { err = service.SaveFavorite(user["uid"].(int), util.MustInt(vars["objid"]), objtype) } else { err = service.CancelFavorite(user["uid"].(int), util.MustInt(vars["objid"]), objtype) } if err != nil { fmt.Fprint(rw, `{"ok": 0, "error":"`+err.Error()+`""}`) return } fmt.Fprint(rw, `{"ok": 1, "message":"success"}`) }
// checkRange 检查范围值是否合法。 // src为要检查的值;destRange为目标范围;msg出错时信息参数 func checkRange(src int, destRange string, msg string) (errMsg string) { parts := strings.SplitN(destRange, ",", 2) parts[0] = strings.TrimSpace(parts[0]) parts[1] = strings.TrimSpace(parts[1]) min, max := 0, 0 if parts[0] == "" { max = util.MustInt(parts[1]) if src > max { errMsg = fmt.Sprintf(msg, max) } return } if parts[1] == "" { min = util.MustInt(parts[0]) if src < min { errMsg = fmt.Sprintf(msg, min) } return } if min == 0 { min = util.MustInt(parts[0]) } if max == 0 { max = util.MustInt(parts[1]) } if src < min || src > max { errMsg = fmt.Sprintf(msg, min, max) return } return }
// 喜欢(或取消喜欢) // uri: /like/{objid:[0-9]+}.json func LikeHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) user, _ := filter.CurrentUser(req) // 入库 err := service.PostComment(util.MustInt(vars["objid"]), util.MustInt(req.FormValue("objtype")), user["uid"].(int), req.FormValue("content"), req.FormValue("objname")) if err != nil { fmt.Fprint(rw, `{"errno": 1, "error":"服务器内部错误"}`) return } fmt.Fprint(rw, `{"errno": 0, "error":""}`) }
// 获得发给某人的短消息(收件箱) func FindToMsgsByUid(uid string) []map[string]interface{} { messages, err := model.NewMessage().Where("to=" + uid + " AND tdel=" + model.TdelNotDel).Order("ctime DESC").FindAll() if err != nil { logger.Errorln("message service FindToMsgsByUid Error:", err) return nil } uids := make(map[int]int) ids := make([]int, 0, len(messages)) for _, message := range messages { uids[message.From] = message.From if message.Hasread == model.NotRead { ids = append(ids, message.Id) } } // 标记已读 go MarkHasRead(ids, false, util.MustInt(uid)) userMap := getUserInfos(uids) result := make([]map[string]interface{}, len(messages)) for i, message := range messages { tmpMap := make(map[string]interface{}) util.Struct2Map(tmpMap, message) tmpMap["user"] = userMap[message.From] // 为了跟系统消息一致 tmpMap["title"] = "发来了一条消息:" result[i] = tmpMap } return result }
// 评论(或回复) // uri: /comment/{objid:[0-9]+}.json func CommentHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) user, _ := filter.CurrentUser(req) if !util.CheckInt(req.PostForm, "objtype") { fmt.Fprint(rw, `{"errno": 1, "error":"参数错误"}`) return } // 入库 comment, err := service.PostComment(user["uid"].(int), util.MustInt(vars["objid"]), req.PostForm) if err != nil { fmt.Fprint(rw, `{"errno": 1, "error":"服务器内部错误"}`) return } buf, err := json.Marshal(comment) if err != nil { logger.Errorln("[RecentCommentHandler] json.marshal error:", err) fmt.Fprint(rw, `{"errno": 1, "error":"解析json出错"}`) return } fmt.Fprint(rw, `{"errno": 0, "error":"", "data":`+string(buf)+`}`) }
// 社区帖子详细页 // uri: /topics/{tid:[0-9]+} func TopicDetailHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) topic, replies, err := service.FindTopicByTid(vars["tid"]) if err != nil { util.Redirect(rw, req, "/topics") return } likeFlag := 0 hadCollect := 0 user, ok := filter.CurrentUser(req) if ok { uid := user["uid"].(int) tid := topic["tid"].(int) likeFlag = service.HadLike(uid, tid, model.TYPE_TOPIC) hadCollect = service.HadFavorite(uid, tid, model.TYPE_TOPIC) } service.Views.Incr(req, model.TYPE_TOPIC, util.MustInt(vars["tid"])) // 设置内容模板 req.Form.Set(filter.CONTENT_TPL_KEY, "/template/topics/detail.html,/template/common/comment.html") // 设置模板数据 filter.SetData(req, map[string]interface{}{"activeTopics": "active", "topic": topic, "replies": replies, "likeflag": likeFlag, "hadcollect": hadCollect}) }
// 新建帖子 // uri: /topics/new{json:(|.json)} func NewTopicHandler(rw http.ResponseWriter, req *http.Request) { nodes := genNodes() vars := mux.Vars(req) title := req.FormValue("title") // 请求新建帖子页面 if title == "" || req.Method != "POST" || vars["json"] == "" { req.Form.Set(filter.CONTENT_TPL_KEY, "/template/topics/new.html") filter.SetData(req, map[string]interface{}{"nodes": nodes}) return } user, _ := filter.CurrentUser(req) // 入库 topic := model.NewTopic() topic.Uid = user["uid"].(int) topic.Nid = util.MustInt(req.FormValue("nid")) topic.Title = req.FormValue("title") topic.Content = req.FormValue("content") errMsg, err := service.PublishTopic(topic) if err != nil { fmt.Fprint(rw, `{"errno": 1, "error":"`, errMsg, `"}`) return } fmt.Fprint(rw, `{"errno": 0, "error":""}`) }
// 发表评论(或回复)。 // objid 注册的评论对象 // uid 评论人 func PostComment(uid, objid int, form url.Values) (*model.Comment, error) { comment := model.NewComment() comment.Objid = objid objtype := util.MustInt(form.Get("objtype")) comment.Objtype = objtype comment.Uid = uid comment.Content = form.Get("content") // TODO:评论楼层怎么处理,避免冲突?最后的楼层信息保存在内存中? // 暂时只是从数据库中取出最后的评论楼层 stringBuilder := util.NewBuffer() stringBuilder.Append("objid=").AppendInt(objid).Append(" AND objtype=").AppendInt(objtype) tmpCmt, err := model.NewComment().Where(stringBuilder.String()).Order("ctime DESC").Find() if err != nil { logger.Errorln("post comment service error:", err) return nil, err } else { comment.Floor = tmpCmt.Floor + 1 } // 入评论库 cid, err := comment.Insert() if err != nil { logger.Errorln("post comment service error:", err) return nil, err } comment.Cid = cid comment.Ctime = util.TimeNow() decodeCmtContent(comment) // 回调,不关心处理结果(有些对象可能不需要回调) if commenter, ok := commenters[objtype]; ok { logger.Debugf("评论[objid:%d] [objtype:%d] [uid:%d] 成功,通知被评论者更新", objid, objtype, uid) go commenter.UpdateComment(cid, objid, uid, time.Now().Format("2006-01-02 15:04:05")) } // 发评论,活跃度+5 go IncUserWeight("uid="+strconv.Itoa(uid), 5) // 给被评论对象所有者发系统消息 ext := map[string]interface{}{ "objid": objid, "objtype": objtype, "cid": cid, "uid": uid, } go SendSystemMsgTo(0, objtype, ext) // @某人 发系统消息 go SendSysMsgAtUids(form.Get("uid"), ext) go SendSysMsgAtUsernames(form.Get("usernames"), ext) return comment, nil }
func initMaxOnlineNum() { maxRwMu.Lock() defer maxRwMu.Unlock() if maxOnlineNum == 0 { data, err := ioutil.ReadFile(getDataFile()) if err != nil { logger.Errorln("read data file error:", err) return } maxOnlineNum = util.MustInt(strings.TrimSpace(string(data))) } }
// 喜欢(或取消喜欢) // uri: /like/{objid:[0-9]+}.json func LikeHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) user, _ := filter.CurrentUser(req) if !util.CheckInt(req.PostForm, "objtype") || !util.CheckInt(req.PostForm, "flag") { fmt.Fprint(rw, `{"ok": 0, "error":"参数错误"}`) return } uid := user["uid"].(int) objid := util.MustInt(vars["objid"]) objtype := util.MustInt(req.PostFormValue("objtype")) likeFlag := util.MustInt(req.PostFormValue("flag")) err := service.LikeObject(uid, objid, objtype, likeFlag) if err != nil { fmt.Fprint(rw, `{"ok": 0, "error":"服务器内部错误"}`) return } fmt.Fprint(rw, `{"ok": 1, "msg":"success", "data":""}`) }
// 某节点下的帖子列表 // uri: /topics/node{nid:[0-9]+} func NodesHandler(rw http.ResponseWriter, req *http.Request) { page, _ := strconv.Atoi(req.FormValue("p")) if page == 0 { page = 1 } vars := mux.Vars(req) topics, total := service.FindTopics(page, 0, "nid="+vars["nid"]) pageHtml := service.GetPageHtml(page, total) // 当前节点信息 node := model.GetNode(util.MustInt(vars["nid"])) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/topics/node.html") // 设置模板数据 filter.SetData(req, map[string]interface{}{"activeTopics": "active", "topics": topics, "page": template.HTML(pageHtml), "total": total, "node": node}) }
func doCrawl(wbconf map[string]string, isAll bool) { crawlUrl := wbconf["incr_url"] if isAll { crawlUrl = wbconf["all_url"] } listselector := wbconf["listselector"] resultselector := wbconf["resultselector"] pageField := wbconf["page_field"] maxPage := 1 if isAll { maxPage = util.MustInt(wbconf["max_page"]) } // 个人博客,一般通过 tag 方式获取,这种处理方式和搜索不一样 if wbconf["keywords"] == "" { for p := maxPage; p >= 1; p-- { if pageField == "" { // 标题不包含 go 等关键词的,也入库 if err := parseArticleList(crawlUrl+strconv.Itoa(p), listselector, resultselector, false); err != nil { break } } } return } keywords := strings.Split(wbconf["keywords"], ",") for _, keyword := range keywords { for p := 1; p <= maxPage; p++ { page := fmt.Sprintf("&%s=%d", pageField, p) if err := parseArticleList(crawlUrl+keyword+page, listselector, resultselector, true); err != nil { logger.Errorln("parse article url error:", err) break } } } }
// 发短消息 // uri: /message/send{json:(|.json)} func SendMessageHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) content := req.FormValue("content") // 请求新建帖子页面 if content == "" || req.Method != "POST" || vars["json"] == "" { user := service.FindUserByUsername(req.FormValue("username")) filter.SetData(req, map[string]interface{}{"user": user}) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/messages/send.html") return } user, _ := filter.CurrentUser(req) to := util.MustInt(req.FormValue("to")) success := service.SendMessageTo(user["uid"].(int), to, content) if !success { fmt.Fprint(rw, `{"errno": 1, "error":"对不起,发送失败,请稍候再试!"}`) return } fmt.Fprint(rw, `{"errno": 0, "error":""}`) }
// 新建帖子 // uri: /topics/new{json:(|.json)} func NewTopicHandler(rw http.ResponseWriter, req *http.Request) { nodes := genNodes() vars := mux.Vars(req) content := req.FormValue("content") // 请求新建帖子页面 if content == "" || req.Method != "POST" || vars["json"] == "" { req.Form.Set(filter.CONTENT_TPL_KEY, "/template/topics/new.html") filter.SetData(req, map[string]interface{}{"nodes": nodes}) return } // 入库 topic := model.NewTopic() logger.Traceln("anonymous") logger.Traceln(req.FormValue("anonymous")) if req.FormValue("anonymous") == "1" { topic.Uid, _ = strconv.Atoi(Config["auid"]) } else { user, _ := filter.CurrentUser(req) if user != nil { topic.Uid = user["uid"].(int) } else { topic.Uid, _ = strconv.Atoi(Config["auid"]) } } logger.Traceln(topic) topic.Nid = util.MustInt(req.FormValue("nid")) topic.Title = req.FormValue("title") topic.Content = req.FormValue("content") logger.Traceln(topic) errMsg, err := service.PublishTopic(topic) logger.Traceln("PublishTopic end") logger.Traceln(errMsg) if err != nil { fmt.Fprint(rw, `{"errno": 1, "error":"`, errMsg, `"}`) return } fmt.Fprint(rw, `{"errno": 0, "error":""}`) }
// 获取当前(id)博文以及前后博文 func FindArticlesById(idstr string) (curArticle *model.Article, prevNext []*model.Article, err error) { id := util.MustInt(idstr) cond := "id BETWEEN ? AND ? AND status!=2" articles, err := model.NewArticle().Where(cond, id-5, id+5).FindAll() if err != nil { logger.Errorln("article service FindArticlesById Error:", err) return } if len(articles) == 0 { return } prevNext = make([]*model.Article, 2) prevId, nextId := articles[0].Id, articles[len(articles)-1].Id for _, article := range articles { if article.Id < id && article.Id > prevId { prevId = article.Id prevNext[0] = article } else if article.Id > id && article.Id < nextId { nextId = article.Id prevNext[1] = article } else if article.Id == id { curArticle = article } } if prevId == id { prevNext[0] = nil } if nextId == id { prevNext[1] = nil } return }
// 某个资源详细页 // uri: /resources/{id:[0-9]+} func ResourceDetailHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) resource, comments := service.FindResource(vars["id"]) if len(resource) == 0 { util.Redirect(rw, req, "/resources") return } likeFlag := 0 hadCollect := 0 user, ok := filter.CurrentUser(req) if ok { uid := user["uid"].(int) id := resource["id"].(int) likeFlag = service.HadLike(uid, id, model.TYPE_RESOURCE) hadCollect = service.HadFavorite(uid, id, model.TYPE_RESOURCE) } service.Views.Incr(req, model.TYPE_RESOURCE, util.MustInt(vars["id"])) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/resources/detail.html,/template/common/comment.html") filter.SetData(req, map[string]interface{}{"activeResources": "active", "resource": resource, "comments": comments, "likeflag": likeFlag, "hadcollect": hadCollect}) }
// 获得某人的系统消息 // 系统消息类型不同,在ext中存放的字段也不一样,如下: // model.MsgtypeTopicReply/MsgtypeResourceComment/MsgtypeWikiComment存放都为: // {"uid":xxx,"objid":xxx} // model.MsgtypeAtMe为:{"uid":xxx,"cid":xxx,"objid":xxx,"objtype":xxx} func FindSysMsgsByUid(uid string) []map[string]interface{} { messages, err := model.NewSystemMessage().Where("to=" + uid).Order("ctime DESC").FindAll() if err != nil { logger.Errorln("message service FindSysMsgsByUid Error:", err) return nil } uids := make(map[int]int) tids := make(map[int]int) resIds := make(map[int]int) wikiIds := make(map[int]int) // 评论ID cids := make(map[int]int) ids := make([]int, 0, len(messages)) for _, message := range messages { ext := message.Ext() if val, ok := ext["uid"]; ok { uid := int(val.(float64)) uids[uid] = uid } var objid int if val, ok := ext["objid"]; ok { objid = int(val.(float64)) } switch message.Msgtype { case model.MsgtypeTopicReply: tids[objid] = objid case model.MsgtypeWikiComment: wikiIds[objid] = objid case model.MsgtypeAtMe: objTypeFloat := ext["objtype"].(float64) switch int(objTypeFloat) { case model.TYPE_TOPIC: tids[objid] = objid case model.TYPE_BLOG: //tids[objid] = objid case model.TYPE_RESOURCE: resIds[objid] = objid case model.TYPE_WIKI: wikiIds[objid] = objid } } if val, ok := ext["cid"]; ok { cid := int(val.(float64)) cids[cid] = cid } if message.Hasread == "未读" { ids = append(ids, message.Id) } } // 标记已读 go MarkHasRead(ids, true, util.MustInt(uid)) userMap := getUserInfos(uids) commentMap := getComments(cids) topicMap := getTopics(tids) result := make([]map[string]interface{}, len(messages)) for i, message := range messages { tmpMap := make(map[string]interface{}) // 某条信息的提示(标题) title := "" ext := message.Ext() if val, ok := ext["objid"]; ok { objTitle := "" objUrl := "" objid := int(val.(float64)) switch message.Msgtype { case model.MsgtypeTopicReply: objTitle = topicMap[objid].Title objUrl = "/topics/" + strconv.Itoa(topicMap[objid].Tid) title = "回复了你的主题:" case model.MsgtypeAtMe: title = "评论时提到了你,在" switch int(ext["objtype"].(float64)) { case model.TYPE_TOPIC: objTitle = topicMap[objid].Title objUrl = "/topics/" + strconv.Itoa(topicMap[objid].Tid) title += "主题:" } } tmpMap["objtitle"] = objTitle tmpMap["objurl"] = objUrl } tmpMap["ctime"] = message.Ctime tmpMap["id"] = message.Id tmpMap["hasread"] = message.Hasread if val, ok := ext["uid"]; ok { tmpMap["user"] = userMap[int(val.(float64))] } // content 和 cid不会同时存在 if val, ok := ext["content"]; ok { tmpMap["content"] = val.(string) } else if val, ok := ext["cid"]; ok { tmpMap["content"] = decodeCmtContent(commentMap[int(val.(float64))]) } tmpMap["title"] = title result[i] = tmpMap } return result }
// 某个分类的资源列表 // uri: /resources/cat/{catid:[0-9]+} func CatResourcesHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) catid := vars["catid"] page, _ := strconv.Atoi(req.FormValue("p")) if page == 0 { page = 1 } resources, total := service.FindResourcesByCatid(catid, page) pageHtml := service.GetPageHtml(page, total, req.URL.Path) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/resources/index.html") filter.SetData(req, map[string]interface{}{"activeResources": "active", "resources": resources, "categories": service.AllCategory, "page": template.HTML(pageHtml), "curCatid": util.MustInt(catid)}) }