补充自定义类型的自定义序列化
This commit is contained in:
parent
45f3bfae44
commit
0952098885
@ -55,6 +55,7 @@ func (h *BlogApiHandler) Init() error {
|
|||||||
r.POST("", h.CreateBlog)
|
r.POST("", h.CreateBlog)
|
||||||
r.PUT(":id", h.UpdateBlog)
|
r.PUT(":id", h.UpdateBlog)
|
||||||
r.DELETE(":id", h.DeleteBlog)
|
r.DELETE(":id", h.DeleteBlog)
|
||||||
|
r.POST(":id/publish", h.PublishBlog)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +97,27 @@ func (h *BlogApiHandler) QueryBlog(ctx *gin.Context) {
|
|||||||
response.Success(ctx, ins)
|
response.Success(ctx, ins)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *BlogApiHandler) PublishBlog(ctx *gin.Context) {
|
||||||
|
idInt, err := strconv.ParseInt(ctx.Param("id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
response.Failed(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := blog.NewPublishBlogRequest(uint(idInt))
|
||||||
|
if err := ctx.BindJSON(req); err != nil {
|
||||||
|
response.Failed(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ins, err := h.blog.PublishBlog(ctx.Request.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
response.Failed(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(ctx, ins)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *BlogApiHandler) DescribeBlog(ctx *gin.Context) {
|
func (h *BlogApiHandler) DescribeBlog(ctx *gin.Context) {
|
||||||
idInt, err := strconv.ParseInt(ctx.Param("id"), 10, 64)
|
idInt, err := strconv.ParseInt(ctx.Param("id"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -131,6 +153,11 @@ func (h *BlogApiHandler) UpdateBlog(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
in := blog.NewUpdateBlogRequest(uint(idInt))
|
in := blog.NewUpdateBlogRequest(uint(idInt))
|
||||||
|
if err := ctx.BindJSON(in); err != nil {
|
||||||
|
response.Failed(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ins, err := h.blog.UpdateBlog(ctx.Request.Context(), in)
|
ins, err := h.blog.UpdateBlog(ctx.Request.Context(), in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Failed(ctx, err)
|
response.Failed(ctx, err)
|
||||||
|
@ -1,7 +1,43 @@
|
|||||||
package blog
|
package blog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type STAGE int
|
type STAGE int
|
||||||
|
|
||||||
|
func (s STAGE) String() string {
|
||||||
|
return STAGE_MAPPING[s]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义 类型的序列化方式
|
||||||
|
// "草稿"
|
||||||
|
func (s STAGE) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(`"` + s.String() + `"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON([]byte) error
|
||||||
|
func (s *STAGE) UnmarshalJSON(data []byte) error {
|
||||||
|
str := strings.Trim(string(data), `"`)
|
||||||
|
switch str {
|
||||||
|
case "草稿":
|
||||||
|
*s = STAGE_DRAFT
|
||||||
|
// v := STAGE_DRAFT
|
||||||
|
// s = &v
|
||||||
|
case "已发布":
|
||||||
|
*s = STAGE_PUBLISHED
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的发布类型")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var STAGE_MAPPING = map[STAGE]string{
|
||||||
|
STAGE_DRAFT: "草稿",
|
||||||
|
STAGE_PUBLISHED: "已发布",
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
STAGE_DRAFT STAGE = iota
|
STAGE_DRAFT STAGE = iota
|
||||||
STAGE_PUBLISHED
|
STAGE_PUBLISHED
|
||||||
|
17
vblog/apps/blog/impl/dao.go
Normal file
17
vblog/apps/blog/impl/dao.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/infraboard/mcube/v2/ioc/config/datasource"
|
||||||
|
"gitlab.com/go-course-project/go17/vblog/apps/blog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *BlogServiceImpl) update(ctx context.Context, ins *blog.Blog) error {
|
||||||
|
err := datasource.DBFromCtx(ctx).Where("id = ?", ins.Id).Updates(ins).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -100,11 +100,38 @@ func (b *BlogServiceImpl) DescribeBlog(ctx context.Context, in *blog.DescribeBlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PublishBlog implements blog.Service.
|
// PublishBlog implements blog.Service.
|
||||||
func (b *BlogServiceImpl) PublishBlog(context.Context, *blog.PublishBlogRequest) (*blog.Blog, error) {
|
func (b *BlogServiceImpl) PublishBlog(ctx context.Context, in *blog.PublishBlogRequest) (*blog.Blog, error) {
|
||||||
panic("unimplemented")
|
ins, err := b.DescribeBlog(ctx, blog.NewDescribeBlogRequest(in.Id))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.Stage = in.Stage
|
||||||
|
|
||||||
|
err = b.update(ctx, ins)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateBlog implements blog.Service.
|
// UpdateBlog implements blog.Service.
|
||||||
func (b *BlogServiceImpl) UpdateBlog(context.Context, *blog.UpdateBlogRequest) (*blog.Blog, error) {
|
func (b *BlogServiceImpl) UpdateBlog(ctx context.Context, in *blog.UpdateBlogRequest) (*blog.Blog, error) {
|
||||||
panic("unimplemented")
|
ins, err := b.DescribeBlog(ctx, blog.NewDescribeBlogRequest(in.Id))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全量更新
|
||||||
|
ins.CreateBlogRequest = in.CreateBlogRequest
|
||||||
|
if err := ins.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = b.update(ctx, ins)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ins, nil
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,23 @@ func TestCreateBlog(t *testing.T) {
|
|||||||
t.Log(ins)
|
t.Log(ins)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateBlog(t *testing.T) {
|
||||||
|
ins, err := blog.GetService().DescribeBlog(ctx, blog.NewDescribeBlogRequest(31))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := blog.NewUpdateBlogRequest(31)
|
||||||
|
req.CreateBlogRequest = ins.CreateBlogRequest
|
||||||
|
req.Summary = "for update"
|
||||||
|
|
||||||
|
ins, err = blog.GetService().UpdateBlog(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(ins)
|
||||||
|
}
|
||||||
|
|
||||||
// SELECT * FROM `blogs` ORDER BY created_at DESC LIMIT 20
|
// SELECT * FROM `blogs` ORDER BY created_at DESC LIMIT 20
|
||||||
func TestQueryBlog(t *testing.T) {
|
func TestQueryBlog(t *testing.T) {
|
||||||
req := blog.NewQueryBlogRequest()
|
req := blog.NewQueryBlogRequest()
|
||||||
@ -42,6 +59,25 @@ func TestQueryBlog(t *testing.T) {
|
|||||||
t.Log(ins)
|
t.Log(ins)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDescribeBlog(t *testing.T) {
|
||||||
|
req := blog.NewDescribeBlogRequest(31)
|
||||||
|
ins, err := blog.GetService().DescribeBlog(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(ins)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPublishBlog(t *testing.T) {
|
||||||
|
req := blog.NewPublishBlogRequest(31)
|
||||||
|
req.Stage = blog.STAGE_PUBLISHED
|
||||||
|
ins, err := blog.GetService().PublishBlog(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(ins)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewQueryBlogRequest(t *testing.T) {
|
func TestNewQueryBlogRequest(t *testing.T) {
|
||||||
req := blog.NewQueryBlogRequest()
|
req := blog.NewQueryBlogRequest()
|
||||||
req.SetTag("key1=value1,key2=value2")
|
req.SetTag("key1=value1,key2=value2")
|
||||||
|
@ -53,6 +53,12 @@ type DeleteBlogRequest struct {
|
|||||||
utils.GetRequest
|
utils.GetRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPublishBlogRequest(id uint) *PublishBlogRequest {
|
||||||
|
return &PublishBlogRequest{
|
||||||
|
GetRequest: *utils.NewGetRequest(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PublishBlogRequest struct {
|
type PublishBlogRequest struct {
|
||||||
utils.GetRequest
|
utils.GetRequest
|
||||||
StatusSpec
|
StatusSpec
|
||||||
|
@ -386,3 +386,106 @@
|
|||||||
{"level":"info","component":"server","time":"2025-01-19T11:59:44+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
{"level":"info","component":"server","time":"2025-01-19T11:59:44+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
{"level":"info","component":"server","time":"2025-01-19T11:59:44+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
{"level":"info","component":"server","time":"2025-01-19T11:59:44+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
{"level":"info","component":"http","time":"2025-01-19T11:59:44+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
{"level":"info","component":"http","time":"2025-01-19T11:59:44+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:14:40+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:14:40+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:14:40+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:14:40+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:14:42+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:14:42+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:14:42+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:14:42+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:14:42+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:14:42+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:14:42+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:16:02+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:16:02+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:16:02+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:16:02+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:16:04+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:16:04+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:16:04+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:16:04+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:16:04+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:16:04+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:16:04+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:24:36+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:24:36+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:24:36+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:24:36+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:24:39+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:24:39+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:24:39+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:24:39+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:24:39+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:24:39+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:24:39+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:26:25+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:26:25+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:26:25+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:26:25+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:26:28+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:26:28+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:26:28+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:26:28+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:26:28+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:26:28+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:26:28+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:27:10+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:27:10+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:27:10+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:27:10+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:27:12+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:27:12+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:27:12+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:27:12+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:27:12+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:27:12+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:27:12+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:32:57+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:32:57+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:32:57+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:32:57+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:32:59+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:32:59+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:32:59+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:32:59+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:32:59+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:32:59+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:32:59+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:39:58+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:39:58+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:39:58+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:39:58+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:40:00+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:40:00+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:40:00+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:40:00+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:40:00+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:40:00+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:40:00+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:41:34+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:41:34+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:41:34+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:41:34+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:41:36+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:41:36+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:41:36+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:41:36+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:41:36+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:41:36+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:41:36+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:45:34+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:45:34+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:45:34+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:45:34+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
{"level":"info","component":"gin_webframework","time":"2025-01-19T14:45:36+08:00","caller":"config/gin/framework.go:41","message":"enable gin recovery"}
|
||||||
|
{"level":"debug","time":"2025-01-19T14:45:36+08:00","caller":"token/impl/impl.go:39","message":"DefaultExpiredTTL: 3600"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:45:36+08:00","caller":"ioc/server/server.go:74","message":"loaded configs: [app.v1 trace.v1 log.v1 validator.v1 gin_webframework.v1 datasource.v1 grpc.v1 http.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:45:36+08:00","caller":"ioc/server/server.go:75","message":"loaded controllers: [token.v1 user.v1 blog.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:45:36+08:00","caller":"ioc/server/server.go:76","message":"loaded apis: [blogs.v1 tokens.v1]"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:45:36+08:00","caller":"ioc/server/server.go:77","message":"loaded defaults: []"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:45:36+08:00","caller":"config/http/http.go:144","message":"HTTP服务启动成功, 监听地址: 127.0.0.1:8080"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:50:39+08:00","caller":"ioc/server/server.go:101","message":"receive signal 'interrupt', start graceful shutdown"}
|
||||||
|
{"level":"info","component":"http","time":"2025-01-19T14:50:39+08:00","caller":"config/http/http.go:152","message":"start graceful shutdown"}
|
||||||
|
{"level":"error","component":"http","time":"2025-01-19T14:50:39+08:00","caller":"config/http/http.go:146","message":"http: Server closed"}
|
||||||
|
{"level":"info","component":"server","time":"2025-01-19T14:50:39+08:00","caller":"ioc/server/server.go:115","message":"http service stop complete"}
|
||||||
|
@ -587,7 +587,7 @@ const deleteBlog = async (id) => {
|
|||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
```
|
```
|
||||||
|
|
||||||
## 文章的创建
|
## 文章的创建和更新
|
||||||
|
|
||||||
选择一个mardown的编辑器: https://www.npmjs.com/package/md-editor-v3
|
选择一个mardown的编辑器: https://www.npmjs.com/package/md-editor-v3
|
||||||
|
|
||||||
@ -598,3 +598,168 @@ const deleteBlog = async (id) => {
|
|||||||
```
|
```
|
||||||
|
|
||||||
创建成功后,进入到保存模式
|
创建成功后,进入到保存模式
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 页头 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<a-page-header @back="$router.go(-1)" :title="isEdit ? '编辑文章' : '创建文章'">
|
||||||
|
<template #extra>
|
||||||
|
<a-button :loading="saveLoading" @click="save" type="primary">
|
||||||
|
<template #icon>
|
||||||
|
<icon-save />
|
||||||
|
</template>
|
||||||
|
保存
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-page-header>
|
||||||
|
</div>
|
||||||
|
<!-- form表单 -->
|
||||||
|
<a-form ref="formRef" :model="form" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
field="title"
|
||||||
|
:rules="{ required: true, message: '请输入文章的标题' }"
|
||||||
|
label="标题"
|
||||||
|
>
|
||||||
|
<a-input v-model="form.title" placeholder="请填写文章的标题" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:rules="{ required: true, message: '请选择文章分类' }"
|
||||||
|
field="category"
|
||||||
|
label="分类"
|
||||||
|
>
|
||||||
|
<a-select v-model="form.category" placeholder="请选择文章分类">
|
||||||
|
<a-option>软件开发</a-option>
|
||||||
|
<a-option>系统运维</a-option>
|
||||||
|
<a-option>软件测试</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="summary" label="概要">
|
||||||
|
<a-textarea
|
||||||
|
v-model="form.summary"
|
||||||
|
:auto-size="{ minRows: 4, maxRows: 4 }"
|
||||||
|
placeholder="请填写文章的概要信息"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="content" label="文章内容">
|
||||||
|
<MdEditor class="editor" v-model="form.content" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref, useTemplateRef } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { MdEditor } from 'md-editor-v3'
|
||||||
|
import 'md-editor-v3/lib/style.css'
|
||||||
|
import { CREATE_BLOG, DESCRIBE_BLOG, UPDATE_BLOG } from '@/api/blog'
|
||||||
|
|
||||||
|
const formInstance = useTemplateRef('formRef')
|
||||||
|
|
||||||
|
// 查询当前页面的路由信息
|
||||||
|
const route = useRoute()
|
||||||
|
const isEdit = ref(route.query.id ? true : false)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (isEdit.value) {
|
||||||
|
const resp = await DESCRIBE_BLOG(route.query.id)
|
||||||
|
Object.assign(form, { ...resp })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
title: '',
|
||||||
|
summary: '',
|
||||||
|
content: '',
|
||||||
|
category: '',
|
||||||
|
tags: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 文章的保存
|
||||||
|
const saveLoading = ref(false)
|
||||||
|
const save = () => {
|
||||||
|
if (isEdit.value) {
|
||||||
|
// 更新
|
||||||
|
formInstance.value.validate(async (errors) => {
|
||||||
|
if (errors === undefined) {
|
||||||
|
try {
|
||||||
|
saveLoading.value = true
|
||||||
|
await UPDATE_BLOG(route.query.id, form)
|
||||||
|
} finally {
|
||||||
|
saveLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 数据提交给后端之前,我们是需要前端校验数据合法性
|
||||||
|
// formInstance.validate()
|
||||||
|
// formInstance
|
||||||
|
// https://cn.vuejs.org/guide/essentials/template-refs.html
|
||||||
|
formInstance.value.validate(async (errors) => {
|
||||||
|
if (errors === undefined) {
|
||||||
|
try {
|
||||||
|
saveLoading.value = true
|
||||||
|
const resp = await CREATE_BLOG(form)
|
||||||
|
router.replace({ name: 'backend_blog_edit', query: { id: resp.id } })
|
||||||
|
isEdit.value = true
|
||||||
|
} finally {
|
||||||
|
saveLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.editor {
|
||||||
|
height: calc(100vh - 100px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 文章的发布
|
||||||
|
|
||||||
|
后端如何自定义状态展示
|
||||||
|
|
||||||
|
```go
|
||||||
|
type STAGE int
|
||||||
|
|
||||||
|
func (s STAGE) String() string {
|
||||||
|
return STAGE_MAPPING[s]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义 类型的序列化方式
|
||||||
|
// "草稿"
|
||||||
|
func (s STAGE) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(`"` + s.String() + `"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON([]byte) error
|
||||||
|
func (s *STAGE) UnmarshalJSON(data []byte) error {
|
||||||
|
str := strings.Trim(string(data), `"`)
|
||||||
|
switch str {
|
||||||
|
case "草稿":
|
||||||
|
*s = STAGE_DRAFT
|
||||||
|
case "已发布":
|
||||||
|
*s = STAGE_PUBLISHED
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("不支持的发布类型")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var STAGE_MAPPING = map[STAGE]string{
|
||||||
|
STAGE_DRAFT: "草稿",
|
||||||
|
STAGE_PUBLISHED: "已发布",
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
STAGE_DRAFT STAGE = iota
|
||||||
|
STAGE_PUBLISHED
|
||||||
|
)
|
||||||
|
```
|
||||||
|
@ -31,6 +31,13 @@ export const UPDATE_BLOG = (blog_id, data) =>
|
|||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const PUBLISH_BLOG = (blog_id, data) =>
|
||||||
|
client({
|
||||||
|
url: `/api/vblog/v1/blogs/${blog_id}/publish`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
|
||||||
export const DELTE_BLOG = (blog_id) =>
|
export const DELTE_BLOG = (blog_id) =>
|
||||||
client({
|
client({
|
||||||
url: `/api/vblog/v1/blogs/${blog_id}`,
|
url: `/api/vblog/v1/blogs/${blog_id}`,
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<a-table-column title="编码" data-index="id"></a-table-column>
|
<a-table-column title="编码" data-index="id"></a-table-column>
|
||||||
<a-table-column title="标题" data-index="title"></a-table-column>
|
<a-table-column title="标题" data-index="title"></a-table-column>
|
||||||
<a-table-column title="分类" data-index="category"></a-table-column>
|
<a-table-column title="分类" data-index="category"></a-table-column>
|
||||||
|
<a-table-column title="状态" data-index="stage"></a-table-column>
|
||||||
<a-table-column title="更新时间">
|
<a-table-column title="更新时间">
|
||||||
<template #cell="{ record }">
|
<template #cell="{ record }">
|
||||||
<span>{{ dayjs(record.updated_at).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
<span>{{ dayjs(record.updated_at).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||||
@ -49,7 +50,7 @@
|
|||||||
</template>
|
</template>
|
||||||
编辑
|
编辑
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary" @click="$modal.info({ title: 'Name', content: record.id })">
|
<a-button type="primary" @click="publishBlog(record.id)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-send />
|
<icon-send />
|
||||||
</template>
|
</template>
|
||||||
@ -81,7 +82,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import { QUERY_BLOG, DELTE_BLOG } from '@/api/blog'
|
import { QUERY_BLOG, DELTE_BLOG, PUBLISH_BLOG } from '@/api/blog'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
// API 请求数据
|
// API 请求数据
|
||||||
@ -138,6 +139,21 @@ const pageSizeChanged = (pageSize) => {
|
|||||||
queryData()
|
queryData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 发布
|
||||||
|
const publishLoadding = ref(0)
|
||||||
|
const publishBlog = async (id) => {
|
||||||
|
try {
|
||||||
|
publishLoadding.value = id
|
||||||
|
// 等待这个操作执行完成
|
||||||
|
await PUBLISH_BLOG(id, { stage: "已发布" })
|
||||||
|
// 刷新数据
|
||||||
|
queryData()
|
||||||
|
} finally {
|
||||||
|
publishLoadding.value = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user