Go-Zero项目开发7: 构建社交服务RPC与好友/群业务实现
纲要社交服务业务需求好友关系管理申请、处理、好友列表群组管理创建、申请、加入、退群等数据表设计好友关系表friend的冗余存储方案好友申请表friend_request群信息表、群成员表、群申请表项目结构更新apps/social/rpc目录及关键文件构建社交 RPC 服务编写social.proto定义服务接口使用goctl rpc生成代码配置etc/social.yaml及依赖注入模型生成与扩展基于 SQL 生成 5 张表的模型解决包名冲突的前缀设计扩展模型方法好友关系检查、申请查询、批量新增好友记录业务逻辑实现好友申请重复检查、已有关系校验、新增申请记录好友申请处理状态校核、事务内修改状态并建立好友关系好友列表查询事务封装模型层支持Transact方法测试验证通过 gRPC 客户端进行接口测试社交服务业务需求在即时通讯系统中社交模块负责管理用户之间的关系网络核心功能包括好友管理申请好友、处理申请、好友列表查询。群组管理创建群组、申请加群、处理加群申请、群成员列表、退群。本文重点围绕好友关系的设计及核心业务实现群组功能的实现模式与之类似可在后续迭代中补充。数据表设计社交服务涉及 5 张数据表好友关系表friend、好友申请表friend_request、群信息表group、群成员表group_member、群申请表group_request。好友关系表设计是一个关键点常见两种方案方案存储方式优点缺点冗余存储双方分别记录一条user_id → friend_id数据查询简单性能高数据冗余占用双倍存储唯一 Key根据双方 ID 排序生成唯一 Key仅存储一条节省空间查询复杂需分别按双方 ID 检索本系统采用冗余存储方案因其查询效率高且好友关系数据总量相对可控。表结构示例如下CREATETABLEfriend(idbigintNOTNULLAUTO_INCREMENT,user_idvarchar(64)NOTNULLCOMMENT用户ID,friend_idvarchar(64)NOTNULLCOMMENT好友ID,statustinyint(1)NOTNULLDEFAULT1COMMENT1:正常,create_timedatetimeNOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(id),KEYidx_user_id(user_id),KEYidx_friend_id(friend_id))ENGINEInnoDBDEFAULTCHARSETutf8mb4COMMENT好友关系表;friend_request表类似增加请求状态字段。项目结构更新在apps/social/rpc下组织社交服务代码apps/social/rpc/ ├── internal/ │ ├── config/ │ ├── server/ │ ├── svc/ │ └── logic/ ├── model/ # 生成的模型代码带前缀 ├── social.proto └── social.go构建社交 RPC 服务编写 Proto 文件在 social.proto 中先定义好友相关 RPCsyntax proto3; package social; option go_package ./social; message FriendApplyRequest { string user_id 1; string friend_id 2; // 目标用户ID string reason 3; } message FriendApplyResponse {} message FriendApplyHandleRequest { int64 apply_id 1; int32 handle_type 2; // 1:通过 2:拒绝 } message FriendApplyHandleResponse {} message FriendListRequest { string user_id 1; } message FriendListResponse { repeated string friend_ids 1; } service Social { // 申请好友 rpc FriendApply(FriendApplyRequest) returns (FriendApplyResponse); // 处理好友申请 rpc FriendApplyHandle(FriendApplyHandleRequest) returns (FriendApplyHandleResponse); // 好友列表 rpc FriendList(FriendListRequest) returns (FriendListResponse); }生成代码执行命令$ goctl rpc protoc apps/social/rpc/social.proto--gooutapps/social/rpc/ --go-grpcoutapps/social/rpc/--zrpc_outapps/social/rpc/生成后的social.go为服务入口internal下包含通用组件。配置与依赖注入internal/config/config.gopackageconfigimport(github.com/zeromicro/go-zero/core/stores/cachegithub.com/zeromicro/go-zero/zrpc)typeConfigstruct{zrpc.RpcServerConf MySQLstruct{DataSourcestring}CacheRedis cache.CacheConf}etc/social.yaml示例Name:social.rpcListenOn:0.0.0.0:10002Etcd:Hosts:-192.168.1.10:2379Key:social.rpcMySQL:DataSource:root:123456tcp(192.168.1.10:3306)/im_db?charsetutf8mb4parseTimeTrueCacheRedis:-Host:192.168.1.10:6379Pass:yourpassinternal/svc/servicecontext.go中注入所有模型packagesvcimport(im-system/apps/social/rpc/internal/configim-system/apps/social/rpc/modelgithub.com/zeromicro/go-zero/core/stores/sqlx)typeServiceContextstruct{Config config.Config FriendModel model.FriendModel FriendReqModel model.FriendRequestModel GroupModel model.GroupModel GroupMemberModel model.GroupMemberModel GroupReqModel model.GroupRequestModel}funcNewServiceContext(c config.Config)*ServiceContext{conn:sqlx.NewMysql(c.MySQL.DataSource)// 注意生成模型时指定了前缀因此创建的模型类型名会有前缀returnServiceContext{Config:c,FriendModel:model.NewFriendModel(conn,c.CacheRedis),FriendReqModel:model.NewFriendRequestModel(conn,c.CacheRedis),// ... 其他模型}}模型生成与扩展生成数据模型使用goctl model基于 SQL 生成模型通过-prefix参数添加前缀避免多个服务模型命名冲突$ goctl model mysql ddl-srcdeploy/sql/social.sql-dirapps/social/rpc/model-prefixsocial_-stylegoZero扩展模型方法为满足业务需求在模型层添加自定义方法。1 ) 检查好友关系FindOneByUserIdAndFriendId// 在 friendmodel_ext.go 中func(m defaultFriendModel)FindOneByUserIdAndFriendId(ctx context.Context,uid,fuidstring)(Friend,error){query:fmt.Sprintf(select %s from %s where userid ? and friendid ? limit 1,friendRows,m.table)varresp Friend err:m.conn.QueryRowCtx(ctx,resp,query,uid,fuid)iferr!nil{returnnil,err}returnresp,nil}2 ) 查询好友申请记录FindByUserIdAndFriendId// friendrequestmodel_ext.gofunc(m defaultFriendRequestModel)FindByUserIdAndFriendId(ctx context.Context,reqUid,targetUidstring)(FriendRequest,error){query:fmt.Sprintf(select %s from %s where userid ? and friendid ? and status 1 limit 1,friendRequestRows,m.table)varresp FriendRequest err:m.conn.QueryRowCtx(ctx,resp,query,reqUid,targetUid)iferr!nil{returnnil,err}returnresp,nil}3 ) 批量新增好友记录// friendmodel_ext.gofunc(m defaultFriendModel)InsertBatch(ctx context.Context,conn sqlx.Session,list[]Friend)error{iflen(list)0{returnnil}varbuf strings.Builder buf.WriteString(fmt.Sprintf(insert into %s (%s) values ,m.table,friendRows))args:make([]interface{},0,len(list)*len(friendFieldNames))fori,item:rangelist{ifi0{buf.WriteString(, )}buf.WriteString((?, ?, ?, ?))argsappend(args,item.UserId,item.FriendId,item.Status,item.CreateTime)}_,err:conn.ExecCtx(ctx,buf.String(),args...)returnerr}4 ) 事务支持模型接口中增加 Transact 方法// friendmodel.go 接口定义FriendModelinterface{// ... 其他方法Transact(ctx context.Context,fnfunc(ctx context.Context,session sqlx.Session)error)error}// 实现func(m*defaultFriendModel)Transact(ctx context.Context,fnfunc(ctx context.Context,session sqlx.Session)error)error{returnm.conn.Transact(ctx,fn)}其他模型同理。业务逻辑实现好友申请申请流程检查目标用户与自己是否已经是好友。检查是否存在未处理的申请记录。创建新的申请记录。// friendapplylogic.gofunc(l FriendApplyLogic)FriendApply(in social.FriendApplyRequest)(*social.FriendApplyResponse,error){// 1. 检查是否已是好友_,err:l.svcCtx.FriendModel.FindOneByUserIdAndFriendId(l.ctx,in.UserId,in.FriendId)iferrnil{returnnil,errx.NewCodeError(errx.CodeAlreadyFriend,已为好友关系)}if!errors.Is(err,sqlx.ErrNotFound){returnnil,errx.NewCodeErrorf(errx.CodeDBError,查询好友关系失败: %v,err)}// 2. 检查是否有未处理的申请_,errl.svcCtx.FriendReqModel.FindByUserIdAndFriendId(l.ctx,in.UserId,in.FriendId)iferrnil{returnnil,errx.NewCodeError(errx.CodeRequestExists,已存在待处理的申请)}if!errors.Is(err,sqlx.ErrNotFound){returnnil,errx.NewCodeErrorf(errx.CodeDBError,查询申请记录失败: %v,err)}// 3. 新增申请newReq:model.FriendRequest{UserId:in.UserId,FriendId:in.FriendId,Reason:in.Reason,Status:1,// 未处理CreateTime:time.Now(),}_,errl.svcCtx.FriendReqModel.Insert(l.ctx,newReq)iferr!nil{returnnil,errx.NewCodeErrorf(errx.CodeDBError,创建好友申请失败: %v,err)}returnsocial.FriendApplyResponse{},nil}好友申请处理处理流程依赖事务确保状态修改和好友关系建立原子执行。// friendapplyhandlelogic.gofunc(l FriendApplyHandleLogic)FriendApplyHandle(in social.FriendApplyHandleRequest)(*social.FriendApplyHandleResponse,error){// 1. 获取申请记录applyId:in.ApplyId apply,err:l.svcCtx.FriendReqModel.FindOne(l.ctx,applyId)iferr!nil{returnnil,errx.NewCodeError(errx.CodeApplyNotFound,申请记录不存在)}// 2. 检查状态ifapply.Status!1{returnnil,errx.NewCodeError(errx.CodeApplyAlreadyProcessed,申请已处理)}// 3. 事务处理errl.svcCtx.FriendReqModel.Transact(l.ctx,func(ctx context.Context,session sqlx.Session)error{// 更新申请状态apply.Statusin.HandleType err:l.svcCtx.FriendReqModel.UpdateWithSession(ctx,session,apply)iferr!nil{returnerr}// 如果通过建立两条好友关系ifin.HandleType1{friends:[]*model.Friend{{UserId:apply.UserId,FriendId:apply.FriendId,Status:1,CreateTime:time.Now()},{UserId:apply.FriendId,FriendId:apply.UserId,Status:1,CreateTime:time.Now()},}errl.svcCtx.FriendModel.InsertBatch(ctx,session,friends)iferr!nil{returnerr}}returnnil})iferr!nil{returnnil,errx.NewCodeErrorf(errx.CodeDBError,处理好友申请失败: %v,err)}returnsocial.FriendApplyHandleResponse{},nil}注意UpdateWithSession需在模型层提供使用 session 的方法以便在事务内执行。好友列表查询// friendlistlogic.gofunc(l FriendListLogic)FriendList(in social.FriendListRequest)(*social.FriendListResponse,error){friends,err:l.svcCtx.FriendModel.FindByUserId(ctx,in.UserId)iferr!nil{returnnil,errx.NewCodeErrorf(errx.CodeDBError,查询好友列表失败: %v,err)}friendIds:make([]string,0,len(friends))for_,f:rangefriends{friendIdsappend(friendIds,f.FriendId)}returnsocial.FriendListResponse{FriendIds:friendIds},nil}注册拦截器在social.go入口处注册错误拦截器采用与用户服务相同的模式funcmain(){flag.Parse()varc config.Config conf.MustLoad(*configFile,c)s:zrpc.MustNewServer(c.RpcServerConf,func(grpcServer*grpc.Server){social.RegisterSocialServer(grpcServer,server.NewSocialServer(svc.NewServiceContext(c)))},zrpc.WithUnaryServerInterceptor(interceptor.RpcErrInterceptor))defers.Stop()fmt.Printf(Starting social rpc server at %s...\n,c.ListenOn)s.Start()}测试验证使用grpcurl或单元测试验证好友功能申请好友传入双方 ID第一次应成功重复申请应返回“已存在待处理申请”若已为好友则提示“已为好友”。处理申请传入申请 ID 和操作类型成功后好友关系表中出现两条记录。列表查询传入用户 ID返回好友 ID 列表。通过事务保障如果插入好友关系失败申请状态不会改变有效防止数据不一致。总结本文完成了社交服务中好友功能的完整搭建涵盖了冗余存储的好友关系设计决策。利用goctl快速生成 RPC 与模型代码。模型层自定义方法的扩展包括支持事务的批量插入。业务逻辑中对状态机、并发安全的合理处理。统一错误码和拦截器的应用。群组相关的创建、申请、成员管理等业务可参照好友模式实现。下一节我们将整合社交 API 层对外提供 HTTP 接口逐步构建完整的社交服务。

