forked from Open-CT/OpenPBL
fix: bug modify (#12)
* fix: get server url * refactor: fix ui * feat: message list * refactor: rename to web * feat: support redis endpoint * fix: redis session * docs: update README * fix: skip preflight check * docs: update README
This commit is contained in:
parent
8e3c8a7d78
commit
fb330b43f8
|
@ -8,11 +8,11 @@ RUN go env -w CGO_ENABLED=0 GOPROXY=https://goproxy.io,direct GOOS=linux GOARCH=
|
||||||
ENV PATH=$PATH:/openpbl/node-v12.22.0-linux-x64/bin
|
ENV PATH=$PATH:/openpbl/node-v12.22.0-linux-x64/bin
|
||||||
RUN npm install -g yarn
|
RUN npm install -g yarn
|
||||||
|
|
||||||
COPY openpbl-landing/package.json /openpbl/openpbl-landing/package.json
|
COPY web/package.json /openpbl/web/package.json
|
||||||
RUN cd openpbl-landing && yarn install
|
RUN cd web && yarn install
|
||||||
|
|
||||||
COPY openpbl-landing /openpbl/openpbl-landing
|
COPY web /openpbl/web
|
||||||
RUN cd openpbl-landing && yarn build && rm -rf node_modules
|
RUN cd web && yarn build && rm -rf node_modules
|
||||||
|
|
||||||
COPY ./ /openpbl
|
COPY ./ /openpbl
|
||||||
RUN cd /openpbl && go build main.go
|
RUN cd /openpbl && go build main.go
|
||||||
|
|
|
@ -9,8 +9,6 @@ System of PBL.
|
||||||
`web/.env`
|
`web/.env`
|
||||||
|
|
||||||
```dotenv
|
```dotenv
|
||||||
REACT_APP_BASE_URL='http://localhost:5000/api'
|
|
||||||
|
|
||||||
REACT_APP_OSS_REGION='oss-cn-hangzhou'
|
REACT_APP_OSS_REGION='oss-cn-hangzhou'
|
||||||
REACT_APP_OSS_ACCESSKEYID=
|
REACT_APP_OSS_ACCESSKEYID=
|
||||||
REACT_APP_OSS_ACCESSKEYSECRET=
|
REACT_APP_OSS_ACCESSKEYSECRET=
|
||||||
|
@ -23,6 +21,7 @@ REACT_APP_APP_NAME=
|
||||||
REACT_APP_CASDOOR_ORGANIZATION='openct'
|
REACT_APP_CASDOOR_ORGANIZATION='openct'
|
||||||
|
|
||||||
GENERATE_SOURCEMAP=false
|
GENERATE_SOURCEMAP=false
|
||||||
|
SKIP_PREFLIGHT_CHECK=true
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -56,6 +55,7 @@ driverName = mysql
|
||||||
dataSourceName = root:123@tcp(localhost:3306)/
|
dataSourceName = root:123@tcp(localhost:3306)/
|
||||||
dbName = openpbl_db
|
dbName = openpbl_db
|
||||||
|
|
||||||
|
redisEndpoint =
|
||||||
jwtSecret = CasdoorSecret
|
jwtSecret = CasdoorSecret
|
||||||
|
|
||||||
casdoorEndpoint =
|
casdoorEndpoint =
|
||||||
|
|
|
@ -11,6 +11,7 @@ driverName = mysql
|
||||||
dataSourceName = root:123@tcp(localhost:3306)/
|
dataSourceName = root:123@tcp(localhost:3306)/
|
||||||
dbName = openpbl_db
|
dbName = openpbl_db
|
||||||
|
|
||||||
|
redisEndpoint =
|
||||||
jwtSecret = CasdoorSecret
|
jwtSecret = CasdoorSecret
|
||||||
|
|
||||||
casdoorEndpoint =
|
casdoorEndpoint =
|
||||||
|
|
|
@ -3,6 +3,7 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"OpenPBL/models"
|
"OpenPBL/models"
|
||||||
"OpenPBL/util"
|
"OpenPBL/util"
|
||||||
|
"fmt"
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/casdoor/casdoor-go-sdk/auth"
|
"github.com/casdoor/casdoor-go-sdk/auth"
|
||||||
)
|
)
|
||||||
|
@ -36,12 +37,14 @@ type MessagesResponse struct {
|
||||||
// GetUserMessages
|
// GetUserMessages
|
||||||
// @Title
|
// @Title
|
||||||
// @Description
|
// @Description
|
||||||
// @Param type path string true "received sent"
|
// @Param readType params string true "read unread all"
|
||||||
|
// @Param messageType params string true "info warn error all"
|
||||||
// @Param from params int false ""
|
// @Param from params int false ""
|
||||||
// @Param size params int false ""
|
// @Param size params int false ""
|
||||||
|
// @Param orderType params string false "desc asc"
|
||||||
// @Success 200 {object} MessagesResponse
|
// @Success 200 {object} MessagesResponse
|
||||||
// @Failure 400
|
// @Failure 400
|
||||||
// @router /:type [get]
|
// @router / [get]
|
||||||
func (m *MessageController) GetUserMessages() {
|
func (m *MessageController) GetUserMessages() {
|
||||||
user := m.GetSessionUser()
|
user := m.GetSessionUser()
|
||||||
var resp ProjectResponse
|
var resp ProjectResponse
|
||||||
|
@ -62,16 +65,17 @@ func (m *MessageController) GetUserMessages() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
size = 10
|
size = 10
|
||||||
}
|
}
|
||||||
t := m.GetString(":type")
|
orderType := m.GetString("orderType")
|
||||||
|
r := m.GetString("readType")
|
||||||
|
t := m.GetString("messageType")
|
||||||
uid := util.GetUserId(user)
|
uid := util.GetUserId(user)
|
||||||
|
|
||||||
var messages []models.Message
|
var messages []models.Message
|
||||||
var rows int64
|
var rows int64
|
||||||
if t == "received" {
|
messages, rows, err = models.GetMessages(uid, orderType, t, r, from, size)
|
||||||
messages, rows, err = models.GetReceivedMessages(uid, "desc", from, size)
|
|
||||||
} else if t == "sent" {
|
fmt.Println(err)
|
||||||
messages, rows, err = models.GetSentMessages(uid, "desc", from, size)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.Data["json"] = MessagesResponse{
|
m.Data["json"] = MessagesResponse{
|
||||||
Code: 400,
|
Code: 400,
|
||||||
|
@ -85,3 +89,121 @@ func (m *MessageController) GetUserMessages() {
|
||||||
}
|
}
|
||||||
m.ServeJSON()
|
m.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUserMessage
|
||||||
|
// @Title
|
||||||
|
// @Description
|
||||||
|
// @Param messageId path string true ""
|
||||||
|
// @Success 200 {object} Response
|
||||||
|
// @Failure 400
|
||||||
|
// @router /:messageId/read [post]
|
||||||
|
func (m *MessageController) ReadUserMessage() {
|
||||||
|
user := m.GetSessionUser()
|
||||||
|
var resp ProjectResponse
|
||||||
|
if user == nil {
|
||||||
|
resp = ProjectResponse{
|
||||||
|
Code: 401,
|
||||||
|
Msg: "请先登录",
|
||||||
|
}
|
||||||
|
m.Data["json"] = resp
|
||||||
|
m.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := util.GetUserId(user)
|
||||||
|
mid, err := m.GetInt64(":messageId")
|
||||||
|
err = models.ReadMessage(mid, uid)
|
||||||
|
if err != nil {
|
||||||
|
m.Data["json"] = Response{
|
||||||
|
Code: 400,
|
||||||
|
Msg: err.Error(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.Data["json"] = Response{
|
||||||
|
Code: 200,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserMessage
|
||||||
|
// @Title
|
||||||
|
// @Description
|
||||||
|
// @Param messageId path string true ""
|
||||||
|
// @Success 200 {object} Response
|
||||||
|
// @Failure 400
|
||||||
|
// @router /:messageId/delete [post]
|
||||||
|
func (m *MessageController) DeleteUserMessage() {
|
||||||
|
user := m.GetSessionUser()
|
||||||
|
var resp ProjectResponse
|
||||||
|
if user == nil {
|
||||||
|
resp = ProjectResponse{
|
||||||
|
Code: 401,
|
||||||
|
Msg: "请先登录",
|
||||||
|
}
|
||||||
|
m.Data["json"] = resp
|
||||||
|
m.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := util.GetUserId(user)
|
||||||
|
mid, err := m.GetInt64(":messageId")
|
||||||
|
msg := models.Message{
|
||||||
|
Id: mid,
|
||||||
|
ReceiverId: uid,
|
||||||
|
}
|
||||||
|
err = msg.Delete()
|
||||||
|
if err != nil {
|
||||||
|
m.Data["json"] = Response{
|
||||||
|
Code: 400,
|
||||||
|
Msg: err.Error(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.Data["json"] = Response{
|
||||||
|
Code: 200,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAllUserMessage
|
||||||
|
// @Title
|
||||||
|
// @Description
|
||||||
|
// @Success 200 {object} Response
|
||||||
|
// @Failure 400
|
||||||
|
// @router /read-all [post]
|
||||||
|
func (m *MessageController) ReadAllUserMessage() {
|
||||||
|
user := m.GetSessionUser()
|
||||||
|
var resp ProjectResponse
|
||||||
|
if user == nil {
|
||||||
|
resp = ProjectResponse{
|
||||||
|
Code: 401,
|
||||||
|
Msg: "请先登录",
|
||||||
|
}
|
||||||
|
m.Data["json"] = resp
|
||||||
|
m.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := util.GetUserId(user)
|
||||||
|
err := models.ReadAllMessage(uid)
|
||||||
|
if err != nil {
|
||||||
|
m.Data["json"] = Response{
|
||||||
|
Code: 400,
|
||||||
|
Msg: err.Error(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.Data["json"] = Response{
|
||||||
|
Code: 200,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func CreateMessage(msg *models.Message) bool {
|
||||||
|
err := msg.Create()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -16,4 +16,4 @@ services:
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: root
|
MYSQL_ROOT_PASSWORD: root
|
||||||
volumes:
|
volumes:
|
||||||
- /usr/local/docker/openpbl-mysql:/var/lib/mysql
|
- /usr/local/docker/mysql:/var/lib/mysql
|
1
go.sum
1
go.sum
|
@ -126,6 +126,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
|
14
main.go
14
main.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/astaxie/beego/plugins/cors"
|
"github.com/astaxie/beego/plugins/cors"
|
||||||
|
_ "github.com/astaxie/beego/session/redis"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,11 +21,11 @@ func main() {
|
||||||
var err error
|
var err error
|
||||||
configPath := util.GetConfigFile(mode)
|
configPath := util.GetConfigFile(mode)
|
||||||
err = beego.LoadAppConfig("ini", configPath)
|
err = beego.LoadAppConfig("ini", configPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
log.Println("App start with runmode: " + mode)
|
log.Println("App start with runmode: " + mode)
|
||||||
|
log.Println("Load config file: " + configPath)
|
||||||
models.InitAdapter()
|
models.InitAdapter()
|
||||||
controllers.InitCasdoor()
|
controllers.InitCasdoor()
|
||||||
|
|
||||||
|
@ -39,15 +40,20 @@ func main() {
|
||||||
beego.BConfig.WebConfig.DirectoryIndex = true
|
beego.BConfig.WebConfig.DirectoryIndex = true
|
||||||
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
|
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
|
||||||
}
|
}
|
||||||
beego.SetStaticPath("/static", "openpbl-landing/build/static")
|
beego.SetStaticPath("/static", "web/build/static")
|
||||||
beego.BConfig.WebConfig.DirectoryIndex = true
|
beego.BConfig.WebConfig.DirectoryIndex = true
|
||||||
beego.InsertFilter("/", beego.BeforeRouter, routers.TransparentStatic)
|
beego.InsertFilter("/", beego.BeforeRouter, routers.TransparentStatic)
|
||||||
beego.InsertFilter("/*", beego.BeforeRouter, routers.TransparentStatic)
|
beego.InsertFilter("/*", beego.BeforeRouter, routers.TransparentStatic)
|
||||||
|
|
||||||
beego.BConfig.WebConfig.Session.SessionName = "openct_session_id"
|
beego.BConfig.WebConfig.Session.SessionName = "openpbl_session_id"
|
||||||
|
if beego.AppConfig.String("redisEndpoint") == "" {
|
||||||
beego.BConfig.WebConfig.Session.SessionProvider = "file"
|
beego.BConfig.WebConfig.Session.SessionProvider = "file"
|
||||||
beego.BConfig.WebConfig.Session.SessionProviderConfig = "./tmp"
|
beego.BConfig.WebConfig.Session.SessionProviderConfig = "./tmp"
|
||||||
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 365
|
} else {
|
||||||
|
beego.BConfig.WebConfig.Session.SessionProvider = "redis"
|
||||||
|
beego.BConfig.WebConfig.Session.SessionProviderConfig = beego.AppConfig.String("redisEndpoint")
|
||||||
|
}
|
||||||
|
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 30
|
||||||
|
|
||||||
beego.Run()
|
beego.Run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,8 @@ func (a *Adapter) createTable() {
|
||||||
|
|
||||||
new(ProjectSkill),
|
new(ProjectSkill),
|
||||||
new(ProjectSubject),
|
new(ProjectSubject),
|
||||||
|
|
||||||
|
new(Message),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
|
|
@ -6,21 +6,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Id int64 `json:"id" xorm:"not null pk"`
|
Id int64 `json:"id" xorm:"not null pk autoincr"`
|
||||||
ReceiverId string `json:"receiverId" xorm:"not null index"`
|
ReceiverId string `json:"receiverId" xorm:"index"`
|
||||||
|
|
||||||
SenderId string `json:"senderId" xorm:"index"`
|
MessageType string `json:"messageType" xorm:"not null index"` // warn error info
|
||||||
SenderName string `json:"senderName"`
|
|
||||||
|
|
||||||
ProjectId int64 `json:"projectId" xorm:"index"`
|
MessageTitle string `json:"messageTitle" xorm:"text"`
|
||||||
ProjectTitle string `json:"projectTitle"`
|
Content string `json:"content" xorm:"longtext"`
|
||||||
|
|
||||||
MessageType string `json:"messageType" xorm:"not null index"` // remind message
|
|
||||||
Content string `json:"content" xorm:"text"`
|
|
||||||
|
|
||||||
Read bool `json:"read" xorm:"default false index"`
|
Read bool `json:"read" xorm:"default false index"`
|
||||||
|
|
||||||
CreateAt time.Time `json:"CreateAt" xorm:"created"`
|
CreateAt time.Time `json:"createAt" xorm:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,34 +33,50 @@ func (m *Message) Delete() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadMessage(messageId int64) (err error) {
|
func ReadMessage(messageId int64, uid string) (err error) {
|
||||||
_, err = (&Message{}).GetEngine().
|
_, err = (&Message{}).GetEngine().
|
||||||
ID(messageId).
|
ID(messageId).
|
||||||
Update(&Message{
|
Update(&Message{
|
||||||
Id: messageId,
|
Id: messageId,
|
||||||
|
ReceiverId: uid,
|
||||||
|
Read: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func ReadAllMessage(uid string) (err error) {
|
||||||
|
_, err = (&Message{}).GetEngine().
|
||||||
|
Update(&Message{
|
||||||
|
ReceiverId: uid,
|
||||||
Read: true,
|
Read: true,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetReceivedMessages(uid string, orderType string, from int, size int) (m []Message, rows int64, err error) {
|
func GetMessages(uid string, orderType string, messageType string, read string, from int, size int) (m []Message, rows int64, err error) {
|
||||||
err = (&Message{}).GetEngine().
|
s := (&Message{}).GetEngine().
|
||||||
Where("receiver_id = ?", uid).
|
Where("receiver_id = ?", uid)
|
||||||
Desc("create_at").
|
s2 := (&Message{}).GetEngine().
|
||||||
Limit(size, from).
|
Where("receiver_id = ?", uid)
|
||||||
Find(&m)
|
if read == "read" {
|
||||||
rows, err = (&Message{}).GetEngine().
|
s = s.Where("read = true")
|
||||||
Count(&Message{ReceiverId: uid})
|
s2 = s2.Where("read = true")
|
||||||
return
|
} else if read == "unread" {
|
||||||
|
s = s.Where("read = false")
|
||||||
|
s2 = s2.Where("read = false")
|
||||||
|
}
|
||||||
|
if messageType == "error" || messageType == "info" || messageType == "warn" {
|
||||||
|
s = s.Where("message_type = ?", messageType)
|
||||||
|
s2 = s2.Where("message_type = ?", messageType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSentMessages(uid string, orderType string, from int, size int) (m []Message, rows int64, err error) {
|
rows, err = s2.Count()
|
||||||
err = (&Message{}).GetEngine().
|
|
||||||
Where("sender_id = ?", uid).
|
if orderType == "asc" {
|
||||||
Desc("create_at").
|
s = s.Asc("create_at")
|
||||||
Limit(size, from).
|
} else {
|
||||||
Find(&m)
|
s = s.Desc("create_at")
|
||||||
rows, err = (&Message{}).GetEngine().
|
}
|
||||||
Count(&Message{SenderId: uid})
|
|
||||||
|
err = s.Limit(size, from).Find(&m)
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -37,7 +37,7 @@ func init() {
|
||||||
beego.GlobalControllerRouter["OpenPBL/controllers:MessageController"] = append(beego.GlobalControllerRouter["OpenPBL/controllers:MessageController"],
|
beego.GlobalControllerRouter["OpenPBL/controllers:MessageController"] = append(beego.GlobalControllerRouter["OpenPBL/controllers:MessageController"],
|
||||||
beego.ControllerComments{
|
beego.ControllerComments{
|
||||||
Method: "GetUserMessages",
|
Method: "GetUserMessages",
|
||||||
Router: "/:type",
|
Router: "/",
|
||||||
AllowHTTPMethods: []string{"get"},
|
AllowHTTPMethods: []string{"get"},
|
||||||
MethodParams: param.Make(),
|
MethodParams: param.Make(),
|
||||||
Filters: nil,
|
Filters: nil,
|
||||||
|
|
|
@ -13,7 +13,7 @@ func TransparentStatic(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
path := "openpbl-landing/build"
|
path := "web/build"
|
||||||
if urlPath == "/" {
|
if urlPath == "/" {
|
||||||
path += "/index.html"
|
path += "/index.html"
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,7 +21,7 @@ func TransparentStatic(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, "openpbl-landing/build/index.html")
|
http.ServeFile(ctx.ResponseWriter, ctx.Request, "web/build/index.html")
|
||||||
} else {
|
} else {
|
||||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, path)
|
http.ServeFile(ctx.ResponseWriter, ctx.Request, path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,10 @@ func init() {
|
||||||
beego.NSInclude(
|
beego.NSInclude(
|
||||||
&controllers.StudentController{})),
|
&controllers.StudentController{})),
|
||||||
|
|
||||||
|
beego.NSNamespace("/message",
|
||||||
|
beego.NSInclude(
|
||||||
|
&controllers.MessageController{})),
|
||||||
|
|
||||||
)
|
)
|
||||||
beego.AddNamespace(ns)
|
beego.AddNamespace(ns)
|
||||||
}
|
}
|
||||||
|
|
3
web/.env
3
web/.env
|
@ -1,5 +1,3 @@
|
||||||
REACT_APP_BASE_URL='http://localhost:5000/api'
|
|
||||||
|
|
||||||
REACT_APP_OSS_REGION='oss-cn-hangzhou'
|
REACT_APP_OSS_REGION='oss-cn-hangzhou'
|
||||||
REACT_APP_OSS_ACCESSKEYID=
|
REACT_APP_OSS_ACCESSKEYID=
|
||||||
REACT_APP_OSS_ACCESSKEYSECRET=
|
REACT_APP_OSS_ACCESSKEYSECRET=
|
||||||
|
@ -12,3 +10,4 @@ REACT_APP_APP_NAME=
|
||||||
REACT_APP_CASDOOR_ORGANIZATION='openct'
|
REACT_APP_CASDOOR_ORGANIZATION='openct'
|
||||||
|
|
||||||
GENERATE_SOURCEMAP=false
|
GENERATE_SOURCEMAP=false
|
||||||
|
SKIP_PREFLIGHT_CHECK=true
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "openpbl-landing",
|
"name": "web",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import request from "./request";
|
||||||
|
import qs from 'qs'
|
||||||
|
|
||||||
|
const MessageApi = {
|
||||||
|
getUserMessages(query) {
|
||||||
|
return request({
|
||||||
|
url: `/message`,
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
},
|
||||||
|
readMessage(m) {
|
||||||
|
return request({
|
||||||
|
url: `/message/${m.id}/read`,
|
||||||
|
method: 'post',
|
||||||
|
data: qs.stringify(m)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
readAllMessage() {
|
||||||
|
return request({
|
||||||
|
url: `/message/read-all`,
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteMessage(m) {
|
||||||
|
return request({
|
||||||
|
url: `/message/${m.id}/delete`,
|
||||||
|
method: 'post',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MessageApi
|
|
@ -1,8 +1,16 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {message} from 'antd'
|
import {message} from 'antd'
|
||||||
|
|
||||||
|
function getServerUrl() {
|
||||||
|
const hostname = window.location.hostname
|
||||||
|
if (hostname === 'localhost') {
|
||||||
|
return `http://${hostname}:5000/api`
|
||||||
|
}
|
||||||
|
return '/api'
|
||||||
|
}
|
||||||
|
|
||||||
const request = axios.create({
|
const request = axios.create({
|
||||||
baseURL: process.env.REACT_APP_BASE_URL,
|
baseURL: getServerUrl(),
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
withCredentials: true
|
withCredentials: true
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,9 +1,41 @@
|
||||||
import React from "react";
|
import React, {useState} from "react";
|
||||||
|
|
||||||
|
|
||||||
|
import MessageList from "../component/MessageList";
|
||||||
|
import MessageApi from "../../../api/MessageApi"
|
||||||
|
|
||||||
function AllMessage() {
|
function AllMessage() {
|
||||||
|
const [messages, setMessages] = useState([])
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [total, setTotal] = useState(0)
|
||||||
|
|
||||||
|
const updateMessageList = (from, size) => {
|
||||||
|
setLoading(true)
|
||||||
|
const query = {
|
||||||
|
readType: 'all',
|
||||||
|
messageType: 'all',
|
||||||
|
from: from,
|
||||||
|
size: size,
|
||||||
|
orderType: 'desc'
|
||||||
|
}
|
||||||
|
MessageApi.getUserMessages(query)
|
||||||
|
.then(res=>{
|
||||||
|
setLoading(false)
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
setMessages(res.data.messages)
|
||||||
|
setTotal(res.data.count)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e=>{console.log(e)})
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
all message
|
<MessageList
|
||||||
|
loading={loading}
|
||||||
|
total={total}
|
||||||
|
updateMessageList={updateMessageList}
|
||||||
|
messages={messages}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import React from "react";
|
||||||
|
import MessageList from "../component/MessageList";
|
||||||
|
|
||||||
|
function Communication() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default Communication
|
|
@ -0,0 +1,77 @@
|
||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import {Button, Pagination, Table} from "antd";
|
||||||
|
|
||||||
|
import util from "../../component/Util"
|
||||||
|
|
||||||
|
function MessageList(obj) {
|
||||||
|
const [page, setPage] = useState(1)
|
||||||
|
const [pageSize, setPageSize] = useState(10)
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
obj.updateMessageList(page - 1, pageSize);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const changePage = p => {
|
||||||
|
setPage(p)
|
||||||
|
obj.updateMessageList((p-1)*pageSize, pageSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Table
|
||||||
|
loading={obj.loading}
|
||||||
|
dataSource={obj.messages}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '标题',
|
||||||
|
dataIndex: 'messageTitle',
|
||||||
|
key: 'messageTitle'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '时间',
|
||||||
|
dataIndex: 'createAt',
|
||||||
|
key: 'createAt',
|
||||||
|
render: (text, item, index) => (
|
||||||
|
<>
|
||||||
|
{util.FilterMoment(text)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'messageType',
|
||||||
|
key: 'messageType',
|
||||||
|
render: (text, item, index) => (
|
||||||
|
<>
|
||||||
|
{text === 'warning' ? '警告': null}
|
||||||
|
{text === 'info' ? '信息': null}
|
||||||
|
{text === 'error' ? '错误': null}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'active',
|
||||||
|
key: 'active',
|
||||||
|
render: (text, item, index) => (
|
||||||
|
<>
|
||||||
|
<Button>未读</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
pagination={false}
|
||||||
|
/>
|
||||||
|
<Pagination
|
||||||
|
total={obj.total}
|
||||||
|
showTotal={t => `共${obj.total}条消息`}
|
||||||
|
current={page}
|
||||||
|
onChange={changePage}
|
||||||
|
onShowSizeChange={()=>obj.updateMessageList(page-1, pageSize)}
|
||||||
|
style={{margin: '20px', textAlign: 'right'}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MessageList
|
|
@ -1,12 +1,12 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import DocumentTitle from "react-document-title";
|
import DocumentTitle from "react-document-title";
|
||||||
import {Affix, Layout, Menu} from "antd";
|
import {Affix, Layout, Menu} from "antd";
|
||||||
|
|
||||||
import {Link, Redirect, Route, Switch} from "react-router-dom";
|
import {Link, Redirect, Route, Switch} from "react-router-dom";
|
||||||
|
|
||||||
import AllMessage from "./AllMessage";
|
import AllMessage from "./AllMessage";
|
||||||
import UnreadMessage from "./UnreadMessage";
|
import UnreadMessage from "./UnreadMessage";
|
||||||
import ReadMessage from "./ReadMessage";
|
import ReadMessage from "./ReadMessage";
|
||||||
|
import Communication from "./Communication";
|
||||||
|
|
||||||
class Message extends React.Component {
|
class Message extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
|
@ -32,12 +32,20 @@ class Message extends React.Component {
|
||||||
未读消息
|
未读消息
|
||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="read-message">已读消息</Menu.Item>
|
<Menu.Item key="read-message">
|
||||||
<Menu.Item key="remind">留言沟通</Menu.Item>
|
<Link to="/message/read">
|
||||||
|
已读消息
|
||||||
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="remind">
|
||||||
|
<Link to="/message/communication">
|
||||||
|
留言沟通
|
||||||
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Layout.Sider>
|
</Layout.Sider>
|
||||||
</Affix>
|
</Affix>
|
||||||
<Layout.Content style={{backgroundColor: 'white'}}>
|
<Layout.Content style={{backgroundColor: 'white', marginLeft: '10px'}}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/message" render={() => (
|
<Route exact path="/message" render={() => (
|
||||||
<Redirect to="/message/all"/>
|
<Redirect to="/message/all"/>
|
||||||
|
@ -45,6 +53,7 @@ class Message extends React.Component {
|
||||||
<Route exact path="/message/all" component={AllMessage}/>
|
<Route exact path="/message/all" component={AllMessage}/>
|
||||||
<Route exact path="/message/unread" component={UnreadMessage}/>
|
<Route exact path="/message/unread" component={UnreadMessage}/>
|
||||||
<Route exact path="/message/read" component={ReadMessage}/>
|
<Route exact path="/message/read" component={ReadMessage}/>
|
||||||
|
<Route exact path="/message/communication" component={Communication}/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {Link} from 'react-router-dom'
|
||||||
import ChapterApi from "../../../../../api/ChapterApi"
|
import ChapterApi from "../../../../../api/ChapterApi"
|
||||||
import SectionApi from "../../../../../api/SectionApi"
|
import SectionApi from "../../../../../api/SectionApi"
|
||||||
|
|
||||||
import util from "../../../component/Util"
|
import util from "../../../../component/Util"
|
||||||
|
|
||||||
const {SubMenu} = Menu;
|
const {SubMenu} = Menu;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {Button, Collapse, Divider, InputNumber, List, message, Progress, Tooltip
|
||||||
import TaskApi from "../../../../api/TaskApi";
|
import TaskApi from "../../../../api/TaskApi";
|
||||||
import ChapterApi from "../../../../api/ChapterApi";
|
import ChapterApi from "../../../../api/ChapterApi";
|
||||||
import TaskCard from "../../PreviewProject/component/TaskCard";
|
import TaskCard from "../../PreviewProject/component/TaskCard";
|
||||||
import util from "../../component/Util"
|
import util from "../../../component/Util"
|
||||||
import SubmitApi from "../../../../api/SubmitApi";
|
import SubmitApi from "../../../../api/SubmitApi";
|
||||||
|
|
||||||
function StudentEvidence(obj) {
|
function StudentEvidence(obj) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ class MyProject extends React.PureComponent {
|
||||||
const {type} = this.state
|
const {type} = this.state
|
||||||
return (
|
return (
|
||||||
<DocumentTitle title="My Project">
|
<DocumentTitle title="My Project">
|
||||||
<Layout>
|
<Layout style={{margin: '20px'}}>
|
||||||
<Affix offsetTop={0}>
|
<Affix offsetTop={0}>
|
||||||
<Layout.Sider
|
<Layout.Sider
|
||||||
breakpoint="lg"
|
breakpoint="lg"
|
||||||
|
@ -95,7 +95,7 @@ class MyProject extends React.PureComponent {
|
||||||
}
|
}
|
||||||
</Layout.Sider>
|
</Layout.Sider>
|
||||||
</Affix>
|
</Affix>
|
||||||
<Layout.Content>
|
<Layout.Content style={{marginLeft: '10px'}}>
|
||||||
{type === 'teacher' ?
|
{type === 'teacher' ?
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/my-project" render={() => (
|
<Route exact path="/my-project" render={() => (
|
||||||
|
|
|
@ -6,7 +6,7 @@ import SectionApi from "../../../api/SectionApi";
|
||||||
import "../CreateProject/Section/component/section-edit.less"
|
import "../CreateProject/Section/component/section-edit.less"
|
||||||
import "./preview.less"
|
import "./preview.less"
|
||||||
import TaskApi from "../../../api/TaskApi";
|
import TaskApi from "../../../api/TaskApi";
|
||||||
import util from "../component/Util"
|
import util from "../../component/Util"
|
||||||
import StudentApi from "../../../api/StudentApi";
|
import StudentApi from "../../../api/StudentApi";
|
||||||
import TaskCard from "./component/TaskCard";
|
import TaskCard from "./component/TaskCard";
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import TaskApi from "../../../../api/TaskApi";
|
||||||
import ProjectApi from "../../../../api/ProjectApi";
|
import ProjectApi from "../../../../api/ProjectApi";
|
||||||
import ChapterApi from "../../../../api/ChapterApi";
|
import ChapterApi from "../../../../api/ChapterApi";
|
||||||
import SectionApi from "../../../../api/SectionApi";
|
import SectionApi from "../../../../api/SectionApi";
|
||||||
import util from "../../component/Util"
|
import util from "../../../component/Util"
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const {SubMenu} = Menu
|
const {SubMenu} = Menu
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {Menu, message, Progress} from 'antd'
|
||||||
import QueueAnim from 'rc-queue-anim';
|
import QueueAnim from 'rc-queue-anim';
|
||||||
|
|
||||||
import ChapterApi from "../../../../api/ChapterApi";
|
import ChapterApi from "../../../../api/ChapterApi";
|
||||||
import util from "../../component/Util"
|
import util from "../../../component/Util"
|
||||||
|
|
||||||
const {SubMenu} = Menu;
|
const {SubMenu} = Menu;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {Avatar, Button, message, Pagination, Popconfirm, Table} from "antd";
|
||||||
import {DeleteOutlined} from "@ant-design/icons"
|
import {DeleteOutlined} from "@ant-design/icons"
|
||||||
|
|
||||||
import ProjectApi from "../../../../api/ProjectApi";
|
import ProjectApi from "../../../../api/ProjectApi";
|
||||||
import util from "../../component/Util"
|
import util from "../../../component/Util"
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
|
|
||||||
function StudentAdmin(obj) {
|
function StudentAdmin(obj) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import StudentApi from "../../../api/StudentApi";
|
||||||
import StudentAdmin from "./component/StudentAdmin";
|
import StudentAdmin from "./component/StudentAdmin";
|
||||||
import {getUser} from "../../User/Auth/Auth";
|
import {getUser} from "../../User/Auth/Auth";
|
||||||
import {DeleteOutlined} from "@ant-design/icons";
|
import {DeleteOutlined} from "@ant-design/icons";
|
||||||
import util from "../component/Util"
|
import util from "../../component/Util"
|
||||||
import StudentEvidence from "../Evidence/component/StudentEvidence";
|
import StudentEvidence from "../Evidence/component/StudentEvidence";
|
||||||
|
|
||||||
class ProjectInfo extends React.PureComponent {
|
class ProjectInfo extends React.PureComponent {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {Link} from 'react-router-dom';
|
||||||
|
|
||||||
import './project-list.less';
|
import './project-list.less';
|
||||||
import ProjectListApi from '../../../api/ProjectListApi'
|
import ProjectListApi from '../../../api/ProjectListApi'
|
||||||
import util from './Util'
|
import util from '../../component/Util'
|
||||||
|
|
||||||
const {Meta} = Card;
|
const {Meta} = Card;
|
||||||
const {Search} = Input;
|
const {Search} = Input;
|
||||||
|
|
Loading…
Reference in New Issue