补充自定义类型的自定义序列化

This commit is contained in:
yumaojun03 2025-01-19 14:51:14 +08:00
parent 45f3bfae44
commit 0952098885
10 changed files with 447 additions and 7 deletions

View File

@ -55,6 +55,7 @@ func (h *BlogApiHandler) Init() error {
r.POST("", h.CreateBlog)
r.PUT(":id", h.UpdateBlog)
r.DELETE(":id", h.DeleteBlog)
r.POST(":id/publish", h.PublishBlog)
return nil
}
@ -96,6 +97,27 @@ func (h *BlogApiHandler) QueryBlog(ctx *gin.Context) {
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) {
idInt, err := strconv.ParseInt(ctx.Param("id"), 10, 64)
if err != nil {
@ -131,6 +153,11 @@ func (h *BlogApiHandler) UpdateBlog(ctx *gin.Context) {
}
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)
if err != nil {
response.Failed(ctx, err)

View File

@ -1,7 +1,43 @@
package blog
import (
"fmt"
"strings"
)
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 (
STAGE_DRAFT STAGE = iota
STAGE_PUBLISHED

View 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
}

View File

@ -100,11 +100,38 @@ func (b *BlogServiceImpl) DescribeBlog(ctx context.Context, in *blog.DescribeBlo
}
// PublishBlog implements blog.Service.
func (b *BlogServiceImpl) PublishBlog(context.Context, *blog.PublishBlogRequest) (*blog.Blog, error) {
panic("unimplemented")
func (b *BlogServiceImpl) PublishBlog(ctx context.Context, in *blog.PublishBlogRequest) (*blog.Blog, error) {
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.
func (b *BlogServiceImpl) UpdateBlog(context.Context, *blog.UpdateBlogRequest) (*blog.Blog, error) {
panic("unimplemented")
func (b *BlogServiceImpl) UpdateBlog(ctx context.Context, in *blog.UpdateBlogRequest) (*blog.Blog, error) {
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
}

View File

@ -29,6 +29,23 @@ func TestCreateBlog(t *testing.T) {
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
func TestQueryBlog(t *testing.T) {
req := blog.NewQueryBlogRequest()
@ -42,6 +59,25 @@ func TestQueryBlog(t *testing.T) {
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) {
req := blog.NewQueryBlogRequest()
req.SetTag("key1=value1,key2=value2")

View File

@ -53,6 +53,12 @@ type DeleteBlogRequest struct {
utils.GetRequest
}
func NewPublishBlogRequest(id uint) *PublishBlogRequest {
return &PublishBlogRequest{
GetRequest: *utils.NewGetRequest(id),
}
}
type PublishBlogRequest struct {
utils.GetRequest
StatusSpec

View File

@ -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: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":"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"}

View File

@ -587,7 +587,7 @@ const deleteBlog = async (id) => {
</a-popconfirm>
```
## 文章的创建
## 文章的创建和更新
选择一个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
)
```

View File

@ -31,6 +31,13 @@ export const UPDATE_BLOG = (blog_id, 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) =>
client({
url: `/api/vblog/v1/blogs/${blog_id}`,

View File

@ -35,6 +35,7 @@
<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="category"></a-table-column>
<a-table-column title="状态" data-index="stage"></a-table-column>
<a-table-column title="更新时间">
<template #cell="{ record }">
<span>{{ dayjs(record.updated_at).format('YYYY-MM-DD HH:mm:ss') }}</span>
@ -49,7 +50,7 @@
</template>
编辑
</a-button>
<a-button type="primary" @click="$modal.info({ title: 'Name', content: record.id })">
<a-button type="primary" @click="publishBlog(record.id)">
<template #icon>
<icon-send />
</template>
@ -81,7 +82,7 @@
<script setup>
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'
// API
@ -138,6 +139,21 @@ const pageSizeChanged = (pageSize) => {
queryData()
}
//
const publishLoadding = ref(0)
const publishBlog = async (id) => {
try {
publishLoadding.value = id
//
await PUBLISH_BLOG(id, { stage: "已发布" })
//
queryData()
} finally {
publishLoadding.value = 0
}
}
</script>
<style lang="css" scoped>