相关新闻

2026年全国可做的真人电销外包公司怎么选?跨区域合规与选型标准解析

2026年全国可做的真人电销外包公司怎么选?跨区域合规与选型标准解析

不少面向全国市场的企业在电销外包时面临以下问题:不同省份的外呼线路合规要求存在差异,本地服务商无法覆盖全国所有区域,多地同步外呼时进度难以统一,不同地区客户沟通习惯差异导致部分坐席难以匹配异地客户需求。 2026年外呼监管…

2026/7/26 22:42:36 阅读更多 →
技术实践中的注意事项与方案对比方法论

技术实践中的注意事项与方案对比方法论

1. 为什么需要注意事项与对比详解?在技术实践和项目开发中,我们经常会遇到这样的情况:看似简单的操作,实际执行时却总是踩坑;明明按照文档一步步操作,结果却与预期不符;面对多个相似的技术方案时…

2026/7/28 6:06:16 阅读更多 →
SpringCloud微服务架构实战:核心组件与优化指南

SpringCloud微服务架构实战:核心组件与优化指南

1. SpringCloud入门:为什么选择它作为微服务架构的核心框架SpringCloud本质上是一套基于SpringBoot的微服务工具集,它解决了分布式系统中的八大核心痛点。我在2018年第一次接触SpringCloud时,正面临着一个传统单体架构向微服务转型的困境——…

