提交代码
This commit is contained in:
parent
40bf977f5b
commit
153ae31ea8
|
@ -1,12 +1,15 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"bbs/app/provider/database_connect"
|
||||
"github.com/Superdanda/hade/framework"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
)
|
||||
|
||||
// NewHttpEngine 创建了一个绑定了路由的Web引擎
|
||||
func NewHttpEngine(container *framework.HadeContainer) (*gin.Engine, error) {
|
||||
//绑定服务
|
||||
container.Bind(&database_connect.DatabaseConnectProvider{})
|
||||
// 设置为Release,为的是默认在启动中不输出调试信息
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
// 默认启动一个Web引擎
|
||||
|
@ -15,5 +18,9 @@ func NewHttpEngine(container *framework.HadeContainer) (*gin.Engine, error) {
|
|||
// 业务绑定路由操作
|
||||
Routes(r)
|
||||
// 返回绑定路由后的Web引擎
|
||||
|
||||
// 对业务模型进行注册,通过注册名获取业务模型类型信息
|
||||
TypeRegister(container)
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
)
|
||||
|
||||
func ResponseHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
fmt.Println("response handler")
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
package demo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"github.com/Superdanda/hade/framework/provider/orm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DemoApi struct {
|
||||
service *Service
|
||||
}
|
||||
|
||||
func Register(r *gin.Engine) error {
|
||||
api := NewDemoApi()
|
||||
//r.Bind(&demoService.DemoProvider{})
|
||||
|
||||
r.GET("/demo/demo", api.Demo)
|
||||
r.GET("/demo/demo2", api.Demo2)
|
||||
r.GET("/demo/orm", api.orm)
|
||||
r.POST("/demo/demo_post", api.DemoPost)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewDemoApi() *DemoApi {
|
||||
service := NewService()
|
||||
return &DemoApi{service: service}
|
||||
}
|
||||
|
||||
func Demo(c *gin.Context) {
|
||||
configService := c.MustMake(contract.ConfigKey).(contract.Config)
|
||||
log := c.MustMake(contract.LogKey).(contract.Log)
|
||||
password := configService.GetString("database.mysql.password")
|
||||
log.Info(c, "ceshiceshi", map[string]interface{}{})
|
||||
c.JSON(200, password+"后端测试222")
|
||||
}
|
||||
|
||||
// Demo godoc
|
||||
// @Summary 获取所有用户
|
||||
// @tag.description.markdown demo.md
|
||||
// @Produce json
|
||||
// @Tags demo
|
||||
// @Success 200 array []UserDTO
|
||||
// @Router /demo/demo [get]
|
||||
func (api *DemoApi) Demo(c *gin.Context) {
|
||||
c.JSON(200, "this is demo for dev all")
|
||||
}
|
||||
|
||||
func (api *DemoApi) orm(c *gin.Context) {
|
||||
logger := c.MustMakeLog()
|
||||
logger.Info(c, "request start", nil)
|
||||
|
||||
// 初始化一个orm.DB
|
||||
gormService := c.MustMake(contract.ORMKey).(contract.ORMService)
|
||||
db, err := gormService.GetDB(orm.WithConfigPath("database.default"))
|
||||
if err != nil {
|
||||
logger.Error(c, err.Error(), nil)
|
||||
c.AbortWithError(50001, err)
|
||||
return
|
||||
}
|
||||
db.WithContext(c)
|
||||
|
||||
// 将User模型创建到数据库中
|
||||
err = db.AutoMigrate(&User{})
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
logger.Info(c, "migrate ok", nil)
|
||||
|
||||
// 插入一条数据
|
||||
email := "foo@gmail.com"
|
||||
name := "foo"
|
||||
age := uint8(25)
|
||||
birthday := time.Date(2001, 1, 1, 1, 1, 1, 1, time.Local)
|
||||
user := &User{
|
||||
Name: name,
|
||||
Email: &email,
|
||||
Age: age,
|
||||
Birthday: &birthday,
|
||||
MemberNumber: sql.NullString{},
|
||||
ActivatedAt: sql.NullTime{},
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
err = db.Create(user).Error
|
||||
logger.Info(c, "insert user", map[string]interface{}{
|
||||
"id": user.ID,
|
||||
"err": err,
|
||||
})
|
||||
|
||||
// 更新一条数据
|
||||
user.Name = "bar"
|
||||
err = db.Save(user).Error
|
||||
logger.Info(c, "update user", map[string]interface{}{
|
||||
"err": err,
|
||||
"id": user.ID,
|
||||
})
|
||||
|
||||
// 查询一条数据
|
||||
queryUser := &User{ID: user.ID}
|
||||
|
||||
err = db.First(queryUser).Error
|
||||
logger.Info(c, "query user", map[string]interface{}{
|
||||
"err": err,
|
||||
"name": queryUser.Name,
|
||||
})
|
||||
|
||||
// 删除一条数据
|
||||
//err = db.Delete(queryUser).Error
|
||||
//logger.Info(c, "delete user", map[string]interface{}{
|
||||
// "err": err,
|
||||
// "id": user.ID,
|
||||
//})
|
||||
c.JSON(200, "ok")
|
||||
}
|
||||
|
||||
// Demo2 for godoc
|
||||
// @Summary 获取所有学生
|
||||
// @Description 获取所有学生,不进行分页
|
||||
// @Produce json
|
||||
// @Tags demo
|
||||
// @Success 200 {array} UserDTO
|
||||
// @Router /demo/demo2 [get]
|
||||
func (api *DemoApi) Demo2(c *gin.Context) {
|
||||
//demoProvider := c.MustMake(demoService.DemoKey).(demoService.IService)
|
||||
//students := demoProvider.GetAllStudent()
|
||||
//usersDTO := StudentsToUserDTOs(students)
|
||||
c.JSON(200, "usersDTO")
|
||||
}
|
||||
|
||||
func (api *DemoApi) DemoPost(c *gin.Context) {
|
||||
type Foo struct {
|
||||
Name string
|
||||
}
|
||||
foo := &Foo{}
|
||||
err := c.BindJSON(&foo)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, err)
|
||||
}
|
||||
c.JSON(200, nil)
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package demo
|
||||
|
||||
type UserDTO struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package demo
|
|
@ -1,25 +0,0 @@
|
|||
package demo
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UserModel struct {
|
||||
UserId int
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// User is gorm model
|
||||
type User struct {
|
||||
ID uint
|
||||
Name string
|
||||
Email *string
|
||||
Age uint8
|
||||
Birthday *time.Time
|
||||
MemberNumber sql.NullString
|
||||
ActivatedAt sql.NullTime
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package demo
|
|
@ -1,19 +0,0 @@
|
|||
package demo
|
||||
|
||||
type Service struct {
|
||||
//repository *Repository
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
//repository := NewRepository()
|
||||
return &Service{
|
||||
//repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
//}
|
||||
//
|
||||
//func (s *Service) GetUsers() []UserModel {
|
||||
// ids := s.repository.GetUserIds()
|
||||
// return s.repository.GetUserByIds(ids)
|
||||
//}
|
|
@ -0,0 +1,33 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
)
|
||||
|
||||
type QaApi struct{}
|
||||
|
||||
// 注册路由
|
||||
func RegisterRoutes(r *gin.Engine) error {
|
||||
api := QaApi{}
|
||||
if !r.IsBind(qa.QaKey) {
|
||||
r.Bind(&qa.QaProvider{})
|
||||
}
|
||||
Group := r.Group("/")
|
||||
{
|
||||
questionGroup := Group.Group("/question")
|
||||
{
|
||||
questionGroup.POST("/create", api.QuestionCreate)
|
||||
questionGroup.POST("/list", api.QuestionList)
|
||||
questionGroup.POST("/detail", api.QuestionDetail)
|
||||
questionGroup.POST("/delete", api.QuestionDelete)
|
||||
questionGroup.POST("/edit", api.QuestionEdit)
|
||||
}
|
||||
answerGroup := Group.Group("/answer")
|
||||
{
|
||||
answerGroup.POST("/create", api.AnswerCreate)
|
||||
answerGroup.POST("/delete", api.AnswerDelete)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/middleware/auth"
|
||||
"bbs/app/http/result"
|
||||
provider "bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type answerCreateParam struct {
|
||||
QuestionID int64 `json:"questionId" binding:"required"`
|
||||
Context string `json:"context" binding:"required"`
|
||||
}
|
||||
|
||||
// AnswerCreate 代表创建回答
|
||||
// @Summary 创建回答
|
||||
// @Description 创建回答
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags qa
|
||||
// @Param answerCreateParam body answerCreateParam true "创建回答参数"
|
||||
// @Success 200 string Msg "操作成功"
|
||||
// @Router /answer/create [post]
|
||||
func (api *QaApi) AnswerCreate(c *gin.Context) {
|
||||
qaService := c.MustMake(provider.QaKey).(provider.Service)
|
||||
|
||||
param := &answerCreateParam{}
|
||||
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
user := auth.GetAuthUser(c)
|
||||
if user == nil {
|
||||
c.ISetStatus(http.StatusUnauthorized).IJson(result.Fail("登录后再操作"))
|
||||
return
|
||||
}
|
||||
|
||||
newAnswer := provider.NewAnswer(param.QuestionID, param.Context, user.ID)
|
||||
|
||||
err := qaService.PostAnswer(c, newAnswer)
|
||||
if err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("网络开小差,稍后再试"))
|
||||
return
|
||||
}
|
||||
|
||||
c.ISetOkStatus().IJson(result.SuccessWithOKMessage())
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/middleware/auth"
|
||||
"bbs/app/http/result"
|
||||
provider "bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type answerDeleteParam struct {
|
||||
AnswerID int64 `json:"answerId" binding:"required"`
|
||||
}
|
||||
|
||||
// AnswerDelete 代表删除回答
|
||||
// @Summary 创建回答
|
||||
// @Description 创建回答
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags qa
|
||||
// @Param answerDeleteParam body answerDeleteParam true "删除id"
|
||||
// @Success 200 string Msg "操作成功"
|
||||
// @Router /answer/delete [post]
|
||||
func (api *QaApi) AnswerDelete(c *gin.Context) {
|
||||
qaService := c.MustMake(provider.QaKey).(provider.Service)
|
||||
|
||||
param := &answerDeleteParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
user := auth.GetAuthUser(c)
|
||||
answerId := param.AnswerID
|
||||
answer, err := qaService.GetAnswer(c, answerId)
|
||||
if err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("数据不存在"))
|
||||
return
|
||||
}
|
||||
if answer.AuthorID != user.ID {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("没有权限做此操作"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := qaService.DeleteAnswer(c, answerId); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
c.ISetOkStatus().IJson(result.SuccessWithOKMessage())
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/middleware/auth"
|
||||
"bbs/app/http/result"
|
||||
provider "bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type questionCreateParam struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
|
||||
// QuestionCreate 代表创建问题
|
||||
// @Summary 创建问题
|
||||
// @Description 创建问题
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags qa
|
||||
// @Param questionCreateParam body questionCreateParam true "创建问题参数"
|
||||
// @Success 200 string Msg "操作成功"
|
||||
// @Router /question/create [post]
|
||||
func (api *QaApi) QuestionCreate(c *gin.Context) {
|
||||
qaService := c.MustMake(provider.QaKey).(provider.Service)
|
||||
|
||||
param := &questionCreateParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
user := auth.GetAuthUser(c)
|
||||
if user == nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("没有权限做此操作"))
|
||||
return
|
||||
}
|
||||
|
||||
question := provider.NewQuestion(param.Title, param.Content, user.ID)
|
||||
|
||||
if err := qaService.PostQuestion(c, question); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.ISetOkStatus().IJson(result.SuccessWithOKMessage())
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/middleware/auth"
|
||||
"bbs/app/http/result"
|
||||
provider "bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type questionDeleteParam struct {
|
||||
QuestionID int64 `json:"questionId" binding:"required"`
|
||||
}
|
||||
|
||||
// QuestionDelete 删除问题
|
||||
// @Summary 删除问题
|
||||
// @Description 删除问题,同时删除问题中的所有答案
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags qa
|
||||
// @Param questionDeleteParam body questionDeleteParam true "删除id"
|
||||
// @Success 200 string Msg "操作成功"
|
||||
// @Router /question/delete [post]
|
||||
func (api *QaApi) QuestionDelete(c *gin.Context) {
|
||||
qaService := c.MustMake(provider.QaKey).(provider.Service)
|
||||
|
||||
param := &questionDeleteParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
question, err := qaService.GetQuestion(c, param.QuestionID)
|
||||
if err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("数据不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
user := auth.GetAuthUser(c)
|
||||
if user.ID != question.AuthorID {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("没有权限做此操作"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := qaService.DeleteQuestion(c, question.ID); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
c.ISetOkStatus().IJson(result.SuccessWithOKMessage())
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/result"
|
||||
provider "bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type questionDetailParam struct {
|
||||
QuestionID int64 `json:"questionId" binding:"required"`
|
||||
}
|
||||
|
||||
// QuestionDetail 获取问题详情
|
||||
// @Summary 获取问题详细
|
||||
// @Description 获取问题详情,包括问题的所有回答
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags qa
|
||||
// @Param questionDetailParam body questionDetailParam true "问题id"
|
||||
// @Success 200 QuestionDTO question "问题详情,带回答和作者"
|
||||
// @Router /question/detail [post]
|
||||
func (api *QaApi) QuestionDetail(c *gin.Context) {
|
||||
qaService := c.MustMake(provider.QaKey).(provider.Service)
|
||||
|
||||
param := &questionDetailParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 获取问题详情
|
||||
question, err := qaService.GetQuestion(c, param.QuestionID)
|
||||
if err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("数据不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 加载问题作者
|
||||
if err := qaService.QuestionLoadAuthor(c, question); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("网络开小差"))
|
||||
return
|
||||
}
|
||||
// 加载所有答案
|
||||
if err := qaService.QuestionLoadAnswers(c, question); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("网络开小差"))
|
||||
return
|
||||
}
|
||||
// 加载答案的所有作者
|
||||
if err := qaService.AnswersLoadAuthor(c, &question.Answers); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("网络开小差"))
|
||||
return
|
||||
}
|
||||
|
||||
// 输出转换
|
||||
questionDTO := ConvertQuestionToDTO(question, nil)
|
||||
|
||||
c.ISetOkStatus().IJson(questionDTO)
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/middleware/auth"
|
||||
"bbs/app/http/result"
|
||||
provider "bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type questionEditParam struct {
|
||||
ID int64 `json:"id" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
|
||||
// QuestionEdit 编辑问题
|
||||
// @Summary 编辑问题
|
||||
// @Description 编辑问题
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags qa
|
||||
// @Param questionEditParam body questionEditParam true "编辑问题参数"
|
||||
// @Success 200 string Msg "操作成功"
|
||||
// @Router /question/edit [post]
|
||||
func (api *QaApi) QuestionEdit(c *gin.Context) {
|
||||
qaService := c.MustMake(provider.QaKey).(provider.Service)
|
||||
|
||||
param := &questionEditParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
questionOld, err := qaService.GetQuestion(c, param.ID)
|
||||
if err != nil || questionOld == nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("操作的问题不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
user := auth.GetAuthUser(c)
|
||||
if user == nil || user.ID != questionOld.AuthorID {
|
||||
c.ISetStatus(http.StatusUnauthorized).IJson(result.Fail("无权限操作"))
|
||||
return
|
||||
}
|
||||
|
||||
question := &provider.Question{
|
||||
ID: param.ID,
|
||||
Title: param.Title,
|
||||
Context: param.Content,
|
||||
}
|
||||
if err := qaService.UpdateQuestion(c, question); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.ISetOkStatus().IJson(result.SuccessWithOKMessage())
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/result"
|
||||
provider "bbs/app/provider/qa"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type questionListParam struct {
|
||||
Start int `json:"start" binding:"required"`
|
||||
Size int `json:"size" binding:"required"`
|
||||
}
|
||||
|
||||
// QuestionList 获取问题列表
|
||||
// @Summary 获取问题列表
|
||||
// @Description 获取问题列表,包含作者信息,不包含回答
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags qa
|
||||
// @Param questionListParam body questionListParam true "分页查询的参数"
|
||||
// @Success 200 {array} qa.QuestionDTO "问题列表"
|
||||
// @Router /question/list [post]
|
||||
func (api *QaApi) QuestionList(c *gin.Context) {
|
||||
qaService := c.MustMake(provider.QaKey).(provider.Service)
|
||||
|
||||
param := &questionListParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
logger := c.MustMakeLog()
|
||||
pager := provider.Pager{
|
||||
Start: param.Start,
|
||||
Size: param.Size,
|
||||
}
|
||||
|
||||
logger.Debug(c, "get param", map[string]interface{}{
|
||||
"pager": pager,
|
||||
})
|
||||
|
||||
questions, err := qaService.GetQuestions(c, &pager)
|
||||
if err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
if len(questions) == 0 {
|
||||
c.ISetOkStatus().IJson([]*QuestionDTO{})
|
||||
return
|
||||
}
|
||||
|
||||
if err := qaService.QuestionsLoadAuthor(c, &questions); err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
questionsDTO := ConvertQuestionsToDTO(questions)
|
||||
|
||||
c.ISetOkStatus().IJson(questionsDTO)
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/module/user"
|
||||
"time"
|
||||
)
|
||||
|
||||
// QuestionDTO 问题列表返回结构
|
||||
type QuestionDTO struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Context string `json:"context,omitempty"` // 在列表页,只显示前200个字符
|
||||
AnswerNum int `json:"answer_num"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
Author *user.UserDTO `json:"author,omitempty"` // 作者
|
||||
Answers []*AnswerDTO `json:"answers,omitempty"` // 回答
|
||||
}
|
||||
|
||||
// AnswerDTO 回答返回结构
|
||||
type AnswerDTO struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
Author *user.UserDTO `json:"author,omitempty"` // 作者
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/http/module/user"
|
||||
"bbs/app/provider/qa"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ConvertAnswerToDTO(answer *qa.Answer) *AnswerDTO {
|
||||
if answer == nil {
|
||||
return nil
|
||||
}
|
||||
author := user.ConvertUserToDTO(answer.Author)
|
||||
if author == nil {
|
||||
author = &user.UserDTO{
|
||||
ID: answer.AuthorID,
|
||||
}
|
||||
}
|
||||
return &AnswerDTO{
|
||||
ID: answer.ID,
|
||||
Content: answer.Context,
|
||||
CreatedAt: answer.CreatedAt,
|
||||
UpdatedAt: answer.UpdatedAt,
|
||||
Author: author,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertAnswersToDTO 将answers转化为带有tree结构的AnswerDTO
|
||||
func ConvertAnswersToDTO(answers []*qa.Answer) []*AnswerDTO {
|
||||
if answers == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make([]*AnswerDTO, 0, len(answers))
|
||||
for _, answer := range answers {
|
||||
ret = append(ret, ConvertAnswerToDTO(answer))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// ConvertQuestionToDTO 将question转换为DTO
|
||||
func ConvertQuestionToDTO(question *qa.Question, flags map[string]string) *QuestionDTO {
|
||||
if question == nil {
|
||||
return nil
|
||||
}
|
||||
author := user.ConvertUserToDTO(question.Author)
|
||||
if author == nil {
|
||||
author = &user.UserDTO{
|
||||
ID: question.AuthorID,
|
||||
}
|
||||
}
|
||||
|
||||
context := question.Context
|
||||
if flags != nil {
|
||||
if isShortContext, ok := flags["is_short_context"]; ok && isShortContext == "true" {
|
||||
context = getShortContext(context)
|
||||
}
|
||||
}
|
||||
|
||||
return &QuestionDTO{
|
||||
ID: question.ID,
|
||||
Title: question.Title,
|
||||
Context: context,
|
||||
CreatedAt: question.CreatedAt,
|
||||
UpdatedAt: question.UpdatedAt,
|
||||
Author: author,
|
||||
Answers: ConvertAnswersToDTO(question.Answers),
|
||||
AnswerNum: question.AnswerNum,
|
||||
}
|
||||
}
|
||||
|
||||
func getShortContext(context string) string {
|
||||
p := strings.NewReader(context)
|
||||
doc, _ := goquery.NewDocumentFromReader(p)
|
||||
|
||||
doc.Find("script").Each(func(i int, el *goquery.Selection) {
|
||||
el.Remove()
|
||||
})
|
||||
|
||||
text := doc.Text()
|
||||
if len(text) > 20 {
|
||||
text = text[:20] + "..."
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// ConvertQuestionsToDTO 将questions转换为DTO
|
||||
func ConvertQuestionsToDTO(questions []*qa.Question) []*QuestionDTO {
|
||||
if questions == nil {
|
||||
return nil
|
||||
}
|
||||
ret := make([]*QuestionDTO, 0, len(questions))
|
||||
for _, question := range questions {
|
||||
ret = append(ret, ConvertQuestionToDTO(question, map[string]string{"is_short_context": "true"}))
|
||||
}
|
||||
return ret
|
||||
}
|
|
@ -2,7 +2,6 @@ package user
|
|||
|
||||
import (
|
||||
"bbs/app/http/middleware/auth"
|
||||
"bbs/app/http/middleware/response"
|
||||
"bbs/app/provider/user"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
)
|
||||
|
@ -18,7 +17,6 @@ func RegisterRoutes(r *gin.Engine) error {
|
|||
}
|
||||
|
||||
userGroup := r.Group("/user")
|
||||
userGroup.Use(response.ResponseHandler())
|
||||
{
|
||||
// 登录
|
||||
userGroup.POST("/login", api.Login)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"bbs/app/http/result"
|
||||
"bbs/app/provider/user"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
"net/http"
|
||||
|
@ -23,7 +24,7 @@ func (api *UserApi) Login(c *gin.Context) {
|
|||
userService := c.MustMake(user.UserKey).(user.Service)
|
||||
param := &loginParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(http.StatusBadRequest).IText("参数错误")
|
||||
c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -35,11 +36,11 @@ func (api *UserApi) Login(c *gin.Context) {
|
|||
|
||||
userWithToken, err := userService.Login(c, model)
|
||||
if err != nil {
|
||||
c.ISetStatus(http.StatusInternalServerError).IText(err.Error())
|
||||
c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 输出
|
||||
c.ISetOkStatus().IText(userWithToken.Token)
|
||||
c.ISetOkStatus().IJson(result.Success(userWithToken.Token))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package user
|
|||
|
||||
import (
|
||||
"bbs/app/http/middleware/auth"
|
||||
"bbs/app/http/result"
|
||||
"bbs/app/provider/user"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
)
|
||||
|
@ -17,13 +18,13 @@ import (
|
|||
func (api *UserApi) Logout(c *gin.Context) {
|
||||
authUser := auth.GetAuthUser(c)
|
||||
if authUser == nil {
|
||||
c.ISetStatus(500).IText("用户未登录")
|
||||
c.ISetStatus(500).IJson(result.Fail("用户未登录"))
|
||||
return
|
||||
}
|
||||
|
||||
userService := c.MustMake(user.UserKey).(user.Service)
|
||||
if err := userService.Logout(c, authUser); err != nil {
|
||||
c.ISetStatus(500).IText(err.Error())
|
||||
c.ISetStatus(500).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
//c.ISetOkStatus().IText("用户登出成功")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"bbs/app/http/result"
|
||||
"bbs/app/provider/user"
|
||||
"fmt"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
|
@ -30,7 +31,7 @@ func (api *UserApi) Register(c *gin.Context) {
|
|||
|
||||
param := ®isterParam{}
|
||||
if err := c.ShouldBind(param); err != nil {
|
||||
c.ISetStatus(400).IText("参数错误 ")
|
||||
c.ISetStatus(400).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -47,20 +48,20 @@ func (api *UserApi) Register(c *gin.Context) {
|
|||
logger.Error(c, err.Error(), map[string]interface{}{
|
||||
"stack": fmt.Sprintf("%+v", err),
|
||||
})
|
||||
c.ISetStatus(500).IText(err.Error())
|
||||
c.ISetStatus(500).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
if userWithToken == nil {
|
||||
c.ISetStatus(500).IText("注册失败")
|
||||
c.ISetStatus(500).IJson(result.Fail("注册失败"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := userService.SendRegisterMail(c, userWithToken); err != nil {
|
||||
|
||||
c.ISetStatus(500).IText("发送电子邮件失败")
|
||||
c.ISetStatus(500).IJson(result.Fail("发送电子邮件失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.ISetOkStatus().IText("注册成功,请前往邮箱查看邮件")
|
||||
c.ISetOkStatus().IJson(result.SuccessWithMessage("注册成功,请前往邮箱查看邮件"))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"bbs/app/http/result"
|
||||
"bbs/app/provider/user"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
)
|
||||
|
@ -19,21 +20,21 @@ func (api *UserApi) Verify(c *gin.Context) {
|
|||
userService := c.MustMake(user.UserKey).(user.Service)
|
||||
token := c.Query("token")
|
||||
if token == "" {
|
||||
c.ISetStatus(400).IText("参数错误")
|
||||
c.ISetStatus(400).IJson(result.Fail("参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
verified, err := userService.VerifyRegister(c, token)
|
||||
if err != nil {
|
||||
c.ISetStatus(500).IText(err.Error())
|
||||
c.ISetStatus(500).IJson(result.Fail(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !verified {
|
||||
c.ISetStatus(500).IText("验证错误")
|
||||
c.ISetStatus(500).IJson(result.Fail("验证错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 输出
|
||||
c.IRedirect("/#/login").IText("注册成功,请进入登录页面")
|
||||
c.IRedirect("/#/login").IJson(result.SuccessWithMessage("注册成功,请进入登录页面"))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"bbs/app/provider/qa"
|
||||
"bbs/app/provider/user"
|
||||
"github.com/Superdanda/hade/framework"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
)
|
||||
|
||||
func TypeRegister(container framework.Container) {
|
||||
typeRegister := container.MustMake(contract.TypeRegisterKey).(contract.TypeRegisterService)
|
||||
|
||||
qa.RegisterType(typeRegister)
|
||||
user.RegisterType(typeRegister)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package result
|
||||
|
||||
type Result struct {
|
||||
Code int `json:"code"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// Success 方法,封装成功响应的结构体
|
||||
func Success(data interface{}) Result {
|
||||
return Result{
|
||||
Code: 1,
|
||||
Success: true,
|
||||
Message: "Success",
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// SuccessWithMessage 方法,封装成功响应的结构体
|
||||
func SuccessWithMessage(message string) Result {
|
||||
return Result{
|
||||
Code: 1,
|
||||
Success: true,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// SuccessWithOKMessage 方法,封装成功响应的结构体, message 操作成功
|
||||
func SuccessWithOKMessage() Result {
|
||||
return Result{
|
||||
Code: 1,
|
||||
Success: true,
|
||||
Message: "操作成功",
|
||||
}
|
||||
}
|
||||
|
||||
// Fail 方法,封装失败响应的结构体
|
||||
func Fail(message string) Result {
|
||||
return Result{
|
||||
Code: 0,
|
||||
Success: false,
|
||||
Message: message,
|
||||
Data: nil,
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"bbs/app/http/module/demo"
|
||||
"bbs/app/http/module/qa"
|
||||
"bbs/app/http/module/user"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
"github.com/Superdanda/hade/framework/gin"
|
||||
|
@ -22,9 +22,8 @@ func Routes(core *gin.Engine) {
|
|||
// /路径先去./dist目录下查找文件是否存在,找到使用文件服务提供服务
|
||||
core.Use(static.Serve("/", static.LocalFile("./dist", false)))
|
||||
|
||||
err := demo.Register(core)
|
||||
|
||||
err = user.RegisterRoutes(core)
|
||||
err := user.RegisterRoutes(core)
|
||||
err = qa.RegisterRoutes(core)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
@ -23,48 +23,241 @@ const docTemplate = `{
|
|||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/demo/demo": {
|
||||
"get": {
|
||||
"/answer/create": {
|
||||
"post": {
|
||||
"description": "创建回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"demo"
|
||||
"qa"
|
||||
],
|
||||
"summary": "创建回答",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "创建回答参数",
|
||||
"name": "answerCreateParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.answerCreateParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "获取所有用户",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/demo.UserDTO"
|
||||
}
|
||||
}
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/demo/demo2": {
|
||||
"get": {
|
||||
"description": "获取所有学生,不进行分页",
|
||||
"/answer/delete": {
|
||||
"post": {
|
||||
"description": "创建回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"demo"
|
||||
"qa"
|
||||
],
|
||||
"summary": "创建回答",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "删除id",
|
||||
"name": "answerDeleteParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.answerDeleteParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "获取所有学生",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/create": {
|
||||
"post": {
|
||||
"description": "创建问题",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "创建问题",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "创建问题参数",
|
||||
"name": "questionCreateParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionCreateParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/delete": {
|
||||
"post": {
|
||||
"description": "删除问题,同时删除问题中的所有答案",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "删除问题",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "删除id",
|
||||
"name": "questionDeleteParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionDeleteParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/detail": {
|
||||
"post": {
|
||||
"description": "获取问题详情,包括问题的所有回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "获取问题详细",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "问题id",
|
||||
"name": "questionDetailParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionDetailParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "问题详情,带回答和作者",
|
||||
"schema": {
|
||||
"type": "QuestionDTO"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/edit": {
|
||||
"post": {
|
||||
"description": "编辑问题",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "编辑问题",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "编辑问题参数",
|
||||
"name": "questionEditParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionEditParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/list": {
|
||||
"post": {
|
||||
"description": "获取问题列表,包含作者信息,不包含回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "获取问题列表",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "分页查询的参数",
|
||||
"name": "questionListParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionListParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "问题列表",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/demo.UserDTO"
|
||||
"$ref": "#/definitions/qa.QuestionDTO"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,13 +388,177 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"demo.UserDTO": {
|
||||
"qa.AnswerDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"author": {
|
||||
"description": "作者",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user.UserDTO"
|
||||
}
|
||||
]
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.QuestionDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answer_num": {
|
||||
"type": "integer"
|
||||
},
|
||||
"answers": {
|
||||
"description": "回答",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/qa.AnswerDTO"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"description": "作者",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user.UserDTO"
|
||||
}
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"description": "在列表页,只显示前200个字符",
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.answerCreateParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"context",
|
||||
"questionId"
|
||||
],
|
||||
"properties": {
|
||||
"context": {
|
||||
"type": "string"
|
||||
},
|
||||
"questionId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.answerDeleteParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"answerId"
|
||||
],
|
||||
"properties": {
|
||||
"answerId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionCreateParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"title"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionDeleteParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"questionId"
|
||||
],
|
||||
"properties": {
|
||||
"questionId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionDetailParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"questionId"
|
||||
],
|
||||
"properties": {
|
||||
"questionId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionEditParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"id",
|
||||
"title"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionListParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"size",
|
||||
"start"
|
||||
],
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"start": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user.UserDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,48 +16,241 @@
|
|||
},
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/demo/demo": {
|
||||
"get": {
|
||||
"/answer/create": {
|
||||
"post": {
|
||||
"description": "创建回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"demo"
|
||||
"qa"
|
||||
],
|
||||
"summary": "创建回答",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "创建回答参数",
|
||||
"name": "answerCreateParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.answerCreateParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "获取所有用户",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/demo.UserDTO"
|
||||
}
|
||||
}
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/demo/demo2": {
|
||||
"get": {
|
||||
"description": "获取所有学生,不进行分页",
|
||||
"/answer/delete": {
|
||||
"post": {
|
||||
"description": "创建回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"demo"
|
||||
"qa"
|
||||
],
|
||||
"summary": "创建回答",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "删除id",
|
||||
"name": "answerDeleteParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.answerDeleteParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "获取所有学生",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/create": {
|
||||
"post": {
|
||||
"description": "创建问题",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "创建问题",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "创建问题参数",
|
||||
"name": "questionCreateParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionCreateParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/delete": {
|
||||
"post": {
|
||||
"description": "删除问题,同时删除问题中的所有答案",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "删除问题",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "删除id",
|
||||
"name": "questionDeleteParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionDeleteParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/detail": {
|
||||
"post": {
|
||||
"description": "获取问题详情,包括问题的所有回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "获取问题详细",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "问题id",
|
||||
"name": "questionDetailParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionDetailParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "问题详情,带回答和作者",
|
||||
"schema": {
|
||||
"type": "QuestionDTO"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/edit": {
|
||||
"post": {
|
||||
"description": "编辑问题",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "编辑问题",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "编辑问题参数",
|
||||
"name": "questionEditParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionEditParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "操作成功",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/question/list": {
|
||||
"post": {
|
||||
"description": "获取问题列表,包含作者信息,不包含回答",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"qa"
|
||||
],
|
||||
"summary": "获取问题列表",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "分页查询的参数",
|
||||
"name": "questionListParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/qa.questionListParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "问题列表",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/demo.UserDTO"
|
||||
"$ref": "#/definitions/qa.QuestionDTO"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,13 +381,177 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"demo.UserDTO": {
|
||||
"qa.AnswerDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"author": {
|
||||
"description": "作者",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user.UserDTO"
|
||||
}
|
||||
]
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.QuestionDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answer_num": {
|
||||
"type": "integer"
|
||||
},
|
||||
"answers": {
|
||||
"description": "回答",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/qa.AnswerDTO"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"description": "作者",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user.UserDTO"
|
||||
}
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"description": "在列表页,只显示前200个字符",
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.answerCreateParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"context",
|
||||
"questionId"
|
||||
],
|
||||
"properties": {
|
||||
"context": {
|
||||
"type": "string"
|
||||
},
|
||||
"questionId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.answerDeleteParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"answerId"
|
||||
],
|
||||
"properties": {
|
||||
"answerId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionCreateParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"title"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionDeleteParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"questionId"
|
||||
],
|
||||
"properties": {
|
||||
"questionId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionDetailParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"questionId"
|
||||
],
|
||||
"properties": {
|
||||
"questionId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionEditParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"id",
|
||||
"title"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qa.questionListParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"size",
|
||||
"start"
|
||||
],
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"start": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user.UserDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,116 @@
|
|||
basePath: /
|
||||
definitions:
|
||||
demo.UserDTO:
|
||||
qa.AnswerDTO:
|
||||
properties:
|
||||
author:
|
||||
allOf:
|
||||
- $ref: '#/definitions/user.UserDTO'
|
||||
description: 作者
|
||||
content:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
qa.QuestionDTO:
|
||||
properties:
|
||||
answer_num:
|
||||
type: integer
|
||||
answers:
|
||||
description: 回答
|
||||
items:
|
||||
$ref: '#/definitions/qa.AnswerDTO'
|
||||
type: array
|
||||
author:
|
||||
allOf:
|
||||
- $ref: '#/definitions/user.UserDTO'
|
||||
description: 作者
|
||||
context:
|
||||
description: 在列表页,只显示前200个字符
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
title:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
qa.answerCreateParam:
|
||||
properties:
|
||||
context:
|
||||
type: string
|
||||
questionId:
|
||||
type: integer
|
||||
required:
|
||||
- context
|
||||
- questionId
|
||||
type: object
|
||||
qa.answerDeleteParam:
|
||||
properties:
|
||||
answerId:
|
||||
type: integer
|
||||
required:
|
||||
- answerId
|
||||
type: object
|
||||
qa.questionCreateParam:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
- title
|
||||
type: object
|
||||
qa.questionDeleteParam:
|
||||
properties:
|
||||
questionId:
|
||||
type: integer
|
||||
required:
|
||||
- questionId
|
||||
type: object
|
||||
qa.questionDetailParam:
|
||||
properties:
|
||||
questionId:
|
||||
type: integer
|
||||
required:
|
||||
- questionId
|
||||
type: object
|
||||
qa.questionEditParam:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
title:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
- id
|
||||
- title
|
||||
type: object
|
||||
qa.questionListParam:
|
||||
properties:
|
||||
size:
|
||||
type: integer
|
||||
start:
|
||||
type: integer
|
||||
required:
|
||||
- size
|
||||
- start
|
||||
type: object
|
||||
user.UserDTO:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
user_name:
|
||||
type: string
|
||||
type: object
|
||||
user.loginParam:
|
||||
|
@ -45,37 +151,162 @@ info:
|
|||
title: hade
|
||||
version: "1.1"
|
||||
paths:
|
||||
/demo/demo:
|
||||
get:
|
||||
/answer/create:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建回答
|
||||
parameters:
|
||||
- description: 创建回答参数
|
||||
in: body
|
||||
name: answerCreateParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/qa.answerCreateParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
description: 操作成功
|
||||
schema:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/demo.UserDTO'
|
||||
type: array
|
||||
type: array
|
||||
summary: 获取所有用户
|
||||
type: string
|
||||
summary: 创建回答
|
||||
tags:
|
||||
- demo
|
||||
/demo/demo2:
|
||||
get:
|
||||
description: 获取所有学生,不进行分页
|
||||
- qa
|
||||
/answer/delete:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建回答
|
||||
parameters:
|
||||
- description: 删除id
|
||||
in: body
|
||||
name: answerDeleteParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/qa.answerDeleteParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
description: 操作成功
|
||||
schema:
|
||||
type: string
|
||||
summary: 创建回答
|
||||
tags:
|
||||
- qa
|
||||
/question/create:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建问题
|
||||
parameters:
|
||||
- description: 创建问题参数
|
||||
in: body
|
||||
name: questionCreateParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/qa.questionCreateParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 操作成功
|
||||
schema:
|
||||
type: string
|
||||
summary: 创建问题
|
||||
tags:
|
||||
- qa
|
||||
/question/delete:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 删除问题,同时删除问题中的所有答案
|
||||
parameters:
|
||||
- description: 删除id
|
||||
in: body
|
||||
name: questionDeleteParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/qa.questionDeleteParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 操作成功
|
||||
schema:
|
||||
type: string
|
||||
summary: 删除问题
|
||||
tags:
|
||||
- qa
|
||||
/question/detail:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取问题详情,包括问题的所有回答
|
||||
parameters:
|
||||
- description: 问题id
|
||||
in: body
|
||||
name: questionDetailParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/qa.questionDetailParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 问题详情,带回答和作者
|
||||
schema:
|
||||
type: QuestionDTO
|
||||
summary: 获取问题详细
|
||||
tags:
|
||||
- qa
|
||||
/question/edit:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 编辑问题
|
||||
parameters:
|
||||
- description: 编辑问题参数
|
||||
in: body
|
||||
name: questionEditParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/qa.questionEditParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 操作成功
|
||||
schema:
|
||||
type: string
|
||||
summary: 编辑问题
|
||||
tags:
|
||||
- qa
|
||||
/question/list:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取问题列表,包含作者信息,不包含回答
|
||||
parameters:
|
||||
- description: 分页查询的参数
|
||||
in: body
|
||||
name: questionListParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/qa.questionListParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 问题列表
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/demo.UserDTO'
|
||||
$ref: '#/definitions/qa.QuestionDTO'
|
||||
type: array
|
||||
summary: 获取所有学生
|
||||
summary: 获取问题列表
|
||||
tags:
|
||||
- demo
|
||||
- qa
|
||||
/user/login:
|
||||
post:
|
||||
consumes:
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package database_connect
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
const DatabaseConnectKey = "hade:database_connect"
|
||||
|
||||
type Service interface {
|
||||
LocalDatabaseConnect() *gorm.DB
|
||||
AliDataBaseConnect() *gorm.DB
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package database_connect
|
||||
|
||||
import (
|
||||
"github.com/Superdanda/hade/framework"
|
||||
)
|
||||
|
||||
type DatabaseConnectProvider struct {
|
||||
framework.ServiceProvider
|
||||
|
||||
c framework.Container
|
||||
}
|
||||
|
||||
func (sp *DatabaseConnectProvider) Name() string {
|
||||
return DatabaseConnectKey
|
||||
}
|
||||
|
||||
func (sp *DatabaseConnectProvider) Register(c framework.Container) framework.NewInstance {
|
||||
return NewDatabaseConnectService
|
||||
}
|
||||
|
||||
func (sp *DatabaseConnectProvider) IsDefer() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (sp *DatabaseConnectProvider) Params(c framework.Container) []interface{} {
|
||||
return []interface{}{c}
|
||||
}
|
||||
|
||||
func (sp *DatabaseConnectProvider) Boot(c framework.Container) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package database_connect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Superdanda/hade/framework"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
"github.com/Superdanda/hade/framework/provider/orm"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DatabaseConnectService struct {
|
||||
container framework.Container
|
||||
}
|
||||
|
||||
func (d DatabaseConnectService) LocalDatabaseConnect() *gorm.DB {
|
||||
return getDatabaseConnectByYaml("database.local", d)
|
||||
}
|
||||
|
||||
func (d DatabaseConnectService) AliDataBaseConnect() *gorm.DB {
|
||||
return getDatabaseConnectByYaml("database.ali", d)
|
||||
}
|
||||
|
||||
func NewDatabaseConnectService(params ...interface{}) (interface{}, error) {
|
||||
container := params[0].(framework.Container)
|
||||
return &DatabaseConnectService{container: container}, nil
|
||||
}
|
||||
|
||||
func getDatabaseConnectByYaml(yamlPath string, d DatabaseConnectService) *gorm.DB {
|
||||
ormService := d.container.MustMake(contract.ORMKey).(contract.ORMService)
|
||||
db, err := ormService.GetDB(orm.WithConfigPath(yamlPath))
|
||||
if err != nil {
|
||||
fmt.Println(yamlPath + "数据库连接失败,请检查配置")
|
||||
return nil
|
||||
}
|
||||
return db
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package demo
|
|
@ -1,30 +0,0 @@
|
|||
package demo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Superdanda/hade/framework"
|
||||
)
|
||||
|
||||
type DemoServiceProvider struct {
|
||||
}
|
||||
|
||||
func (sp *DemoServiceProvider) Name() string {
|
||||
return Key
|
||||
}
|
||||
|
||||
func (sp *DemoServiceProvider) Register(c framework.Container) framework.NewInstance {
|
||||
return NewDemoService
|
||||
}
|
||||
|
||||
func (sp *DemoServiceProvider) IsDefer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (sp *DemoServiceProvider) Params(c framework.Container) []interface{} {
|
||||
return []interface{}{c}
|
||||
}
|
||||
|
||||
func (sp *DemoServiceProvider) Boot(c framework.Container) error {
|
||||
fmt.Println("demo services boot")
|
||||
return nil
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package demo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Superdanda/hade/framework"
|
||||
)
|
||||
|
||||
// Key Demo 服务的 key
|
||||
const Key = "hade:demo"
|
||||
|
||||
// Service Demo 服务的接口
|
||||
type Service interface {
|
||||
GetFoo() Foo
|
||||
}
|
||||
|
||||
// Foo Demo 服务接口定义的一个数据结构
|
||||
type Foo struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// DemoService serviceProvider 实现
|
||||
type DemoService struct {
|
||||
Service
|
||||
c framework.Container
|
||||
}
|
||||
|
||||
// GetFoo 实现接口
|
||||
func (s *DemoService) GetFoo() Foo {
|
||||
return Foo{Name: "i am foo"}
|
||||
}
|
||||
|
||||
func NewDemoService(params ...interface{}) (interface{}, error) {
|
||||
c := params[0].(framework.Container)
|
||||
fmt.Println("new demo services")
|
||||
return &DemoService{c: c}, nil
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/provider/user"
|
||||
"context"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
const QaKey = "bbs:qa"
|
||||
|
||||
type Service interface {
|
||||
// GetQuestions 获取问题列表,question简化结构
|
||||
GetQuestions(ctx context.Context, pager *Pager) ([]*Question, error)
|
||||
// GetQuestion 获取某个问题详情,question简化结构
|
||||
GetQuestion(ctx context.Context, questionID int64) (*Question, error)
|
||||
// PostQuestion 上传某个问题
|
||||
// ctx必须带操作人id
|
||||
PostQuestion(ctx context.Context, question *Question) error
|
||||
|
||||
// QuestionLoadAuthor 问题加载Author字段
|
||||
QuestionLoadAuthor(ctx context.Context, question *Question) error
|
||||
// QuestionsLoadAuthor 批量加载Author字段
|
||||
QuestionsLoadAuthor(ctx context.Context, questions *[]*Question) error
|
||||
|
||||
// QuestionLoadAnswers 单个问题加载Answers
|
||||
QuestionLoadAnswers(ctx context.Context, question *Question) error
|
||||
// QuestionsLoadAnswers 批量问题加载Answers
|
||||
QuestionsLoadAnswers(ctx context.Context, questions *[]*Question) error
|
||||
|
||||
// PostAnswer 上传某个回答
|
||||
// ctx必须带操作人信息
|
||||
PostAnswer(ctx context.Context, answer *Answer) error
|
||||
// GetAnswer 获取回答
|
||||
GetAnswer(ctx context.Context, answerID int64) (*Answer, error)
|
||||
|
||||
// AnswerLoadAuthor 问题加载Author字段
|
||||
AnswerLoadAuthor(ctx context.Context, question *Answer) error
|
||||
// AnswersLoadAuthor 批量加载Author字段
|
||||
AnswersLoadAuthor(ctx context.Context, questions *[]*Answer) error
|
||||
|
||||
// DeleteQuestion 删除问题,同时删除对应的回答
|
||||
// ctx必须带操作人信息
|
||||
DeleteQuestion(ctx context.Context, questionID int64) error
|
||||
// DeleteAnswer 删除某个回答
|
||||
// ctx必须带操作人信息
|
||||
DeleteAnswer(ctx context.Context, answerID int64) error
|
||||
|
||||
// UpdateQuestion 代表更新问题, 只会对比其中的context,title两个字段,其他字段不会对比
|
||||
// ctx必须带操作人
|
||||
UpdateQuestion(ctx context.Context, question *Question) error
|
||||
}
|
||||
|
||||
func RegisterType(typeRegister contract.TypeRegisterService) {
|
||||
typeRegister.Register("Question", Question{})
|
||||
typeRegister.Register("Answer", Answer{})
|
||||
}
|
||||
|
||||
// Question 代表问题
|
||||
type Question struct {
|
||||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
Title string `gorm:"column:title;comment:标题"`
|
||||
Context string `gorm:"column:context;comment:内容"`
|
||||
AuthorID int64 `gorm:"column:author_id;comment:作者id;not null;default:0"`
|
||||
AnswerNum int `gorm:"column:answer_num;comment:回答数;not null;default:0"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;autoCreateTime;<-:false;comment:更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
Author *user.User `gorm:"foreignKey:AuthorID"`
|
||||
Answers []*Answer `gorm:"foreignKey:QuestionID"`
|
||||
}
|
||||
|
||||
func NewQuestion(Title string, Context string, AuthorID int64) *Question {
|
||||
return &Question{
|
||||
Title: Title,
|
||||
Context: Context,
|
||||
AuthorID: AuthorID,
|
||||
}
|
||||
}
|
||||
|
||||
// Answer 代表一个回答
|
||||
type Answer struct {
|
||||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
QuestionID int64 `gorm:"column:question_id;index;comment:问题id;not null;default 0"`
|
||||
Context string `gorm:"column:context;comment:内容"`
|
||||
AuthorID int64 `gorm:"column:author_id;comment:作者id;not null;default:0"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;autoCreateTime;<-:false;comment:更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
Author *user.User `gorm:"foreignKey:ID;references:author_id"`
|
||||
Question *Question `gorm:"foreignKey:QuestionID"`
|
||||
}
|
||||
|
||||
func NewAnswer(QuestionID int64, Context string, AuthorID int64) *Answer {
|
||||
return &Answer{
|
||||
QuestionID: QuestionID,
|
||||
Context: Context,
|
||||
AuthorID: AuthorID,
|
||||
}
|
||||
}
|
||||
|
||||
// Pager 代表分页机制
|
||||
type Pager struct {
|
||||
Total int64 // 共有多少数据,只有返回值使用
|
||||
Start int // 起始位置
|
||||
Size int // 每个页面个数
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"github.com/Superdanda/hade/framework"
|
||||
)
|
||||
|
||||
type QaProvider struct {
|
||||
framework.ServiceProvider
|
||||
|
||||
c framework.Container
|
||||
}
|
||||
|
||||
func (sp *QaProvider) Name() string {
|
||||
return QaKey
|
||||
}
|
||||
|
||||
func (sp *QaProvider) Register(c framework.Container) framework.NewInstance {
|
||||
return NewQaService
|
||||
}
|
||||
|
||||
func (sp *QaProvider) IsDefer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (sp *QaProvider) Params(c framework.Container) []interface{} {
|
||||
return []interface{}{c}
|
||||
}
|
||||
|
||||
func (sp *QaProvider) Boot(c framework.Container) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
package qa
|
||||
|
||||
import (
|
||||
"bbs/app/provider/database_connect"
|
||||
"context"
|
||||
"github.com/Superdanda/hade/framework"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
"github.com/jianfengye/collection"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type QaService struct {
|
||||
container framework.Container
|
||||
ormDB *gorm.DB // db
|
||||
logger contract.Log // log
|
||||
}
|
||||
|
||||
func (q QaService) GetQuestions(ctx context.Context, pager *Pager) ([]*Question, error) {
|
||||
questions := make([]*Question, 0, pager.Size)
|
||||
total := int64(0)
|
||||
if err := q.ormDB.Count(&total).Error; err != nil {
|
||||
pager.Total = total
|
||||
}
|
||||
if err := q.ormDB.WithContext(ctx).Order("created_at desc").Offset(pager.Start).Limit(pager.Size).Find(&questions).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return []*Question{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return questions, nil
|
||||
}
|
||||
|
||||
func (q QaService) GetQuestion(ctx context.Context, questionID int64) (*Question, error) {
|
||||
question := &Question{}
|
||||
if err := q.ormDB.WithContext(ctx).First(question, questionID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return question, nil
|
||||
}
|
||||
|
||||
func (q QaService) PostQuestion(ctx context.Context, question *Question) error {
|
||||
if err := q.ormDB.WithContext(ctx).Create(question).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) QuestionLoadAuthor(ctx context.Context, question *Question) error {
|
||||
if err := q.ormDB.WithContext(ctx).Preload("Author").First(question).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) QuestionsLoadAuthor(ctx context.Context, questions *[]*Question) error {
|
||||
if questions == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
questionColl := collection.NewObjPointCollection(*questions)
|
||||
ids, err := questionColl.Pluck("ID").ToInt64s()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := q.ormDB.WithContext(ctx).Preload("Author").Order("created_at desc").Find(questions, ids).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) QuestionLoadAnswers(ctx context.Context, question *Question) error {
|
||||
if err := q.ormDB.WithContext(ctx).Preload("Answers", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("answers.created_at desc")
|
||||
}).First(question).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) QuestionsLoadAnswers(ctx context.Context, questions *[]*Question) error {
|
||||
if questions == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
questionColl := collection.NewObjPointCollection(*questions)
|
||||
ids, err := questionColl.Pluck("ID").ToInt64s()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := q.ormDB.WithContext(ctx).Preload("Answers").Find(questions, ids).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) PostAnswer(ctx context.Context, answer *Answer) error {
|
||||
if answer.QuestionID == 0 {
|
||||
return errors.New("问题不存在")
|
||||
}
|
||||
// 必须使用事务
|
||||
err := q.ormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
question := &Question{ID: answer.QuestionID}
|
||||
// 获取问题
|
||||
if err := tx.First(question).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 增加回答
|
||||
if err := tx.Create(answer).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 问题回答数量+1
|
||||
question.AnswerNum = question.AnswerNum + 1
|
||||
if err := tx.Save(question).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) GetAnswer(ctx context.Context, answerID int64) (*Answer, error) {
|
||||
answer := &Answer{ID: answerID}
|
||||
if err := q.ormDB.WithContext(ctx).First(answer).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return answer, nil
|
||||
}
|
||||
|
||||
func (q QaService) AnswerLoadAuthor(ctx context.Context, question *Answer) error {
|
||||
if err := q.ormDB.WithContext(ctx).Preload("Author").First(question).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) AnswersLoadAuthor(ctx context.Context, answers *[]*Answer) error {
|
||||
if answers == nil {
|
||||
return nil
|
||||
}
|
||||
answerColl := collection.NewObjPointCollection(*answers)
|
||||
ids, err := answerColl.Pluck("ID").ToInt64s()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 使用PreLoad的机制,获取Create方法
|
||||
if err := q.ormDB.WithContext(ctx).Preload("Author").Order("created_at desc").Find(answers, ids).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) DeleteQuestion(ctx context.Context, questionID int64) error {
|
||||
question := &Question{ID: questionID}
|
||||
if err := q.ormDB.WithContext(ctx).Delete(question).Error; err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) DeleteAnswer(ctx context.Context, answerID int64) error {
|
||||
answer := &Answer{ID: answerID}
|
||||
if err := q.ormDB.WithContext(ctx).Delete(answer).Error; err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q QaService) UpdateQuestion(ctx context.Context, question *Question) error {
|
||||
questionDB := &Question{ID: question.ID}
|
||||
if err := q.ormDB.WithContext(ctx).First(questionDB).Error; err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
questionDB.UpdatedAt = time.Now()
|
||||
if question.Title != "" {
|
||||
questionDB.Title = question.Title
|
||||
}
|
||||
if question.Context != "" {
|
||||
questionDB.Context = question.Context
|
||||
}
|
||||
if err := q.ormDB.WithContext(ctx).Save(questionDB).Error; err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewQaService(params ...interface{}) (interface{}, error) {
|
||||
container := params[0].(framework.Container)
|
||||
ormService := container.MustMake(database_connect.DatabaseConnectKey).(database_connect.Service)
|
||||
logger := container.MustMake(contract.LogKey).(contract.Log)
|
||||
return &QaService{container: container, ormDB: ormService.LocalDatabaseConnect(), logger: logger}, nil
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package qa
|
|
@ -3,6 +3,7 @@ package user
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/Superdanda/hade/framework/contract"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -30,6 +31,10 @@ type Service interface {
|
|||
GetUser(ctx context.Context, userID int64) (*User, error)
|
||||
}
|
||||
|
||||
func RegisterType(typeRegister contract.TypeRegisterService) {
|
||||
typeRegister.Register("User", User{})
|
||||
}
|
||||
|
||||
// User 代表一个用户,注意这里的用户信息字段在不同接口和参数可能为空
|
||||
type User struct {
|
||||
ID int64 `gorm:"column:id;primary_key;auto_increment" json:"id"` // 代表用户id, 只有注册成功之后才有这个id,唯一表示一个用户
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"bbs/app/provider/database_connect"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
@ -17,6 +18,7 @@ type UserService struct {
|
|||
container framework.Container
|
||||
logger contract.Log
|
||||
config contract.Config
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
@ -39,10 +41,8 @@ func genToken(n int) (string, error) {
|
|||
}
|
||||
|
||||
func (u *UserService) Register(ctx context.Context, user *User) (*User, error) {
|
||||
ormService := u.container.MustMake(contract.ORMKey).(contract.ORMService)
|
||||
|
||||
//验证用户是否已经存在
|
||||
_, err, _ := u.checkUserNameOrEmailIfExist(ormService, user)
|
||||
_, err, _ := u.checkUserNameOrEmailIfExist(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func (u *UserService) SendRegisterMail(ctx context.Context, user *User) error {
|
|||
password := configer.GetString("app.smtp.password")
|
||||
from := configer.GetString("app.smtp.from")
|
||||
domain := configer.GetString("app.domain")
|
||||
|
||||
//QZwFMGzyJgetGgaj
|
||||
// 实例化gomail
|
||||
d := gomail.NewDialer(host, port, username, password)
|
||||
|
||||
|
@ -110,12 +110,8 @@ func (u *UserService) VerifyRegister(ctx context.Context, token string) (bool, e
|
|||
if user.Token != token {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
//验证邮箱,用户名的唯一
|
||||
ormService := u.container.MustMake(contract.ORMKey).(contract.ORMService)
|
||||
|
||||
//验证用户是否已经存在
|
||||
_, err, _ := u.checkUserNameOrEmailIfExist(ormService, user)
|
||||
_, err, _ := u.checkUserNameOrEmailIfExist(user)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -128,38 +124,32 @@ func (u *UserService) VerifyRegister(ctx context.Context, token string) (bool, e
|
|||
}
|
||||
user.Password = string(hash)
|
||||
// 具体在数据库创建用户
|
||||
db, err := ormService.GetDB()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := db.Create(user).Error; err != nil {
|
||||
if err := u.db.Create(user).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (u *UserService) Login(ctx context.Context, user *User) (*User, error) {
|
||||
ormService := u.container.MustMake(contract.ORMKey).(contract.ORMService)
|
||||
db, err := ormService.GetDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userDB := &User{}
|
||||
if err := db.Where("username=?", user.UserName).First(userDB).Error; err != nil {
|
||||
if err := u.db.Where("username=?", user.UserName).First(userDB).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.Wrap(err, "该用户未注册")
|
||||
return nil, errors.New("该用户未注册")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(userDB.Password), []byte(user.Password)); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.New("密码错误")
|
||||
}
|
||||
|
||||
userDB.Password = ""
|
||||
// 缓存session
|
||||
cacheService := u.container.MustMake(contract.CacheKey).(contract.CacheService)
|
||||
token, err := genToken(10)
|
||||
token, _ := genToken(10)
|
||||
key := fmt.Sprintf("session:%v", token)
|
||||
if err := cacheService.SetObj(ctx, key, userDB, 24*time.Hour); err != nil {
|
||||
return nil, err
|
||||
|
@ -196,13 +186,8 @@ func (u *UserService) VerifyLogin(ctx context.Context, token string) (*User, err
|
|||
}
|
||||
|
||||
func (u *UserService) GetUser(ctx context.Context, userID int64) (*User, error) {
|
||||
ormService := u.container.MustMake(contract.ORMKey).(contract.ORMService)
|
||||
db, err := ormService.GetDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user := &User{ID: userID}
|
||||
if err := db.WithContext(ctx).First(user).Error; err != nil {
|
||||
if err := u.db.WithContext(ctx).First(user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
|
@ -212,23 +197,17 @@ func NewUserService(params ...interface{}) (interface{}, error) {
|
|||
container := params[0].(framework.Container)
|
||||
logger := container.MustMake(contract.LogKey).(contract.Log)
|
||||
config := container.MustMake(contract.ConfigKey).(contract.Config)
|
||||
return &UserService{container: container, logger: logger, config: config}, nil
|
||||
databaseConnectService := container.MustMake(database_connect.DatabaseConnectKey).(database_connect.Service)
|
||||
db := databaseConnectService.LocalDatabaseConnect()
|
||||
return &UserService{container: container, logger: logger, config: config, db: db}, nil
|
||||
}
|
||||
|
||||
func (s *UserService) Foo() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *UserService) checkUserNameOrEmailIfExist(dbService contract.ORMService, user *User) (bool, error, *User) {
|
||||
db, err := dbService.GetDB()
|
||||
func (s *UserService) checkUserNameOrEmailIfExist(user *User) (bool, error, *User) {
|
||||
userDB := &User{}
|
||||
if err != nil {
|
||||
return false, err, nil
|
||||
}
|
||||
if !errors.Is(db.Where(&User{Email: user.Email}).First(userDB).Error, gorm.ErrRecordNotFound) {
|
||||
if !errors.Is(s.db.Where(&User{Email: user.Email}).First(userDB).Error, gorm.ErrRecordNotFound) {
|
||||
return true, errors.New("邮箱已注册用户,不能重复注册"), userDB
|
||||
}
|
||||
if !errors.Is(db.Where(&User{UserName: user.UserName}).First(userDB).Error, gorm.ErrRecordNotFound) {
|
||||
if !errors.Is(s.db.Where(&User{UserName: user.UserName}).First(userDB).Error, gorm.ErrRecordNotFound) {
|
||||
return true, errors.New("邮箱已注册用户,不能重复注册"), userDB
|
||||
}
|
||||
return false, nil, nil
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
url: http://127.0.0.1:8066
|
||||
|
||||
domain: "http://127.0.0.1:8888"
|
||||
name: bbs
|
||||
|
||||
swagger_open: true
|
||||
|
@ -22,5 +22,5 @@ smtp:
|
|||
host: "smtp.126.com"
|
||||
port: 25
|
||||
from: "superdanda@126.com"
|
||||
username: "superdanda"
|
||||
password: "A10337191315."
|
||||
username: "superdanda@126.com"
|
||||
password: "QZwFMGzyJgetGgaj"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
driver: memory
|
|
@ -3,20 +3,36 @@ conn_max_open: 100 # 通用配置,连接池最大连接数
|
|||
conn_max_lifetime: 1h # 通用配置,连接数最大生命周期
|
||||
|
||||
|
||||
driver: mysql # 连接驱动
|
||||
dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效
|
||||
host: 47.97.21.20 # ip地址
|
||||
port: 3306 # 端口
|
||||
database: questionnaire # 数据库
|
||||
username: root # 用户名
|
||||
password: "kerowsqw34" # 密码
|
||||
charset: utf8mb4 # 字符集
|
||||
collation: utf8mb4_unicode_ci # 字符序
|
||||
timeout: 10s # 连接超时
|
||||
read_timeout: 2s # 读超时
|
||||
write_timeout: 2s # 写超时
|
||||
parse_time: true # 是否解析时间
|
||||
protocol: tcp # 传输协议
|
||||
loc: Local # 时区
|
||||
local:
|
||||
driver: mysql # 连接驱动
|
||||
dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效
|
||||
host: 127.0.0.1 # ip地址
|
||||
port: 3306 # 端口
|
||||
database: bbs # 数据库
|
||||
username: root # 用户名
|
||||
password: "123456" # 密码
|
||||
charset: utf8mb4 # 字符集
|
||||
collation: utf8mb4_unicode_ci # 字符序
|
||||
timeout: 10s # 连接超时
|
||||
read_timeout: 2s # 读超时
|
||||
write_timeout: 2s # 写超时
|
||||
parse_time: true # 是否解析时间
|
||||
|
||||
ali:
|
||||
driver: mysql # 连接驱动
|
||||
dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效
|
||||
host: 47.97.21.20 # ip地址
|
||||
port: 3306 # 端口
|
||||
database: bbs # 数据库
|
||||
username: root # 用户名
|
||||
password: "kerowsqw34" # 密码
|
||||
charset: utf8mb4 # 字符集
|
||||
collation: utf8mb4_unicode_ci # 字符序
|
||||
timeout: 10s # 连接超时
|
||||
read_timeout: 2s # 读超时
|
||||
write_timeout: 2s # 写超时
|
||||
parse_time: true # 是否解析时间
|
||||
|
||||
|
||||
sync:
|
||||
filePath: /app/provider
|
||||
|
|
8
go.mod
8
go.mod
|
@ -3,7 +3,7 @@ module bbs
|
|||
go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/Superdanda/hade v1.0.4
|
||||
github.com/Superdanda/hade v1.0.6
|
||||
github.com/swaggo/swag v1.16.4
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
)
|
||||
|
@ -17,7 +17,7 @@ require (
|
|||
github.com/jianfengye/collection v1.4.2 // indirect
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/redis/go-redis/v9 v9.7.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.0 // indirect
|
||||
github.com/sevlyar/go-daemon v0.1.6 // indirect
|
||||
|
@ -33,7 +33,9 @@ require (
|
|||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
|
@ -109,7 +111,7 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
13
go.sum
13
go.sum
|
@ -36,10 +36,14 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63n
|
|||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/Superdanda/hade v1.0.4 h1:NFsH8BBbsHmbedkGHdpjGk3e49GEJtRWU4ENOxp0sfU=
|
||||
github.com/Superdanda/hade v1.0.4/go.mod h1:z52uXdtEfX25FRCj7YeQOctw6fho9nIaIWc/picNhuA=
|
||||
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
|
||||
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
|
||||
github.com/Superdanda/hade v1.0.6 h1:oZg1y2OUYKR+JdsbAzbv5DHNhu5VM6XoKliNmYBWUvA=
|
||||
github.com/Superdanda/hade v1.0.6/go.mod h1:z52uXdtEfX25FRCj7YeQOctw6fho9nIaIWc/picNhuA=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
|
@ -368,10 +372,13 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
|||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -398,6 +405,7 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
|
@ -408,6 +416,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
|||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
|
|
4
main.go
4
main.go
|
@ -1,8 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bbs/app/console"
|
||||
"bbs/app/http"
|
||||
"github.com/Superdanda/hade/app/console"
|
||||
"github.com/Superdanda/hade/framework"
|
||||
"github.com/Superdanda/hade/framework/provider/app"
|
||||
"github.com/Superdanda/hade/framework/provider/cache"
|
||||
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/Superdanda/hade/framework/provider/orm"
|
||||
"github.com/Superdanda/hade/framework/provider/redis"
|
||||
"github.com/Superdanda/hade/framework/provider/ssh"
|
||||
"github.com/Superdanda/hade/framework/provider/type_register"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -32,6 +33,7 @@ func main() {
|
|||
container.Bind(&redis.RedisProvider{})
|
||||
container.Bind(&cache.HadeCacheProvider{})
|
||||
container.Bind(&ssh.SSHProvider{})
|
||||
container.Bind(&type_register.TypeRegisterProvider{})
|
||||
|
||||
// 将HTTP引擎初始化,并且作为服务提供者绑定到服务容器中
|
||||
if engine, err := http.NewHttpEngine(container); err == nil {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
"": {
|
||||
"name": "element-starter",
|
||||
"dependencies": {
|
||||
"@toast-ui/editor": "^3.2.2",
|
||||
"@toast-ui/vue-editor": "^3.2.3",
|
||||
"axios": "^0.24.0",
|
||||
"element-ui": "^2.3.4",
|
||||
"js-cookie": "^3.0.1",
|
||||
|
@ -35,6 +37,32 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@toast-ui/editor": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/@toast-ui/editor/-/editor-3.2.2.tgz",
|
||||
"integrity": "sha512-ASX7LFjN2ZYQJrwmkUajPs7DRr9FsM1+RQ82CfTO0Y5ZXorBk1VZS4C2Dpxinx9kl55V4F8/A2h2QF4QMDtRbA==",
|
||||
"dependencies": {
|
||||
"dompurify": "^2.3.3",
|
||||
"prosemirror-commands": "^1.1.9",
|
||||
"prosemirror-history": "^1.1.3",
|
||||
"prosemirror-inputrules": "^1.1.3",
|
||||
"prosemirror-keymap": "^1.1.4",
|
||||
"prosemirror-model": "^1.14.1",
|
||||
"prosemirror-state": "^1.3.4",
|
||||
"prosemirror-view": "^1.18.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@toast-ui/vue-editor": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/@toast-ui/vue-editor/-/vue-editor-3.2.3.tgz",
|
||||
"integrity": "sha512-IjoV5tBh/yesIuqRqmOQx1+F0oeeAbIeBA7edMTawIXHQXBeJ1qzGHLTY5NWrUQ6BBtV8CDBeedjnVsJ+mHjKQ==",
|
||||
"dependencies": {
|
||||
"@toast-ui/editor": "^3.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "http://registry.npm.taobao.org/accepts/download/accepts-1.3.3.tgz",
|
||||
|
@ -2146,6 +2174,11 @@
|
|||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-2.5.7.tgz",
|
||||
"integrity": "sha512-2q4bEI+coQM8f5ez7kt2xclg1XsecaV9ASJk/54vwlfRRNQfDqJz2pzQ8t0Ix/ToBpXlVjrRIx7pFC/o8itG2Q=="
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "http://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz",
|
||||
|
@ -4086,6 +4119,11 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/orderedmap": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/orderedmap/-/orderedmap-2.1.1.tgz",
|
||||
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="
|
||||
},
|
||||
"node_modules/original": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "http://registry.npm.taobao.org/original/download/original-1.0.0.tgz",
|
||||
|
@ -4935,6 +4973,81 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prosemirror-commands": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-commands/-/prosemirror-commands-1.6.2.tgz",
|
||||
"integrity": "sha512-0nDHH++qcf/BuPLYvmqZTUUsPJUCPBUXt0J1ErTcDIS369CTp773itzLGIgIXG4LJXOlwYCr44+Mh4ii6MP1QA==",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-history": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-history/-/prosemirror-history-1.4.1.tgz",
|
||||
"integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.31.0",
|
||||
"rope-sequence": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-inputrules": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz",
|
||||
"integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-keymap": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz",
|
||||
"integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"w3c-keyname": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-model": {
|
||||
"version": "1.23.0",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-model/-/prosemirror-model-1.23.0.tgz",
|
||||
"integrity": "sha512-Q/fgsgl/dlOAW9ILu4OOhYWQbc7TQd4BwKH/RwmUjyVf8682Be4zj3rOYdLnYEcGzyg8LL9Q5IWYKD8tdToreQ==",
|
||||
"dependencies": {
|
||||
"orderedmap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-state": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
|
||||
"integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-transform": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-transform/-/prosemirror-transform-1.10.2.tgz",
|
||||
"integrity": "sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.34.3",
|
||||
"resolved": "https://registry.npmmirror.com/prosemirror-view/-/prosemirror-view-1.34.3.tgz",
|
||||
"integrity": "sha512-mKZ54PrX19sSaQye+sef+YjBbNu2voNwLS1ivb6aD2IRmxRGW64HU9B644+7OfJStGLyxvOreKqEgfvXa91WIA==",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "http://registry.npm.taobao.org/proxy-addr/download/proxy-addr-1.1.4.tgz",
|
||||
|
@ -5416,6 +5529,11 @@
|
|||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/rope-sequence": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmmirror.com/rope-sequence/-/rope-sequence-1.3.4.tgz",
|
||||
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "http://registry.npm.taobao.org/sax/download/sax-1.2.2.tgz",
|
||||
|
@ -6522,6 +6640,11 @@
|
|||
"vue": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "http://registry.npm.taobao.org/watchpack/download/watchpack-1.3.1.tgz",
|
||||
|
|
|
@ -8,13 +8,15 @@
|
|||
"build": "rimraf dist && webpack -p --progress --hide-modules"
|
||||
},
|
||||
"dependencies": {
|
||||
"@toast-ui/editor": "^3.2.2",
|
||||
"@toast-ui/vue-editor": "^3.2.3",
|
||||
"axios": "^0.24.0",
|
||||
"element-ui": "^2.3.4",
|
||||
"js-cookie": "^3.0.1",
|
||||
"vue": "^2.5.16",
|
||||
"vue-router": "^3.5.3",
|
||||
"vue-style-loader": "^4.1.3",
|
||||
"vuex": "^3.6.2",
|
||||
"axios": "^0.24.0",
|
||||
"js-cookie": "^3.0.1"
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<title>element-starter</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<body style="margin: 0px;">
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -4,6 +4,11 @@ import Router from 'vue-router'
|
|||
import ViewLogin from '../views/login/index'
|
||||
import ViewRegister from '../views/register/index'
|
||||
import View404 from '../views/404'
|
||||
import ViewContainer from '../views/layout/container'
|
||||
import ViewList from '../views/list/index'
|
||||
import ViewDetail from '../views/detail/index'
|
||||
import ViewCreate from '../views/create/index'
|
||||
import ViewEdit from '../views/edit/index'
|
||||
|
||||
|
||||
Vue.use(Router)
|
||||
|
@ -29,30 +34,29 @@ export const constantRoutes = [
|
|||
component: View404,
|
||||
hidden: true
|
||||
},
|
||||
// {
|
||||
// path: '/',
|
||||
// redirect: '/',
|
||||
// component: ViewContainer,
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// component: ViewList
|
||||
// },
|
||||
// {
|
||||
// path: 'detail',
|
||||
// component: ViewDetail
|
||||
// },
|
||||
// {
|
||||
// path: 'create',
|
||||
// component: ViewCreate
|
||||
// },
|
||||
// {
|
||||
// path: 'edit',
|
||||
// component: ViewEdit
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// 404 page must be placed at the end !!!
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/',
|
||||
component: ViewContainer,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: ViewList
|
||||
},
|
||||
{
|
||||
path: 'detail',
|
||||
component: ViewDetail
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: ViewCreate
|
||||
},
|
||||
{
|
||||
path: 'edit',
|
||||
component: ViewEdit
|
||||
}
|
||||
]
|
||||
},
|
||||
{ path: '*', redirect: '/404', hidden: true }
|
||||
]
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
import axios from 'axios'
|
||||
import { Message } from 'element-ui'
|
||||
import {Message} from 'element-ui'
|
||||
|
||||
|
||||
// 创建一个axios
|
||||
|
@ -25,30 +24,43 @@ service.interceptors.request.use(
|
|||
service.interceptors.response.use(
|
||||
response => {
|
||||
// Any status code that lie within the range of 2xx cause this function to trigger
|
||||
// Do something with response data
|
||||
// 判断http status是否为200
|
||||
if (response.status !== 200) {
|
||||
const data = response.data
|
||||
if (typeof data == 'string') {
|
||||
Message({
|
||||
message: data,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
}
|
||||
// Do something with result data
|
||||
// 处理 2xx 范围内的响应
|
||||
const res = response.data;
|
||||
|
||||
// 判断后端返回的 code 是否为 0(表示失败)
|
||||
if (res.code !== 1) {
|
||||
Message({
|
||||
message: res.message || '网络开小差啦!',
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
});
|
||||
|
||||
// 返回一个拒绝的 Promise,以便调用方处理错误
|
||||
console.log("执行到第一个 reject ")
|
||||
console.log(res)
|
||||
return Promise.reject(new Error(res.message || '网络开小差啦!'));
|
||||
} else {
|
||||
// 正常返回数据
|
||||
console.log("执行到下面的 else ")
|
||||
console.log(res)
|
||||
return res;
|
||||
}
|
||||
return response
|
||||
},
|
||||
error => {
|
||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
||||
// Do something with response error
|
||||
// Do something with result error
|
||||
console.log('err: ' + error) // for debug
|
||||
const message = (error.response && error.response.data && error.response.data.message)
|
||||
|| '网络开小差啦!';
|
||||
// 打印Message消息
|
||||
Message({
|
||||
message: error.response.data,
|
||||
message,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
console.log("执行到下面的 error了 ")
|
||||
console.log(message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="center" align="middle">
|
||||
<el-col :span="8">
|
||||
<el-card class="box-card" shadow="never">
|
||||
<el-form ref="form" :model="question" >
|
||||
<el-form-item >
|
||||
<el-input v-model="question.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item >
|
||||
<editor :options="editorOptions"
|
||||
height="500px"
|
||||
initialEditType="wysiwyg"
|
||||
previewStyle="vertical"
|
||||
ref="toastuiEditor"
|
||||
:initialValue="content"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="postQuestion">立即提问</el-button>
|
||||
<el-button>取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import '@toast-ui/editor/dist/toastui-editor.css';
|
||||
|
||||
import { Editor } from '@toast-ui/vue-editor';
|
||||
import request from "../../utils/request";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
editor: Editor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
question: {
|
||||
title: '',
|
||||
content: ''
|
||||
},
|
||||
content: '<p>123213213123</p>',
|
||||
editorOptions: {
|
||||
minHeight: '200px',
|
||||
language: 'en-US',
|
||||
useCommandShortcut: true,
|
||||
usageStatistics: true,
|
||||
hideModeSwitch: true,
|
||||
toolbarItems: [
|
||||
['heading', 'bold', 'italic', 'strike'],
|
||||
['hr', 'quote'],
|
||||
['ul', 'ol', 'task', 'indent', 'outdent'],
|
||||
['table', 'image', 'link'],
|
||||
['code', 'codeblock'],
|
||||
['scrollSync'],
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
postQuestion: function() {
|
||||
debugger
|
||||
let html = this.$refs.toastuiEditor.$data.editor.getHTML()
|
||||
this.question.content = html;
|
||||
const that = this
|
||||
request({
|
||||
method: 'POST',
|
||||
url: "/question/create",
|
||||
data: this.question,
|
||||
}).then(function () {
|
||||
that.$router.push({ path: '/' })
|
||||
})
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
/*width: 480px;*/
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,176 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="center" align="middle">
|
||||
<el-col :span="8">
|
||||
<el-card v-if="question" class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ question.title }} <span class="header_name" style="margin-right: 5px; float: right;"> <span
|
||||
@click="gotoQuestionEdit">修改</span> | <span @click="gotoDeleteQuestion">删除</span> </span> </span>
|
||||
</div>
|
||||
<div>
|
||||
<viewer ref="questionViewer" :options="questionViewerOptions" :initialValue="question.context"/>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-divider content-position="left">所有回答</el-divider>
|
||||
<el-card v-for="answer in question.answers" style="margin-top: 5px; " class="box-card" shadow="hover">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ answer.author.user_name }} | {{ answer.created_at | formatDate }} <span class="header_name"
|
||||
style="margin-right: 5px; float: right;"
|
||||
@click="gotoDeleteAnswer(answer.id)">删除</span></span>
|
||||
</div>
|
||||
<div>
|
||||
<viewer ref="answerViewer" :initialValue="answer.content"/>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-divider content-position="left">我来回答</el-divider>
|
||||
<el-card class="box-card" shadow="never">
|
||||
<el-form ref="form" :model="question">
|
||||
<el-form-item>
|
||||
<editor :options="editorOptions"
|
||||
:initialValue="answerContext"
|
||||
height="200px"
|
||||
initialEditType="markdown"
|
||||
ref="toastuiEditor"
|
||||
previewStyle="vertical"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="postAnswer">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import '@toast-ui/editor/dist/toastui-editor-viewer.css';
|
||||
import '@toast-ui/editor/dist/toastui-editor.css';
|
||||
|
||||
import {Editor, Viewer} from '@toast-ui/vue-editor';
|
||||
import request from "../../utils/request";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
viewer: Viewer,
|
||||
editor: Editor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: 0,
|
||||
question: null,
|
||||
questionViewerOptions: {
|
||||
usageStatistics: true,
|
||||
},
|
||||
answerContext: '',
|
||||
editorOptions: {
|
||||
minHeight: '200px',
|
||||
language: 'en-US',
|
||||
useCommandShortcut: true,
|
||||
usageStatistics: true,
|
||||
hideModeSwitch: false,
|
||||
toolbarItems: [
|
||||
['heading', 'bold', 'italic', 'strike'],
|
||||
['hr', 'quote'],
|
||||
['ul', 'ol', 'task', 'indent', 'outdent'],
|
||||
['table', 'link'],
|
||||
['code', 'codeblock'],
|
||||
['scrollSync'],
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.id) {
|
||||
let id = parseInt(this.$route.query.id)
|
||||
this.getDetail(id);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDetail: function (id) {
|
||||
const that = this
|
||||
this.id = id
|
||||
request({
|
||||
url: "/question/detail",
|
||||
method: 'POST',
|
||||
params: {
|
||||
"id": id
|
||||
}
|
||||
}).then(function (response) {
|
||||
that.question = response.data;
|
||||
})
|
||||
},
|
||||
postAnswer: function () {
|
||||
let html = this.$refs.toastuiEditor.$data.editor.getHTML()
|
||||
this.answerContext = html
|
||||
const that = this
|
||||
request({
|
||||
method: 'POST',
|
||||
url: "/answer/create",
|
||||
data: {
|
||||
"question_id": that.id,
|
||||
"context": that.answerContext,
|
||||
},
|
||||
}).then(function () {
|
||||
that.$router.go(0)
|
||||
})
|
||||
},
|
||||
gotoQuestionEdit: function () {
|
||||
this.$router.push({path: '/edit', query: {'id': this.id}})
|
||||
},
|
||||
gotoDeleteQuestion: function () {
|
||||
const that = this
|
||||
request({
|
||||
method: 'POST',
|
||||
url: "/question/delete",
|
||||
data: {
|
||||
"questionId": that.id,
|
||||
}
|
||||
}).then(function () {
|
||||
that.$router.go(0)
|
||||
})
|
||||
},
|
||||
gotoDeleteAnswer: function (id) {
|
||||
const that = this
|
||||
request({
|
||||
method: 'POST',
|
||||
url: "/answer/delete",
|
||||
data: {
|
||||
"answerId": id,
|
||||
},
|
||||
}).then(function () {
|
||||
that.$router.go(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
/*width: 480px;*/
|
||||
}
|
||||
|
||||
.header_name {
|
||||
float: right;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
margin: 0 15px 0 0;
|
||||
line-height: 34px;
|
||||
background-color: transparent;
|
||||
color: #486e9b;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header_name > a {
|
||||
background-color: transparent;
|
||||
color: #486e9b;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="center" align="middle">
|
||||
<el-col :span="8">
|
||||
<el-card class="box-card" shadow="never">
|
||||
<el-form ref="form" :model="question" label-width="80px">
|
||||
<el-form-item label="问题标题">
|
||||
<el-input v-model="question.title">{{question.title}}</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="问题描述" v-if="question.context">
|
||||
<editor :options="editorOptions"
|
||||
:initialValue="question.context"
|
||||
height="500px"
|
||||
initialEditType="wysiwyg"
|
||||
previewStyle="vertical" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">更新</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import '@toast-ui/editor/dist/toastui-editor.css';
|
||||
|
||||
import { Editor } from '@toast-ui/vue-editor';
|
||||
import request from "../../utils/request";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
editor: Editor
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.id) {
|
||||
let id = parseInt(this.$route.query.id)
|
||||
this.getDetail(id);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
question: {
|
||||
title: '',
|
||||
context: ''
|
||||
},
|
||||
editorOptions: {
|
||||
minHeight: '200px',
|
||||
language: 'en-US',
|
||||
useCommandShortcut: true,
|
||||
usageStatistics: true,
|
||||
hideModeSwitch: true,
|
||||
toolbarItems: [
|
||||
['heading', 'bold', 'italic', 'strike'],
|
||||
['hr', 'quote'],
|
||||
['ul', 'ol', 'task', 'indent', 'outdent'],
|
||||
['table', 'image', 'link'],
|
||||
['code', 'codeblock'],
|
||||
['scrollSync'],
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDetail: function (id) {
|
||||
const that = this
|
||||
this.id = id
|
||||
request({
|
||||
url: "/question/detail",
|
||||
method: 'POST',
|
||||
data: {
|
||||
"questionId": id
|
||||
}
|
||||
}).then(function (response) {
|
||||
that.question = response.data;
|
||||
})
|
||||
},
|
||||
onSubmit: function () {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
/*width: 480px;*/
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,171 @@
|
|||
<template>
|
||||
<el-row type="flex" justify="center" align="middle">
|
||||
<el-col :span="8">
|
||||
<div class="infinite-list-wrapper" style="overflow:auto">
|
||||
<ul
|
||||
class="list"
|
||||
v-infinite-scroll="load"
|
||||
infinite-scroll-disabled="disabled">
|
||||
<el-card v-for="question in questions" class="box-card" shadow="hover">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{question.title}}</span>
|
||||
</div>
|
||||
<div class="text item">
|
||||
{{question.context}}
|
||||
</div>
|
||||
<div class="bottom clearfix">
|
||||
<time class="time">{{question.created_at}} | {{question.author.user_name}} | {{question.answer_num}} 回答</time>
|
||||
<el-button type="text" class="button" @click="gotoDetail(question.id)">去看看</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</ul>
|
||||
<p v-if="loading" class="loading_tips">加载中...</p>
|
||||
<p v-if="disabled" class="loading_tips">没有更多了</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import request from "../../utils/request";
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
count: 10,
|
||||
start: 0,
|
||||
size: 10,
|
||||
questions: [],
|
||||
loading: false,
|
||||
noMore: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getQuestions();
|
||||
},
|
||||
computed: {
|
||||
disabled () {
|
||||
return this.loading || this.noMore
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
load () {
|
||||
if (this.noMore === true) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
setTimeout(() => {
|
||||
this.loading = false
|
||||
this.getQuestions()
|
||||
}, 2000)
|
||||
},
|
||||
getQuestions() {
|
||||
const that = this;
|
||||
request({
|
||||
url: '/question/list',
|
||||
method: 'POST',
|
||||
data: {
|
||||
start: this.start,
|
||||
size: this.size,
|
||||
}
|
||||
}).then(function (response) {
|
||||
const questions = response.data
|
||||
if (questions === null || questions.length === 0) {
|
||||
that.noMore = true
|
||||
}
|
||||
that.questions = that.questions.concat(questions)
|
||||
that.start = that.start + questions.length
|
||||
})
|
||||
this.loading = false;
|
||||
},
|
||||
gotoDetail(id) {
|
||||
// go to detail page
|
||||
this.$router.push({path: '/detail', query:{'id': id}})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.loading_tips {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 13px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.carousel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
margin-top: 10px;
|
||||
/*height: 240px;*/
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.el-carousel__item h3 {
|
||||
color: #475669;
|
||||
font-size: 18px;
|
||||
opacity: 0.75;
|
||||
line-height: 300px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n) {
|
||||
background-color: #99a9bf;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n+1) {
|
||||
background-color: #d3dce6;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 13px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
clear: both
|
||||
}
|
||||
|
||||
|
||||
</style>
|
Loading…
Reference in New Issue