java转go第五阶段(2) 前后端分离个人博客系统


前言

这篇文章主要记录学习黑马react文章发布功能,以及用go编写后端代码的过程。


前端项目
后端项目

一、学习经过

  • 学习富文本编辑器又发现了一个干货十足的个人博客
  • 来自上面那个博客指路的slate富文本编辑器 使用文档

二、学习总结

1富文本编辑器Slate

听说这东西是一个深坑,我跳了两天没整明白,怕报错删成了最基础的组件。

子组件RichTextEditor

import React, { useState} from 'react';
import {createEditor, Editor, Transforms} from 'slate';
import { Slate, Editable, withReact } from 'slate-react';
//TODO 啥也不是,整个框架占位置,现在白板的跟text没区别

const RichTextEditor = ({value, onChange}) => {
    // 创建一个 Slate 编辑器实例
    const [editor] = useState(() => withReact(createEditor()))

    return (
        <Slate
            editor={editor}
            value={value}
            onChange={(newValue) => onChange(newValue)}
            initialValue={value}>
            <Editable
                placeholder="请输入内容..."
                style={{ border: '1px solid #ccc', padding: '10px' }}
            />
        </Slate>
    );
};



export default RichTextEditor;

父组件中传入初始化内容,通过onChange获取修改后的值

initialValues={{
                        type: 0 ,
                        coverURL : '',
                        richText : [
                            {
                                type: 'paragraph',
                                children: [{ text: '开始输入你的文本...' }],
                            },
                        ]}}
                 <Form.Item
                        label="内容"
                        name="richText"
                        rules={[{ required: true, message: '请输入文章内容' }]}
                    >
                        <RichTextEditor
                            value={form.getFieldValue('richText')}
                            onChange={(newValue)=> form.setFieldValue({richText : newValue})}/>
                    </Form.Item>

2 注意GORM的更新语句

GORM有两种更新方式,

  • Save:此方法会更新记录的所有字段。无论你是否修改了某个字段,Save方法都会将结构体中的所有字段值更新到数据库对应的记录里。
	return ur.db.Save(user).Error

  • Updates:该方法仅更新你指定的字段。你能够选择性地更新部分字段,而不会影响其他字段。
	return ur.db.Model(user).Updates(user).Error

需要格外注意的是,一般用ai生成的往往采用Save方式,但这种方式会导致当更新时间为空时,默认使用类型的零值做填充,而时间的零值会触发MySQL的报错,导致修改不成功。而使用Updates则不会有这种情况,Gin框架会自动管理更新时间为当前时间。

3 ant的Form组件带token的上传

不带token的话直接用action属性加请求地址
在这里插入图片描述
但如果要在请求头带上token的话,可以这样写,我的是拆分到了各模块里,看起来优雅点,具体可以去看我的项目源码

// 上传图片
export function uploadImageAPI(file) {
    const formData = new FormData();
    formData.append('image', file);

    return request({
        url: '/upload',  // 根据实际接口调整
        method: 'post',
        data: formData,
    });
}
// 封装上传处理函数
    const handleUpload = async ({ file, onSuccess, onError }) => {
        try {
            const res = await uploadImageAPI(file);
            onSuccess(res.data); // 通知Upload组件上传成功
            // 将返回的图片URL存入表单(根据实际接口结构调整)
            form.setFieldsValue({ coverURL: res.data.url });
        } catch (error) {
            onError(error); // 通知Upload组件上传失败
            if (error.response && error.response.status === 400) {
                console.error('上传失败:', error.response.data);
            } else {
                console.error('其他错误:', error);
            }
        }
    };

 <Upload
       listType="picture-card"
	   showUploadList
	   name = "image"
       onChange={onChange}
       customRequest={handleUpload}
>
       <div style={{ marginTop: 8 }}>
       		<PlusOutlined />
       </div>
</Upload>

4 go集成阿里云oss服务器

初始化oss并放入全局变量方便取用

// 初始化OSS客户端
func InitOSS() {
	// 获取OSS配置
	accessKeyID := global.Cfg.OSSAccessKeyId
	accessKeySecret := global.Cfg.OSSAccessKeySecret
	endpoint := global.Cfg.OSSEndpoint
	bucketName := global.Cfg.OSSBucketName

	// 初始化OSS客户端
	client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
	if err != nil {
		global.Logger.Fatalf("无法初始化OSS客户端: %v", err)
	}
	// 获取存储桶
	global.OSSBucket, err = client.Bucket(bucketName)
	if err != nil {
		global.Logger.Fatalf("无法获取OSS存储桶: %v", err)
	}
}

访问oss服务器进行文件上传的操作进行封装


// 文件上传

func UploadFile(file *multipart.FileHeader) (string, error) {
	src, err := file.Open()
	if err != nil {
		global.Logger.Println("无法打开文件: %v", err)
		return "", err
	}
	defer src.Close()
	// 指定文件夹名,项目统一上传到指定文件夹下
	folderName := "GIN-API/"
	fileName := folderName + file.Filename
	err = global.OSSBucket.PutObject(fileName, src)
	if err != nil {
		global.Logger.Println("无法上传文件到OSS: %v", err)
		return "", err
	}
	// 返回文件URL
	url := "https://" + global.Cfg.OSSBucketName + "." + global.Cfg.OSSEndpoint + "/" + fileName
	return url, nil
}

图片上传的具体handler逻辑

func UploadImage(c *gin.Context) {
	// 获取上传的图片文件
	file, err := c.FormFile("image")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "上传文件失败"})
		global.Logger.Println("上传文件失败: %v", err)
		return
	}
	// 穷的慌怕上传大了
	if file.Size > 1024*1024*2 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "上传文件大小不能超过2MB"})
		return
	}
	url, err := utils.UploadFile(file)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "上传文件失败"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"url": url})
}

整体项目架构可以看后端项目源码


总结

前端与后端的开发是两种不太一样的思维方式,后端采取分层的思维方式,每一层都只完成特定的功能,然后逐层传递,相互调用。而前端往往由一个个组件构成,相对于前端要更为松散,颗粒度也更细,也有可能是我前端学习不够,没法拥有一种宏观的视觉所导致的。此外全栈的开发有助于对问题的定位,出bug的时候可以拥有多种手段定位到问题所在,方便对bug进行打印调试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值