2026/7/28 17:26:11 阅读更多 →

最新新闻

为什么顶尖设计总监都在秘密训练“提示词架构师”?——AI原生工作流重构倒计时47天

为什么顶尖设计总监都在秘密训练“提示词架构师”?——AI原生工作流重构倒计时47天

更多请点击: https://kaifayun.com 第一章:AI设计 未来趋势 AI设计正从工具辅助迈向自主协同范式,核心驱动力已从算力堆叠转向认知建模与人机意图对齐。设计师不再仅调用API,而是构建可解释、可干预、可演化的AI原生工作流——其…

2026/7/28 20:46:21 阅读更多 →
研发效率不是“加班”堆出来的:2026年软件研发管理平台效能度量能力横评

研发效率不是“加班”堆出来的:2026年软件研发管理平台效能度量能力横评

一、行业现状 “怎么衡量研发效率”一直是技术管理者的核心命题。据多家市场研究机构报告(如 GlobeNewswire 转发的 2026 年 DevOps 市场分析),全球 DevOps 市场 2025 年达 198 亿美元,以 22.73% 的复合年增长率(CAGR&…

2026/7/28 20:46:21 阅读更多 →
AI做背景音乐不求人:从提示词设计→动态节奏控制→多轨混音,一文打通全链路

AI做背景音乐不求人:从提示词设计→动态节奏控制→多轨混音,一文打通全链路

更多请点击: https://kaifayun.com 第一章:AI做背景音乐不求人:从提示词设计→动态节奏控制→多轨混音,一文打通全链路 精准提示词设计:让AI听懂你的氛围需求 高质量AI配乐始于可执行的提示词(Prompt&…

2026/7/28 20:46:21 阅读更多 →
高端瓷砖十大品牌盘点:同样是选砖,为什么越来越多人把目光投向金丝玉玛

高端瓷砖十大品牌盘点:同样是选砖,为什么越来越多人把目光投向金丝玉玛

装修这件事,选砖是重头戏。瓷砖十大品牌摆在那里,东鹏、蒙娜丽莎、马可波罗、金丝玉玛、鹰牌……名字都耳熟能详,但真到做选择的时候,你会发现自己纠结的点往往是:哪个品牌更懂"高级感"? 今天就认…

2026/7/28 20:46:21 阅读更多 →
小米平板5 Windows驱动安装完整指南:3步解锁桌面级体验

小米平板5 Windows驱动安装完整指南:3步解锁桌面级体验

小米平板5 Windows驱动安装完整指南:3步解锁桌面级体验 【免费下载链接】MiPad5-Drivers https://github.com/Project-Aloha/windows_oem_xiaomi_nabu 项目地址: https://gitcode.com/gh_mirrors/mi/MiPad5-Drivers 想要让你的小米平板5变身为真正的Windows生…

2026/7/28 20:46:21 阅读更多 →
Unity游戏世界构建实战:从场景管理到动态交互与性能优化

Unity游戏世界构建实战:从场景管理到动态交互与性能优化

1. 项目概述与核心目标大家好,我是LDB工作室的一名老伙计。今天咱们继续这个系列教程的第三篇。前两篇我们搭建了基础框架,引入了角色和简单的交互逻辑,算是把游戏的“骨架”给立起来了。但一个游戏光有骨架可不行,它得有血有肉&a…

2026/7/28 20:45:20 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