抖声后端开发记录(3)| 青训营笔记

92 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天

社交接口

聊天接口

还是从建表开始:

type ChatRecord struct {
	Id         int64  `gorm:"primary_key;AUTO_INCREMENT" json:"id,omitempty"`
	ChatKey    string `json:"chat_key"`
	FromUserId int64  `json:"from_user_id"`
	ToUserId   int64  `json:"to_user_id"`
	Content    string `json:"content"`
	CreatedAt  int64  `json:"created_at,omitempty"`
}

这个chatKey才是来索引的,我们的聊天实际上就是两个人之间的数据。所以使用一个chatKey来将不同人之间的数据进行分组。

DAO:

type ChatRecordDao struct {
	db *gorm.DB
}

var (
	chatRecordDao  *ChatRecordDao
	chatRecordOnce sync.Once
)

func NewChatRecordInstance() *ChatRecordDao {
	chatRecordOnce.Do(func() {
		chatRecordDao = &ChatRecordDao{
			db: db,
		}
	})
	return chatRecordDao
}

chat部分需要请求两种数据,一种是创建,我发送的数据要在数据库中创建。一种是请求,我请求指定的数据。这里前端使用的是pre_message来确定请求的是什么数据。是一个timestamp的int64值。

为此我们提供两个接口即可:

func (c *ChatRecordDao) Create(r *ChatRecord) error {
	return c.db.Save(r).Error
}

func (c *ChatRecordDao) ListByKey(chatKey string) (*[]ChatRecord, error) {
	var cr []ChatRecord
	result := c.db.Where("chat_key = ?", chatKey).Order("created_at ASC").Find(&cr)
	return &cr, result.Error
}

func (c *ChatRecordDao) ListByKeyPretime(chatKey string, timestamp int64) (*[]ChatRecord, error) {
	var cr []ChatRecord
	result := c.db.Where("chat_key = ? and created_at > ?", chatKey, timestamp).Order("created_at ASC").Find(&cr)
	return &cr, result.Error
}

这里这个created_at的条件得是大于,不能带等于,因为我们的操作基本上都是在1s内完成的,如果带等于的话,会一直返回最后一条数据。

下面是服务相关。生成chatkey的操作我们也放在这里了。

func SaveMsg(r *repository.ChatRecord) error {
	return repository.NewChatRecordInstance().Create(r)
}

func GetMsgList(chatKey string) (*[]repository.ChatRecord, error) {
	return repository.NewChatRecordInstance().ListByKey(chatKey)
}

func GetAddedMsg(chatKey string, timestamp int64) (*[]repository.ChatRecord, error) {
	return repository.NewChatRecordInstance().ListByKeyPretime(chatKey, timestamp)
}

// simple follow for the exist genChatKey function
func GenChatKey(userA int64, userB int64) string {
	if userA > userB {
		return fmt.Sprintf("%d_%d", userB, userA)
	}
	return fmt.Sprintf("%d_%d", userA, userB)
}

完成服务接口,我们的额服务接口都很简单,有的时候我都觉得可以之间把这一层删了,为了保持良好的架构还是忍住了。

func MessageAction(c *gin.Context) {

	toUserId := c.Query("to_user_id")
	content := c.Query("content")

	id := c.GetInt64("UserID")

	userIdB, _ := strconv.ParseInt(toUserId, 10, 64)
	chatKey := service_chat.GenChatKey(id, userIdB)
	curMessage := repository.ChatRecord{
		ChatKey:    chatKey,
		FromUserId: id,
		ToUserId:   userIdB,
		Content:    content,
	}
	err := service_chat.SaveMsg(&curMessage)
	if err != nil {
		c.JSON(http.StatusOK, Response{StatusCode: 2, StatusMsg: "Save chat record faild."})
		return
	}

	c.JSON(http.StatusOK, Response{StatusCode: 0})
}

在send message的handle中,我们要拿取目标用户和发送用户,还有内容。 这里少了一些验证,记得自己加。随后生成我们需要创建的数据,送入到数据库中。返回操作成功与否。

func MessageChat(c *gin.Context) {

	toUserId := c.Query("to_user_id")

	var pre_time int64
	var err error

	pre_time_str := c.Query("pre_msg_time")
	pre_time, err = strconv.ParseInt(pre_time_str, 10, 64)
	if err != nil {
		pre_time = 0
	}

	id := c.GetInt64("UserID")

	userIdB, err := strconv.ParseInt(toUserId, 10, 64)
	if err != nil {
		c.JSON(http.StatusOK, Response{StatusCode: 1, StatusMsg: "Invaild user id"})
		return
	}
	chatKey := service_chat.GenChatKey(id, userIdB)

	chatRecord, err := service_chat.GetAddedMsg(chatKey, pre_time)
	if err != nil {
		log.Printf("[WARN] Fetch chat list faild. %s", err)
		c.JSON(http.StatusOK, Response{StatusCode: 2, StatusMsg: "Fetch chat list faild."})
		return
	}
	resp := make([]Message, len(*chatRecord))
	for i, obj := range *chatRecord {
		resp[i] = *RepoChatToMsg(&obj)
	}

	c.JSON(http.StatusOK, ChatResponse{Response: Response{StatusCode: 0}, MessageList: resp})
}

最后是我们的获取消息列表的handle。注意我们要对pre_msg_time的合法性做校验。如果不提供这个值怎么办,提供错误的值怎么办。

这里如果没给或者给错了都会使得pre_msg_time为0.其实也就是要返回全部数据(这个逻辑其实还是有点瑕疵的)。

随后生成chatkey,将pre_msg_time和chatkey作为参数到数据库查询。返回查询得到的结果。