Commit 0c74287d authored by shajiaiming's avatar shajiaiming

fix

parent 7f0dae21
package models
import (
"bwallet/pkg/setting"
"github.com/jinzhu/gorm"
)
type Article struct {
Model
Title string `gorm:"size:255;not null;" json:"title"`
Source string `gorm:"not null;" json:"source"`
LogoPic string `gorm:"not null;" json:"logo_pic"`
ImgUrl string `gorm:"not null;" json:"img_url"`
Content string `gorm:"not null;" json:"content"`
Top uint8 `gorm:"not null;default:0" json:"top"`
Highlight uint8 `gorm:"not null;default:0" json:"highlight"`
PlatformId int `gorm:"not null;default:1" json:"platform_id"`
CreateTime int64 `gorm:"not null;" json:"create_time"`
}
func (w Article) TableName() string {
return setting.DatabaseSetting.Name_Coin + ".article"
}
func ExistArticleById(id int) (bool, error) {
var article Article
err := db.Select("id").Where("id = ?", id).First(&article).Error
if err != nil && err != gorm.ErrRecordNotFound {
return false, err
}
if article.ID > 0 {
return true, nil
}
return false, nil
}
func GetArticleTotal(maps interface{}) (int, error) {
var count int
if _, ok := maps.(map[string]interface{})["search_type"]; ok {
if err := db.Model(&Article{}).Where("platform_id in (?)", maps.(map[string]interface{})["search_type"]).Count(&count).Error; err != nil {
return 0, err
}
} else {
if err := db.Model(&Article{}).Where(maps).Count(&count).Error; err != nil {
return 0, err
}
}
return count, nil
}
func GetArticle(pageNum, pageSize int, maps interface{}) ([]*Article, error) {
var article []*Article
if _, ok := maps.(map[string]interface{})["search_type"]; ok {
err := db.Where("platform_id in (?)", maps.(map[string]interface{})["search_type"]).Order("top desc").Order("create_time desc").Offset(pageNum).Limit(pageSize).Find(&article).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
} else {
err := db.Where(maps).Order("top desc").Order("create_time desc").Offset(pageNum).Limit(pageSize).Find(&article).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
}
return article, nil
}
func GetOneArticle(maps interface{}) (*Article, error) {
var article Article
err := db.Where(maps).First(&article).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
err = db.Model(&article).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return &article, nil
}
func AddArticle(data map[string]interface{}) (error) {
article := Article{
Title: data["title"].(string),
Source: data["source"].(string),
LogoPic: data["logo_pic"].(string),
ImgUrl: data["img_url"].(string),
Content: data["content"].(string),
Top: data["top"].(uint8),
Highlight: data["highlight"].(uint8),
PlatformId: data["platform_id"].(int),
CreateTime: data["create_time"].(int64),
}
if err := db.Create(&article).Error; err != nil {
return err
}
return nil
}
func EditArticle(id int, data interface{}) error {
if err := db.Model(&Article{}).Where("id = ?", id).Updates(data).Error; err != nil {
return err
}
return nil
}
func DeleteArticle(id int) error {
if err := db.Where("id = ?", id).Delete(Article{}).Error; err != nil {
return err
}
return nil
}
...@@ -12,13 +12,12 @@ type News struct { ...@@ -12,13 +12,12 @@ type News struct {
Content string `gorm:"not null;" json:"content"` Content string `gorm:"not null;" json:"content"`
Top uint8 `gorm:"not null;default:0" json:"top"` Top uint8 `gorm:"not null;default:0" json:"top"`
Highlight uint8 `gorm:"not null;default:0" json:"highlight"` Highlight uint8 `gorm:"not null;default:0" json:"highlight"`
Status uint8 `gorm:"not null;default:0" json:"status"`
PlatformId int `gorm:"not null;default:1" json:"platform_id"` PlatformId int `gorm:"not null;default:1" json:"platform_id"`
CreateTime int64 `gorm:"not null;" json:"create_time"` CreateTime int64 `gorm:"not null;" json:"create_time"`
} }
func (w News) TableName() string { func (w News) TableName() string {
return setting.DatabaseSetting.Name_Coin + ".article" return setting.DatabaseSetting.Name_Coin + ".news"
} }
func ExistNewsById(id int) (bool, error) { func ExistNewsById(id int) (bool, error) {
...@@ -60,12 +59,12 @@ func GetNewsTotal(maps interface{}) (int, error) { ...@@ -60,12 +59,12 @@ func GetNewsTotal(maps interface{}) (int, error) {
func GetNews(pageNum, pageSize int, maps interface{}) ([]*News, error) { func GetNews(pageNum, pageSize int, maps interface{}) ([]*News, error) {
var news []*News var news []*News
if _, ok := maps.(map[string]interface{})["search_type"]; ok { if _, ok := maps.(map[string]interface{})["search_type"]; ok {
err := db.Where("platform_id in (?)", maps.(map[string]interface{})["search_type"]).Order("create_time desc").Offset(pageNum).Limit(pageSize).Find(&news).Error err := db.Where("platform_id in (?)", maps.(map[string]interface{})["search_type"]).Order("top desc").Order("create_time desc").Offset(pageNum).Limit(pageSize).Find(&news).Error
if err != nil && err != gorm.ErrRecordNotFound { if err != nil && err != gorm.ErrRecordNotFound {
return nil, err return nil, err
} }
} else { } else {
err := db.Where(maps).Order("create_time desc").Offset(pageNum).Limit(pageSize).Find(&news).Error err := db.Where(maps).Order("top desc").Order("create_time desc").Offset(pageNum).Limit(pageSize).Find(&news).Error
if err != nil && err != gorm.ErrRecordNotFound { if err != nil && err != gorm.ErrRecordNotFound {
return nil, err return nil, err
} }
...@@ -95,7 +94,6 @@ func AddNews(data map[string]interface{}) (error) { ...@@ -95,7 +94,6 @@ func AddNews(data map[string]interface{}) (error) {
Content: data["content"].(string), Content: data["content"].(string),
Top: data["top"].(uint8), Top: data["top"].(uint8),
Highlight: data["highlight"].(uint8), Highlight: data["highlight"].(uint8),
Status: data["status"].(uint8),
PlatformId: data["platform_id"].(int), PlatformId: data["platform_id"].(int),
CreateTime: data["create_time"].(int64), CreateTime: data["create_time"].(int64),
} }
......
...@@ -139,4 +139,12 @@ var ( ...@@ -139,4 +139,12 @@ var (
ErrUpdateNews = &Errno{Code: 20102, Message: "The news update error."} ErrUpdateNews = &Errno{Code: 20102, Message: "The news update error."}
ErrDeleteNews = &Errno{Code: 20102, Message: "The news delete error."} ErrDeleteNews = &Errno{Code: 20102, Message: "The news delete error."}
ErrExistNews = &Errno{Code: 20103, Message: "The news already exists."} ErrExistNews = &Errno{Code: 20103, Message: "The news already exists."}
// article errors
ErrArticleNotFound = &Errno{Code: 20101, Message: "The article was not found."}
ErrCountArticle = &Errno{Code: 20102, Message: "The article statistic error."}
ErrAddArticle = &Errno{Code: 20101, Message: "The article add error."}
ErrUpdateArticle = &Errno{Code: 20102, Message: "The article update error."}
ErrDeleteArticle = &Errno{Code: 20102, Message: "The article delete error."}
ErrExistArticle = &Errno{Code: 20103, Message: "The article already exists."}
) )
...@@ -2,6 +2,8 @@ package util ...@@ -2,6 +2,8 @@ package util
import ( import (
"bwallet/pkg/setting" "bwallet/pkg/setting"
"crypto/md5"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
...@@ -110,3 +112,13 @@ func Deserialization(byt []byte, ptr interface{}) (err error) { ...@@ -110,3 +112,13 @@ func Deserialization(byt []byte, ptr interface{}) (err error) {
err = json.Unmarshal(byt, &ptr) err = json.Unmarshal(byt, &ptr)
return return
} }
func StringToMd5(str string) string {
m5 := md5.New()
_, err := m5.Write([]byte(str))
if err != nil {
panic(err)
}
md5String := hex.EncodeToString(m5.Sum(nil))
return md5String
}
package app
import (
"bwallet/pkg/errno"
"bwallet/pkg/handler"
"bwallet/pkg/util"
"bwallet/service/article_service"
"github.com/Unknwon/com"
"github.com/astaxie/beego/validation"
"github.com/gin-gonic/gin"
"strconv"
)
func GetOneArticle(c *gin.Context) {
id := com.StrTo(c.DefaultQuery("id", "0")).MustInt()
valid := validation.Validation{}
valid.Min(id, 1, "id").Message("Id必须大于0")
if valid.HasErrors() {
handler.SendResponse(c, valid.Errors[0], nil)
return
}
articleService := article_service.Article{Id: id}
exists, err := articleService.ExistById()
if err != nil || !exists {
handler.SendResponse(c, errno.ErrArticleNotFound, nil)
return
}
article, err := articleService.Get()
if err != nil {
handler.SendResponse(c, errno.ErrArticleNotFound, nil)
return
}
handler.SendResponse(c, nil, article)
}
func GetArticle(c *gin.Context) {
platform_id, _ := strconv.Atoi(c.Request.Header.Get("FZM-PLATFORM-ID"))
valid := validation.Validation{}
valid.Min(platform_id, 1, "platform_id").Message("平台Id不能为空")
if valid.HasErrors() {
handler.SendResponse(c, valid.Errors[0], nil)
return
}
articleService := article_service.Article{
PlatformId: platform_id,
SearchType: -1,
PageNum: util.GetPage(c),
PageSize: util.GetLimit(c),
}
total, err := articleService.Count()
if err != nil {
handler.SendResponse(c, errno.ErrCountArticle, nil)
return
}
article, err := articleService.GetAll()
if err != nil {
handler.SendResponse(c, errno.InternalServerError, nil)
return
}
data := make(map[string]interface{})
data["items"] = article
data["total"] = total
handler.SendResponse(c, nil, data)
}
package backend
import (
"bwallet/pkg/errno"
"bwallet/pkg/handler"
"bwallet/pkg/util"
"bwallet/service/article_service"
"bwallet/validate_service"
"fmt"
"github.com/Unknwon/com"
"github.com/astaxie/beego/validation"
"github.com/gin-gonic/gin"
"strings"
)
func GetOneArticle(c *gin.Context) {
id := com.StrTo(c.DefaultQuery("id", "0")).MustInt()
valid := validation.Validation{}
valid.Min(id, 1, "id").Message("ID必须大于0")
if valid.HasErrors() {
handler.SendResponse(c, errno.ErrValidation, nil)
return
}
token := c.Request.Header.Get("Token")
user, _ := util.ParseToken(token)
group := user.UserInfo.Group
var platform_id int
platform_id = user.UserInfo.PlatformId
articleService := article_service.Article{}
if ("administrator" == group) {
articleService = article_service.Article{
Id: id,
PageNum: util.GetPage(c),
PageSize: util.GetLimit(c),
}
} else {
articleService = article_service.Article{
Id: id,
PlatformId: platform_id,
PageNum: util.GetPage(c),
PageSize: util.GetLimit(c),
}
}
article, err := articleService.GetAll()
if err != nil {
handler.SendResponse(c, errno.ErrArticleNotFound, nil)
return
}
handler.SendResponse(c, nil, article)
return
}
func GetArticle(c *gin.Context) {
token := c.Request.Header.Get("Token")
user, _ := util.ParseToken(token)
group := user.UserInfo.Group
var platform_id int
platform_id = user.UserInfo.PlatformId
if ("administrator" == group) {
if arg := c.Query("platform_id"); arg != "" {
platform_id = com.StrTo(c.Query("platform_id")).MustInt()
}
}
articleService := article_service.Article{
PlatformId: platform_id,
PageNum: util.GetPage(c),
PageSize: util.GetLimit(c),
}
total, err := articleService.Count()
if err != nil {
handler.SendResponse(c, errno.ErrCountArticle, nil)
return
}
article, err := articleService.GetAll()
if err != nil {
handler.SendResponse(c, errno.InternalServerError, nil)
return
}
data := make(map[string]interface{})
data["items"] = article
data["total"] = total
handler.SendResponse(c, nil, data)
}
func AddArticle(c *gin.Context) {
article := validate_service.Article{}
c.ShouldBindJSON(&article)
//方法一
if ok, errors := validate_service.ValidateInputs(article); !ok {
for _, err := range errors {
handler.SendResponse(c, errno.ErrBind, strings.Join(err, " "))
return
}
}
token := c.Request.Header.Get("Token")
user, _ := util.ParseToken(token)
group := user.UserInfo.Group
var platform_id int
platform_id = user.UserInfo.PlatformId
if ("administrator" == group) {
if article.PlatformId != 0 {
platform_id = article.PlatformId
}
}
articleService := article_service.Article{
Title: article.Title,
Source: article.Source,
LogoPic: article.LogoPic,
ImgUrl: article.ImgUrl,
Content: article.Content,
Top: article.Top,
Highlight: article.Highlight,
PlatformId: platform_id,
}
articleService.Add()
handler.SendResponse(c, nil, nil)
}
func EditArticle(c *gin.Context) {
article := validate_service.EditArticle{}
c.ShouldBindJSON(&article)
//方法一
if ok, errors := validate_service.ValidateInputs(article); !ok {
for _, err := range errors {
handler.SendResponse(c, errno.ErrBind, strings.Join(err, " "))
return
}
}
token := c.Request.Header.Get("Token")
user, _ := util.ParseToken(token)
group := user.UserInfo.Group
platform_id := user.UserInfo.PlatformId
if ("administrator" == group) {
if article.PlatformId != 0 {
platform_id = article.PlatformId
}
}
articleService := article_service.Article{
Id: article.Id,
Title: article.Title,
Content: article.Content,
Source: article.Source,
LogoPic: article.LogoPic,
ImgUrl: article.ImgUrl,
Top: article.Top,
Highlight: article.Highlight,
PlatformId: platform_id,
}
if err := articleService.Edit(); err != nil {
handler.SendResponse(c, errno.ErrUpdateArticle, nil)
return
}
handler.SendResponse(c, nil, nil)
}
func DeleteArticle(c *gin.Context) {
id := com.StrTo(c.DefaultQuery("id", "0")).MustInt()
valid := validation.Validation{}
valid.Min(id, 1, "id").Message("Id必须大于0")
if valid.HasErrors() {
handler.SendResponse(c, valid.Errors[0], nil)
return
}
articleService := article_service.Article{Id: id}
exists, err := articleService.ExistById()
if err != nil {
fmt.Println(err,1111)
handler.SendResponse(c, errno.ErrArticleNotFound, nil)
return
}
if !exists {
fmt.Println(err,2222)
handler.SendResponse(c, errno.ErrArticleNotFound, nil)
return
}
token := c.Request.Header.Get("Token")
user, _ := util.ParseToken(token)
group := user.UserInfo.Group
if ("administrator" != group) {
article, _ := articleService.Get()
if article.PlatformId != user.UserInfo.PlatformId {
handler.SendResponse(c, errno.ErrUserAuthIncorrect, nil)
return
}
}
err = articleService.Delete()
if err != nil {
handler.SendResponse(c, errno.ErrDeleteArticle, nil)
return
}
handler.SendResponse(c, nil, nil)
}
...@@ -4,7 +4,6 @@ import ( ...@@ -4,7 +4,6 @@ import (
"bwallet/pkg/errno" "bwallet/pkg/errno"
"bwallet/pkg/handler" "bwallet/pkg/handler"
"bwallet/pkg/util" "bwallet/pkg/util"
"bwallet/service/auth_service"
"bwallet/service/chain_service" "bwallet/service/chain_service"
"bwallet/validate_service" "bwallet/validate_service"
"github.com/Unknwon/com" "github.com/Unknwon/com"
...@@ -15,9 +14,8 @@ import ( ...@@ -15,9 +14,8 @@ import (
func GetChain(c *gin.Context) { func GetChain(c *gin.Context) {
token := c.Request.Header.Get("Token") token := c.Request.Header.Get("Token")
authService := auth_service.Auth{Token: token} user, _ := util.ParseToken(token)
auth, _ := authService.GetUserInfo() group := user.UserInfo.Group
group := auth.Group
if ("administrator" != group) { if ("administrator" != group) {
handler.SendResponse(c, errno.ErrUserAuthIncorrect, nil) handler.SendResponse(c, errno.ErrUserAuthIncorrect, nil)
return return
......
...@@ -120,7 +120,6 @@ func AddNews(c *gin.Context) { ...@@ -120,7 +120,6 @@ func AddNews(c *gin.Context) {
Content: news.Content, Content: news.Content,
Top: news.Top, Top: news.Top,
Highlight: news.Highlight, Highlight: news.Highlight,
Status: news.Status,
PlatformId: platform_id, PlatformId: platform_id,
} }
...@@ -157,7 +156,6 @@ func EditNews(c *gin.Context) { ...@@ -157,7 +156,6 @@ func EditNews(c *gin.Context) {
Content: news.Content, Content: news.Content,
Top: news.Top, Top: news.Top,
Highlight: news.Highlight, Highlight: news.Highlight,
Status: news.Status,
PlatformId: platform_id, PlatformId: platform_id,
} }
......
...@@ -29,6 +29,8 @@ func InitRouter() *gin.Engine { ...@@ -29,6 +29,8 @@ func InitRouter() *gin.Engine {
client := r.Group("/interface") client := r.Group("/interface")
client.GET("/news", app.GetNews) client.GET("/news", app.GetNews)
client.GET("/one-news", app.GetOneNews) client.GET("/one-news", app.GetOneNews)
client.GET("/articles", app.GetArticle)
client.GET("/article", app.GetOneArticle)
api := r.Group("/api") api := r.Group("/api")
...@@ -64,6 +66,12 @@ func InitRouter() *gin.Engine { ...@@ -64,6 +66,12 @@ func InitRouter() *gin.Engine {
api.PUT("/news", backend.EditNews) api.PUT("/news", backend.EditNews)
api.DELETE("/news", backend.DeleteNews) api.DELETE("/news", backend.DeleteNews)
api.GET("/articles", backend.GetArticle)
api.POST("/article", backend.AddArticle)
api.GET("/article", backend.GetOneArticle)
api.PUT("/article", backend.EditArticle)
api.DELETE("/article", backend.DeleteArticle)
api.GET("/chains", backend.GetChains) api.GET("/chains", backend.GetChains)
api.POST("/chain", backend.AddChain) api.POST("/chain", backend.AddChain)
api.GET("/chain", backend.GetChain) api.GET("/chain", backend.GetChain)
......
package article_service
import (
"bwallet/models"
"bwallet/pkg/util"
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
)
type Article struct {
Id int
Title string
Source string
LogoPic string
ImgUrl string
Content string
Top uint8
Highlight uint8
PlatformId int
SearchType int
PageNum int
PageSize int
}
func (a *Article) Get() (*models.Article, error) {
article, err := models.GetOneArticle(a.getMaps())
if err != nil {
return nil, err
}
if -1 == article.PlatformId {
/*
https://iapi.bishijie.com/article/list?app_id=8415d8c1c4f762d8&language=1&page=1&size=20&timestamp=1595918443&sign=3b5f70e637187a9b28d35b4cfccce6b8
*/
params := make(map[string]string)
params["app_id"] = "8415d8c1c4f762d8"
params["id"] = article.Content
params["language"] = "1"
params["timestamp"] = strconv.FormatInt(time.Now().UTC().Unix(), 10)
params["sign"] = util.StringToMd5("app_id=8415d8c1c4f762d8&id=" + params["id"] + "&language=1&timestamp=" + params["timestamp"] + "09395dc651588c375d066666e072a714")
req, err := http.NewRequest(http.MethodGet, "https://iapi.bishijie.com/article/detail", nil)
if err != nil {
return nil, err
}
condition := make(url.Values)
condition.Add("app_id", params["app_id"])
condition.Add("id", article.Content)
condition.Add("language", params["language"])
condition.Add("timestamp", params["timestamp"])
condition.Add("sign", params["sign"])
req.URL.RawQuery = condition.Encode()
//fmt.Println(req.URL.String())
r, err := http.DefaultClient.Do(req)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
result := map[string]interface{}{}
json.Unmarshal(body, &result)
article.Content = result["content"].(string)
}
return article, nil
}
func (a *Article) GetAll() ([]*models.Article, error) {
var article []*models.Article
article, err := models.GetArticle(a.PageNum, a.PageSize, a.getMaps())
if err != nil {
return nil, err
}
return article, nil
}
func (a *Article) Add() error {
article := map[string]interface{}{
"title": a.Title,
"source": a.Source,
"logo_pic": a.LogoPic,
"img_url": a.ImgUrl,
"content": a.Content,
"top": a.Top,
"highlight": a.Highlight,
"platform_id": a.PlatformId,
"create_time": time.Now().UTC().Unix(),
}
if err := models.AddArticle(article); err != nil {
return err
}
return nil
}
func (a *Article) Edit() error {
return models.EditArticle(a.Id, map[string]interface{}{
"title": a.Title,
"source": a.Source,
"logo_pic": a.LogoPic,
"content": a.Content,
"top": a.Top,
"highlight": a.Highlight,
"platform_id": a.PlatformId,
})
}
func (a *Article) ExistById() (bool, error) {
return models.ExistArticleById(a.Id)
}
func (a *Article) Count() (int, error) {
return models.GetArticleTotal(a.getMaps())
}
func (a *Article) Delete() error {
return models.DeleteArticle(a.Id)
}
func (a *Article) getMaps() (map[string]interface{}) {
maps := make(map[string]interface{})
if a.Id != 0 {
maps["id"] = a.Id
}
if a.Title != "" {
maps["title"] = a.Title
}
if a.PlatformId != 0 {
maps["platform_id"] = a.PlatformId
}
if a.SearchType != 0 {
platform_id := [] int{a.PlatformId, a.SearchType}
maps["search_type"] = platform_id
}
return maps
}
...@@ -11,7 +11,6 @@ type News struct { ...@@ -11,7 +11,6 @@ type News struct {
Content string Content string
Top uint8 Top uint8
Highlight uint8 Highlight uint8
Status uint8
PlatformId int PlatformId int
SearchType int SearchType int
...@@ -46,7 +45,6 @@ func (n *News) Add() error { ...@@ -46,7 +45,6 @@ func (n *News) Add() error {
"content": n.Content, "content": n.Content,
"top": n.Top, "top": n.Top,
"highlight": n.Highlight, "highlight": n.Highlight,
"status": n.Status,
"platform_id": n.PlatformId, "platform_id": n.PlatformId,
"create_time": time.Now().UTC().Unix(), "create_time": time.Now().UTC().Unix(),
} }
...@@ -64,7 +62,6 @@ func (n *News) Edit() error { ...@@ -64,7 +62,6 @@ func (n *News) Edit() error {
"content": n.Content, "content": n.Content,
"top": n.Top, "top": n.Top,
"highlight": n.Highlight, "highlight": n.Highlight,
"status": n.Status,
"platform_id": n.PlatformId, "platform_id": n.PlatformId,
}) })
} }
...@@ -89,7 +86,7 @@ func (n *News) getMaps() (map[string]interface{}) { ...@@ -89,7 +86,7 @@ func (n *News) getMaps() (map[string]interface{}) {
} }
if n.Title != "" { if n.Title != "" {
maps["name"] = n.Title maps["title"] = n.Title
} }
if n.PlatformId != 0 { if n.PlatformId != 0 {
......
package validate_service
type Article struct {
Title string `json:"title" validate:"required"`
LogoPic string `json:"logo_pic" validate:"required"`
ImgUrl string `json:"img_url" validate:"required"`
Content string `json:"content" validate:"required"`
Source string `json:"source"`
Top uint8 `json:"top"`
Highlight uint8 `json:"highlight"`
PlatformId int `json:"platform_id"`
}
type EditArticle struct {
Article
Id int `json:"id" validate:"required"`
}
...@@ -5,7 +5,6 @@ type News struct { ...@@ -5,7 +5,6 @@ type News struct {
Content string `json:"content" validate:"required"` Content string `json:"content" validate:"required"`
Top uint8 `json:"top"` Top uint8 `json:"top"`
Highlight uint8 `json:"highlight"` Highlight uint8 `json:"highlight"`
Status uint8 `json:"status"`
PlatformId int `json:"platform_id"` PlatformId int `json:"platform_id"`
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment