/** * package model * 作用:定义全系统通用的数据结构(PO 与 DTO)。 * 规范:所有字段均采用 snake_case (下划线命名) 以保持 DB 和 JSON 的高度一致性。 */ package model import ( "encoding/json" "time" ) // ========================================== // 数据库实体 (PO - Persistent Object) // ========================================== /** * ChatMessage * 对应数据库表:chat_messages * 作用:持久化存储聊天记录,包括文本、多媒体和信令状态。 */ type ChatMessage struct { // 消息唯一标识 ID ID uint `gorm:"primaryKey;comment:消息唯一标识ID" json:"id"` // 房间ID,用于标识聊天室或会话 RoomID string `gorm:"type:varchar(100);index;comment:房间ID" json:"room_id"` // 发送者用户ID SenderUserID string `gorm:"type:varchar(100);index;comment:发送者用户ID" json:"sender_user_id"` // 发送者 IP // json:"-":原始IP属于敏感隐私,仅入库供风控/审计使用, // 禁止随消息历史/推送下发给聊天对端(前端也从未使用该字段) SenderIP string `gorm:"type:varchar(50);comment:发送者IP" json:"-"` // IP 归属地(粗粒度地域信息,前端消息气泡会展示"IP归属地",属于产品功能,保留下发) IPLocation string `gorm:"type:varchar(255);comment:IP归属地" json:"ip_location"` // 接收者用户ID ReceiverUserID string `gorm:"type:varchar(100);index;comment:接收者用户ID" json:"receiver_user_id"` // 消息类型 ( // 0:文本 1:图片 2:语音 3:视频 8:文件 6:信令) MessageType int `gorm:"type:int;comment:消息类型" json:"message_type"` // 消息内容或信令数据 Content string `gorm:"type:text;comment:消息内容" json:"content"` // 存储富文本元数据(如URL预览信息的JSON字符串: {"title":"...","image":"..."}) Extra string `gorm:"type:text;comment:扩展字段,存储JSON格式的元数据" json:"extra"` // 通话时长(秒),仅在通话类型的消息中有效 Duration int `gorm:"type:int;comment:通话时长(秒)" json:"duration"` // 关联的通话ID (用于串联信令) CallID string `gorm:"type:varchar(100);index;comment:关联的通话ID" json:"call_id"` // 通话状态 (invite, accepted, ended, rejected, offer, answer, candidate, hangup, etc.) CallStatus string `gorm:"type:varchar(50);comment:通话状态" json:"call_status"` // 创建时间,自动生成 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 发送端 WebSocket 客户端ID(gorm:"-" 不入库,仅随推送下发) // 为什么需要:消息会回推给发送者的所有在线设备以实现多端同步, // 发送端自身可根据该字段识别"自己的回声"并忽略,避免重复渲染同一条消息 SenderClientID string `gorm:"-" json:"sender_client_id,omitempty"` } // TableName 指定表名和注释 func (ChatMessage) TableName() string { return "chat_messages" } /** * MessageDeletion * 作用:消息删除记录("删除仅对我生效"语义)。 * 逻辑:用户删除某条消息时插入一条 (message_id, user_id) 记录, * 历史消息查询时排除当前用户已删除的消息;消息本体不动, * 对方仍然可见(区别于"撤回"是双方都不可见)。 * 表结构见 migrations/20260811_message_deletions.sql(项目不使用 AutoMigrate) */ type MessageDeletion struct { // 自增主键 ID uint `gorm:"primaryKey" json:"id"` // 被删除的消息ID MessageID uint `gorm:"index:uk_message_user,unique;comment:被删除的消息ID" json:"message_id"` // 执行删除的用户ID(删除仅对该用户生效) UserID string `gorm:"type:varchar(100);index:uk_message_user,unique;comment:执行删除的用户ID" json:"user_id"` // 删除时间 CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` } // TableName 指定表名 func (MessageDeletion) TableName() string { return "message_deletions" } // ========================================== // 交互数据传输对象 (DTO - Data Transfer Object) // ========================================== /** * WsPayload * 作用:WebSocket 传输的最外层通用载荷。 * 逻辑:根据 RequestType 字段决定如何解析 Data。 */ type WsPayload struct { // 请求类型: "bind", "heartbeat", "send_message", "receive_message" RequestType string `json:"request_type"` // 具体数据载荷,结构取决于 RequestType Data interface{} `json:"data"` } /** * SendMessageReq * 作用:客户端发送消息或信令的请求参数结构。 */ type SendMessageReq struct { // [新增] 发送端的 WebSocket ClientID,用于在多端同步时排除自己 SenderClientID string `json:"sender_client_id,omitempty"` // 指定目标客户端ID (选填,用于点对点精确控制) TargetClientID string `json:"target_client_id,omitempty"` // 指定目标用户ID (选填,用于发送给用户的所有设备) ReceiverUserID string `json:"receiver_user_id,omitempty"` // 房间ID RoomID string `json:"room_id"` // 消息类型 MessageType int `json:"message_type"` // 内容 Content string `json:"content"` // 时长 Duration int `json:"duration"` // 辅助字段: 辅助元数据 Extra string `json:"extra,omitempty"` // WebRTC 信令专用字段: 通话唯一ID CallID string `json:"call_id,omitempty"` // WebRTC 信令专用字段: 信令状态 (invite, offer, answer, candidate, hangup) CallStatus string `json:"call_status,omitempty"` } /** * UrlMeta * 作用:URL 预览元数据结构 (存储在 Extra 字段中) */ type UrlMeta struct { // 标题 Title string `json:"title"` // 描述 Description string `json:"description"` // 图片链接 Image string `json:"image"` // 原始URL Url string `json:"url"` } /** * BindReq * 作用:用户绑定请求参数。 */ type BindReq struct { // 用户ID UserID string `json:"user_id"` // WebSocket握手后获得的临时ID ClientID string `json:"client_id"` } /** * ICEServerConfig * 作用:返回给前端的 TURN/STUN 配置信息。 */ type ICEServerConfig struct { // TURN服务器地址列表 Urls []string `json:"urls"` // 鉴权用户名 Username string `json:"username,omitempty"` // 鉴权密码 Credential string `json:"credential,omitempty"` } /** * User * 对应数据库表:users * 作用:用户基本信息表 */ type User struct { // 用户唯一ID ID string `gorm:"primaryKey;type:varchar(100);comment:用户唯一ID" json:"id"` // 邮箱 Email string `gorm:"type:varchar(255);uniqueIndex;comment:邮箱" json:"email"` // 手机号 Phone string `gorm:"type:varchar(20);uniqueIndex;comment:手机号" json:"phone"` // 密码(加密后) Password string `gorm:"type:varchar(255);comment:密码(加密后)" json:"-"` // 用户名称 Name string `gorm:"type:varchar(100);comment:用户名称" json:"name"` // 用户头像URL或字符 Avatar string `gorm:"type:varchar(500);comment:用户头像" json:"avatar"` // 用户描述或签名 Desc string `gorm:"type:varchar(500);comment:用户描述或签名" json:"desc"` // 地区 Region string `gorm:"type:varchar(100);comment:地区" json:"region"` // 朋友圈封面图URL(空则前端展示默认封面) MomentCover string `gorm:"type:varchar(500);comment:朋友圈封面图URL" json:"moment_cover"` // 是否为AI机器人虚拟用户(机器人复用 users 表,消息/群成员/会话链路零改造) IsBot bool `gorm:"default:false;comment:是否AI机器人" json:"is_bot"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` } // TableName 指定表名和注释 func (User) TableName() string { return "users" } /** * AIConfig * 对应数据库表:ai_configs * 作用:AI 提供商配置(全局一份,仅管理员 id=1 可维护)。 * 各机器人共用该配置调用大模型,工厂函数按 provider 创建对应 Provider 实例 */ type AIConfig struct { // 主键ID(全局仅一条记录,id 固定为 1) ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 提供商标识:openai/deepseek/dashscope/moonshot/ollama/custom Provider string `gorm:"type:varchar(50);comment:提供商标识" json:"provider"` // API 基础地址(空则用 provider 的默认地址;custom 必填) BaseURL string `gorm:"type:varchar(500);comment:API基础地址" json:"base_url"` // API 密钥(返回给前端时须脱敏) APIKey string `gorm:"type:varchar(500);comment:API密钥" json:"api_key"` // 模型名称,如 gpt-4o-mini / deepseek-chat / qwen-plus Model string `gorm:"type:varchar(100);comment:模型名称" json:"model"` // 是否启用(关闭后所有机器人停止应答) Enabled bool `gorm:"default:false;comment:是否启用" json:"enabled"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` } // TableName 指定表名 func (AIConfig) TableName() string { return "ai_configs" } /** * AIBot * 对应数据库表:ai_bots * 作用:AI 机器人定义。每个机器人在 users 表有一个 is_bot=1 的虚拟用户, * 使其能作为"联系人/群成员"复用全部现有消息链路; * role_prompt 为角色设定,作为对话的 system 提示词 */ type AIBot struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 关联的虚拟用户ID(users.id,bot_ 前缀) UserID string `gorm:"type:varchar(100);uniqueIndex;comment:虚拟用户ID" json:"user_id"` // 机器人名称(群聊中 @名称 触发应答) Name string `gorm:"type:varchar(100);comment:机器人名称" json:"name"` // 机器人头像 Avatar string `gorm:"type:varchar(500);comment:机器人头像" json:"avatar"` // 角色设定(system 提示词),如"你是一个温柔的客服" RolePrompt string `gorm:"type:text;comment:角色设定" json:"role_prompt"` // 是否启用(停用后不应答、不出现在好友列表) Enabled bool `gorm:"default:true;comment:是否启用" json:"enabled"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` } // TableName 指定表名 func (AIBot) TableName() string { return "ai_bots" } /** * UserContact * 对应数据库表:user_contacts * 作用:用户联系人表,存储好友关系、分组、备注等信息 */ type UserContact struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 用户ID - 当前用户 UserID string `gorm:"type:varchar(100);index;comment:用户ID" json:"user_id"` // 联系人ID - 好友的用户ID ContactID string `gorm:"type:varchar(100);index;comment:联系人ID" json:"contact_id"` // 房间ID - 关联的房间ID RoomID string `gorm:"type:varchar(100);index;comment:房间ID" json:"room_id"` // 备注名称 - 用户自定义的好友备注 RemarkName string `gorm:"type:varchar(100);comment:备注名称" json:"remark_name"` // 分组ID - 好友所属分组 GroupID uint `gorm:"type:int;index;comment:分组ID" json:"group_id"` // 是否置顶 IsTop bool `gorm:"type:tinyint(1);default:0;comment:是否置顶" json:"is_top"` // 是否免打扰 IsMuted bool `gorm:"type:tinyint(1);default:0;comment:是否免打扰" json:"is_muted"` // 是否特别关心(高优先级提醒,类似 QQ 特别关心) IsSpecialCare bool `gorm:"type:tinyint(1);default:0;comment:是否特别关心" json:"is_special_care"` // 是否拉黑(加入黑名单,不再接收消息/出现在默认列表中) IsBlocked bool `gorm:"type:tinyint(1);default:0;comment:是否拉黑" json:"is_blocked"` // 最后聊天时间 LastChatTime *time.Time `gorm:"type:datetime;comment:最后聊天时间" json:"last_chat_time"` // 最后一条消息 LastMessage string `gorm:"type:text;comment:最后一条消息" json:"last_message"` // 未读消息数 UnreadCount int `gorm:"type:int;default:0;comment:未读消息数" json:"unread_count"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` } // TableName 指定表名和注释 func (UserContact) TableName() string { return "user_contacts" } /** * ContactGroup * 对应数据库表:contact_groups * 作用:联系人分组表 */ type ContactGroup struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 用户ID - 分组所属用户 UserID string `gorm:"type:varchar(100);index;comment:用户ID" json:"user_id"` // 分组名称 GroupName string `gorm:"type:varchar(100);comment:分组名称" json:"group_name"` // 排序顺序 SortOrder int `gorm:"type:int;default:0;comment:排序顺序" json:"sort_order"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` } // TableName 指定表名和注释 func (ContactGroup) TableName() string { return "contact_groups" } /** * ChatRoom * 对应数据库表:chat_rooms * 作用:聊天房间表,支持点对点和群聊 */ type ChatRoom struct { // 房间ID - 主键 RoomID string `gorm:"primaryKey;type:varchar(100);comment:房间ID" json:"room_id"` // 房间类型 - "p2p" 点对点 / "group" 群聊 RoomType string `gorm:"type:varchar(20);index;comment:房间类型" json:"room_type"` // 房间名称 - 群聊时显示 RoomName string `gorm:"type:varchar(200);comment:房间名称" json:"room_name"` // 房间头像 - 群聊时显示 RoomAvatar string `gorm:"type:varchar(500);comment:房间头像" json:"room_avatar"` // 群主ID - 群聊必填,单聊为0 OwnerID string `gorm:"type:varchar(100);index;default:0;comment:群主ID(群聊必填,单聊为0)" json:"owner_id"` // 管理员ID列表 - 逗号分隔 AdminIDs string `gorm:"type:varchar(500);comment:管理员ID列表(逗号分隔)" json:"admin_ids"` // 创建者ID CreatorID string `gorm:"type:varchar(100);index;comment:创建者ID" json:"creator_id"` // 最后消息时间 LastMessageTime *time.Time `gorm:"type:datetime;index;comment:最后消息时间" json:"last_message_time"` // 最后消息内容 LastMessage string `gorm:"type:text;comment:最后消息内容" json:"last_message"` // 群公告 - 仅群聊使用 Announcement string `gorm:"type:text;comment:群公告" json:"announcement"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` } // TableName 指定表名和注释 func (ChatRoom) TableName() string { return "chat_rooms" } /** * RoomMember * 对应数据库表:room_members * 作用:房间成员表,存储房间与用户的关联关系 */ type RoomMember struct { // 房间ID RoomID string `gorm:"type:varchar(100);primaryKey;comment:房间ID" json:"room_id"` // 用户ID UserID string `gorm:"type:varchar(100);primaryKey;comment:用户ID" json:"user_id"` // 成员角色:0-成员 1-管理员 2-群主 Role int8 `gorm:"type:tinyint(1);default:0;comment:成员角色(0=成员,1=管理员,2=群主)" json:"role"` // 加入时间 JoinedAt time.Time `gorm:"type:datetime;autoCreateTime;comment:加入时间" json:"joined_at"` // 禁言到期时间(null或过期时间之前表示未禁言) MutedUntil *time.Time `gorm:"type:datetime;comment:禁言到期时间" json:"muted_until,omitempty"` // 用户信息(关联查询,不存储在数据库中) User *User `gorm:"foreignKey:UserID;references:ID" json:"user,omitempty"` // 群名片(群内显示的自定义昵称)。 // 原先是 gorm:"-" 非持久化字段且后端从未赋值,前端"我在群里的昵称"功能整体失效, // 现改为实体列(见 migrations/20260811_room_member_nickname.sql) Nickname string `gorm:"type:varchar(100);default:'';comment:群名片(群内显示昵称)" json:"nickname,omitempty"` } // IsMuted 判断成员是否被禁言 func (m *RoomMember) IsMuted() bool { if m.MutedUntil == nil { return false } return m.MutedUntil.After(time.Now()) } // TableName 指定表名和注释 func (RoomMember) TableName() string { return "room_members" } /** * FriendRequest * 对应数据库表:friend_requests * 作用:好友申请表 */ type FriendRequest struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 发送者用户ID FromUserID string `gorm:"type:varchar(100);index;comment:发送者用户ID" json:"from_user_id"` // 接收者用户ID ToUserID string `gorm:"type:varchar(100);index;comment:接收者用户ID" json:"to_user_id"` // 申请消息 Message string `gorm:"type:text;comment:申请消息" json:"message"` // 状态 - "pending", "accepted", "rejected" Status string `gorm:"type:varchar(20);default:'pending';index;comment:状态" json:"status"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` } // TableName 指定表名和注释 func (FriendRequest) TableName() string { return "friend_requests" } /** * VerificationCode * 对应数据库表:verification_codes * 作用:验证码表 */ type VerificationCode struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 邮箱或手机号 Target string `gorm:"type:varchar(255);index;comment:邮箱或手机号" json:"target"` // 验证码 Code string `gorm:"type:varchar(10);comment:验证码" json:"-"` // 类型 - "email" 或 "sms" Type string `gorm:"type:varchar(20);comment:类型" json:"type"` // 过期时间 ExpiresAt time.Time `gorm:"type:datetime;index;comment:过期时间" json:"expires_at"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` } // TableName 指定表名和注释 func (VerificationCode) TableName() string { return "verification_codes" } /** * Attachment * 对应数据库表:attachments * 作用:附件表,记录上传的文件信息 */ type Attachment struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 上传者ID UploaderID string `gorm:"type:varchar(100);index;comment:上传者ID" json:"uploader_id"` // 文件名 FileName string `gorm:"type:varchar(255);comment:文件名" json:"file_name"` // 文件类型 - "image" 或 "video" FileType string `gorm:"type:varchar(20);index;comment:文件类型" json:"file_type"` // 文件大小(字节) FileSize int64 `gorm:"type:bigint;comment:文件大小(字节)" json:"file_size"` // 文件路径 FilePath string `gorm:"type:varchar(500);comment:文件路径" json:"file_path"` // 文件URL FileURL string `gorm:"type:varchar(500);comment:文件URL" json:"file_url"` // MIME类型 MimeType string `gorm:"type:varchar(100);comment:MIME类型" json:"mime_type"` // 宽度(图片/视频) Width int `gorm:"type:int;comment:宽度" json:"width"` // 高度(图片/视频) Height int `gorm:"type:int;comment:高度" json:"height"` // 时长(视频,秒) Duration int `gorm:"type:int;comment:时长(秒)" json:"duration"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` } // TableName 指定表名和注释 func (Attachment) TableName() string { return "attachments" } /** * ApiRequestLog * 对应数据库表:api_request_logs * 作用:接口请求日志表,记录所有API请求信息 */ type ApiRequestLog struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 请求路由 Route string `gorm:"type:varchar(255);index;comment:请求路由" json:"route"` // 请求IP IP string `gorm:"type:varchar(50);index;comment:请求IP" json:"ip"` // IP归属地 IPLocation string `gorm:"type:varchar(255);comment:IP归属地" json:"ip_location"` // 请求用户ID(未登录为0) UserID string `gorm:"type:varchar(100);index;comment:请求用户ID" json:"user_id"` // 请求方式 Method string `gorm:"type:varchar(10);comment:请求方式" json:"method"` // 请求参数(JSON) RequestParams string `gorm:"type:text;comment:请求参数" json:"request_params"` // 返回参数(JSON) ResponseParams string `gorm:"type:text;comment:返回参数" json:"response_params"` // 返回code ResponseCode int `gorm:"type:int;index;comment:返回code" json:"response_code"` // 返回http状态 HTTPStatus int `gorm:"type:int;comment:返回http状态" json:"http_status"` // 请求时间 RequestTime time.Time `gorm:"type:datetime;index;comment:请求时间" json:"request_time"` } // TableName 指定表名和注释 func (ApiRequestLog) TableName() string { return "api_request_logs" } /** * ChatConversation * 对应数据库表:chat_conversations * 作用:用户级别的最近会话列表,支持置顶、免打扰和未读计数等功能 */ type ChatConversation struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 所属用户ID(当前登录用户) UserID string `gorm:"type:varchar(100);index;comment:用户ID" json:"user_id"` // 目标ID:好友ID或群ID TargetID string `gorm:"type:varchar(100);index;comment:目标ID(好友或群)" json:"target_id"` // 房间ID:与 chat_messages / chat_rooms 中的 room_id 一致 RoomID string `gorm:"type:varchar(100);index;comment:房间ID" json:"room_id"` // 会话类型:1-私聊 2-群聊 Type int `gorm:"type:int;comment:会话类型(1=私聊,2=群聊)" json:"type"` // 是否置顶 IsTop bool `gorm:"type:tinyint(1);default:0;comment:是否置顶" json:"is_top"` // 是否免打扰 IsMuted bool `gorm:"type:tinyint(1);default:0;comment:是否免打扰" json:"is_muted"` // 是否特别关心(用于在前端高亮显示) IsSpecialCare bool `gorm:"type:tinyint(1);default:0;comment:是否特别关心" json:"is_special_care"` // 未读消息数 UnreadCount int `gorm:"type:int;default:0;comment:未读消息数" json:"unread_count"` // 最后一条消息内容(用于列表摘要) LastMessage string `gorm:"type:varchar(500);comment:最后一条消息内容" json:"last_message"` // 最后消息时间 LastTime time.Time `gorm:"type:datetime;index;comment:最后消息时间" json:"last_time"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` // 目标用户信息(关联查询,不存储在数据库中) // 注意:不使用外键约束,因为 target_id 可能是用户ID(私聊)或群ID(群聊) // 使用 - 前缀禁用 GORM 的自动外键创建 TargetUser *User `gorm:"-" json:"target_user,omitempty"` // 群聊信息(关联查询,仅群聊时使用,不存储在数据库中) // 使用 - 前缀禁用 GORM 的自动外键创建,手动加载 Room *ChatRoom `gorm:"-" json:"room,omitempty"` } // TableName 指定表名和注释 func (ChatConversation) TableName() string { return "chat_conversations" } /** * LoginLog * 对应数据库表:login_logs * 作用:登录日志表,记录所有登录尝试 */ type LoginLog struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 登录账号 Account string `gorm:"type:varchar(255);index;comment:登录账号" json:"account"` // 登录ID(失败记录0) UserID string `gorm:"type:varchar(100);index;comment:登录ID" json:"user_id"` // 归属地(IP归属地) Location string `gorm:"type:varchar(255);comment:归属地" json:"location"` // 地址(IP地址) IP string `gorm:"type:varchar(50);index;comment:IP地址" json:"ip"` // 成功/失败状态(true=成功,false=失败) Success bool `gorm:"type:tinyint(1);index;comment:成功状态" json:"success"` // 登录时间 LoginTime time.Time `gorm:"type:datetime;index;comment:登录时间" json:"login_time"` } // TableName 指定表名和注释 func (LoginLog) TableName() string { return "login_logs" } /** * ClusterMessage * 作用:内部通信,Redis Pub/Sub 集群消息转发结构。 */ type ClusterMessage struct { // 发出该消息的源节点ID SenderNodeID string `json:"sender_node_id"` // 目标用户ID TargetUserID string `json:"target_user_id"` // 原始消息体 (WsPayload 的 JSON 字节)。 // 为什么用 json.RawMessage:原来的 interface{} 装 []byte 时会被 encoding/json 编码成 base64 字符串, // 订阅端再 Marshal 一次得到的是带引号的 base64 文本,跨节点转发后客户端收到乱码; // RawMessage 序列化时原样内嵌 JSON,反序列化时保留原始字节,两端零转换。 Payload json.RawMessage `json:"payload"` } // ========================================== // 认证相关 DTO // ========================================== /** * LoginReq * 作用:登录请求参数 */ type LoginReq struct { // 账号(邮箱或手机号) Account string `json:"account" binding:"required"` // 密码 Password string `json:"password" binding:"required"` // 记住我 Remember bool `json:"remember,omitempty"` } /** * RegisterReq * 作用:注册请求参数 */ type RegisterReq struct { // 邮箱 Email string `json:"email" binding:"required,email"` // 手机号 Phone string `json:"phone" binding:"required"` // 密码 Password string `json:"password" binding:"required,min=8"` // 确认密码 ConfirmPassword string `json:"confirm_password" binding:"required"` // 验证码(可选) Code string `json:"code,omitempty"` // 同意用户协议 AgreeTerms bool `json:"agree_terms" binding:"required"` } /** * LoginResponse * 作用:登录响应 */ type LoginResponse struct { // Token Token string `json:"token"` // 用户信息 User User `json:"user"` } /** * RegisterResponse * 作用:注册响应 */ type RegisterResponse struct { // Token Token string `json:"token"` // 用户信息 User User `json:"user"` } /** * SendCodeReq * 作用:发送验证码请求 */ type SendCodeReq struct { // 邮箱或手机号 Target string `json:"target" binding:"required"` // 类型 - "email" 或 "sms" Type string `json:"type" binding:"required,oneof=email sms"` } // ========================================== // 朋友圈相关实体 (Moments) // ========================================== /** * Moment * 对应数据库表:moments * 作用:朋友圈动态表 */ type Moment struct { // 动态ID ID uint `gorm:"primaryKey;comment:动态ID" json:"id"` // 发布者ID UserID string `gorm:"type:varchar(100);index;comment:发布者ID" json:"user_id"` // 文字内容 Content string `gorm:"type:text;comment:文字内容" json:"content"` // 媒体类型: 0=纯文字 1=图片 2=视频 MediaType int8 `gorm:"type:tinyint;default:0;comment:媒体类型" json:"media_type"` // 媒体URL列表(JSON数组) MediaURLs string `gorm:"type:text;comment:媒体URL列表" json:"media_urls"` // 位置信息 Location string `gorm:"type:varchar(255);comment:位置信息" json:"location"` // 可见性: 0=公开 1=仅好友 2=部分好友可见 3=部分好友不可见 Visibility int8 `gorm:"type:tinyint;default:0;comment:可见性" json:"visibility"` // 可见/不可见用户ID列表(JSON数组) VisibleUserIDs string `gorm:"type:text;comment:可见用户ID列表" json:"visible_user_ids"` // @的用户ID列表(JSON数组) MentionUserIDs string `gorm:"type:text;comment:@的用户ID列表" json:"mention_user_ids"` // 话题标签(JSON数组) TopicTags string `gorm:"type:varchar(500);comment:话题标签" json:"topic_tags"` // 点赞数 LikeCount int `gorm:"type:int;default:0;comment:点赞数" json:"like_count"` // 评论数 CommentCount int `gorm:"type:int;default:0;comment:评论数" json:"comment_count"` // 是否已删除 IsDeleted bool `gorm:"type:tinyint(1);default:0;comment:是否已删除" json:"-"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 更新时间 UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` // 关联字段(不存储在数据库中) User *User `gorm:"-" json:"user,omitempty"` IsLiked bool `gorm:"-" json:"is_liked"` Likes []MomentLike `gorm:"-" json:"likes,omitempty"` Comments []MomentComment `gorm:"-" json:"comments,omitempty"` } // TableName 指定表名 func (Moment) TableName() string { return "moments" } /** * MomentLike * 对应数据库表:moment_likes * 作用:动态点赞表 */ type MomentLike struct { // 主键ID ID uint `gorm:"primaryKey;comment:主键ID" json:"id"` // 动态ID。与 UserID 组成唯一索引:并发点赞时靠数据库唯一约束兜底, // 防止"先查后插"竞态导致同一用户对同一动态插入两条点赞记录 MomentID uint `gorm:"type:bigint;uniqueIndex:uk_moment_user;comment:动态ID" json:"moment_id"` // 点赞用户ID UserID string `gorm:"type:varchar(100);uniqueIndex:uk_moment_user;comment:点赞用户ID" json:"user_id"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 关联字段(不存储在数据库中) User *User `gorm:"-" json:"user,omitempty"` } // TableName 指定表名 func (MomentLike) TableName() string { return "moment_likes" } /** * MomentComment * 对应数据库表:moment_comments * 作用:动态评论表 */ type MomentComment struct { // 评论ID ID uint `gorm:"primaryKey;comment:评论ID" json:"id"` // 动态ID MomentID uint `gorm:"type:bigint;index;comment:动态ID" json:"moment_id"` // 评论者ID UserID string `gorm:"type:varchar(100);index;comment:评论者ID" json:"user_id"` // 回复的评论ID(NULL为直接评论) ReplyToCommentID *uint `gorm:"type:bigint;index;comment:回复的评论ID" json:"reply_to_comment_id"` // 回复的用户ID ReplyToUserID string `gorm:"type:varchar(100);comment:回复的用户ID" json:"reply_to_user_id"` // 评论内容 Content string `gorm:"type:text;comment:评论内容" json:"content"` // 是否已删除 IsDeleted bool `gorm:"type:tinyint(1);default:0;comment:是否已删除" json:"-"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 关联字段(不存储在数据库中) User *User `gorm:"-" json:"user,omitempty"` ReplyToUser *User `gorm:"-" json:"reply_to_user,omitempty"` } // TableName 指定表名 func (MomentComment) TableName() string { return "moment_comments" } /** * MomentNotification * 对应数据库表:moment_notifications * 作用:朋友圈通知表 */ type MomentNotification struct { // 通知ID ID uint `gorm:"primaryKey;comment:通知ID" json:"id"` // 接收通知的用户ID UserID string `gorm:"type:varchar(100);index;comment:接收通知的用户ID" json:"user_id"` // 触发通知的用户ID FromUserID string `gorm:"type:varchar(100);comment:触发通知的用户ID" json:"from_user_id"` // 关联的动态ID MomentID uint `gorm:"type:bigint;index;comment:关联的动态ID" json:"moment_id"` // 通知类型: 1=点赞 2=评论 3=回复 4=@提及 Type int8 `gorm:"type:tinyint;comment:通知类型" json:"type"` // 关联的评论ID CommentID *uint `gorm:"type:bigint;comment:关联的评论ID" json:"comment_id"` // 是否已读 IsRead bool `gorm:"type:tinyint(1);default:0;index;comment:是否已读" json:"is_read"` // 创建时间 CreatedAt time.Time `gorm:"autoCreateTime;index;comment:创建时间" json:"created_at"` // 关联字段(不存储在数据库中) FromUser *User `gorm:"-" json:"from_user,omitempty"` Moment *Moment `gorm:"-" json:"moment,omitempty"` Comment *MomentComment `gorm:"-" json:"comment,omitempty"` } // TableName 指定表名 func (MomentNotification) TableName() string { return "moment_notifications" } // ========================================== // 朋友圈相关 DTO // ========================================== /** * CreateMomentReq * 作用:发布动态请求参数 */ type CreateMomentReq struct { // 文字内容 Content string `json:"content"` // 媒体类型: 0=纯文字 1=图片 2=视频 MediaType int8 `json:"media_type"` // 媒体URL列表 MediaURLs []string `json:"media_urls"` // 位置信息 Location string `json:"location,omitempty"` // 可见性: 0=公开 1=仅好友 2=部分好友可见 3=部分好友不可见 Visibility int8 `json:"visibility"` // 可见/不可见用户ID列表 VisibleUserIDs []string `json:"visible_user_ids,omitempty"` // @的用户ID列表 MentionUserIDs []string `json:"mention_user_ids,omitempty"` // 话题标签 TopicTags []string `json:"topic_tags,omitempty"` } /** * CreateCommentReq * 作用:发表评论请求参数 */ type CreateCommentReq struct { // 评论内容 Content string `json:"content" binding:"required"` // 回复的评论ID(可选,回复评论时使用) ReplyToCommentID *uint `json:"reply_to_comment_id,omitempty"` } /** * MomentNotifPayload * 作用:WebSocket 推送朋友圈通知的数据结构 */ type MomentNotifPayload struct { // 通知类型: "like", "comment", "reply", "mention" Type string `json:"type"` // 动态ID MomentID uint `json:"moment_id"` // 触发通知的用户 FromUser *User `json:"from_user"` // 评论内容(评论/回复时使用) Content string `json:"content,omitempty"` // 评论ID CommentID uint `json:"comment_id,omitempty"` } // 朋友圈通知类型常量 const ( MomentNotifTypeLike = 1 // 点赞 MomentNotifTypeComment = 2 // 评论 MomentNotifTypeReply = 3 // 回复 MomentNotifTypeMention = 4 // @提及 ) // 动态可见性常量 const ( MomentVisibilityPublic = 0 // 公开 MomentVisibilityFriendsOnly = 1 // 仅好友可见 MomentVisibilityPartialVisible = 2 // 部分好友可见 MomentVisibilityPartialHidden = 3 // 部分好友不可见 ) // 媒体类型常量 const ( MomentMediaTypeText = 0 // 纯文字 MomentMediaTypeImage = 1 // 图片 MomentMediaTypeVideo = 2 // 视频 ) // ========================================== // 已读回执与用户设置 // ========================================== /** * MessageReadReceipt * 作用:消息已读回执表 */ type MessageReadReceipt struct { ID uint `json:"id" gorm:"primaryKey"` MessageID uint `json:"message_id" gorm:"index;not null;uniqueIndex:uk_msg_user,priority:1"` UserID string `json:"user_id" gorm:"size:100;index;not null;uniqueIndex:uk_msg_user,priority:2"` ReadAt time.Time `json:"read_at" gorm:"not null"` } func (MessageReadReceipt) TableName() string { return "message_read_receipts" } /** * UserSetting * 作用:用户个性化设置键值表 */ type UserSetting struct { UserID string `json:"user_id" gorm:"size:100;primaryKey"` SettingKey string `json:"setting_key" gorm:"size:50;primaryKey"` SettingValue string `json:"setting_value" gorm:"type:text"` UpdatedAt time.Time `json:"updated_at"` } func (UserSetting) TableName() string { return "user_settings" } // ========================================== // 常量定义 // ========================================== const ( // Redis Key 前缀: 用户所在节点映射 (Hash结构或String结构) KeyUserNodeMap = "ws:user:node:" // Redis Channel: 集群广播频道 ChanClusterBroadcast = "ws:cluster:broadcast" // 文件大小限制(字节) MaxImageSize = 10 * 1024 * 1024 // 10MB MaxVideoSize = 500 * 1024 * 1024 // 500MB MaxAudioSize = 50 * 1024 * 1024 // 50MB MaxFileSize = 100 * 1024 * 1024 // 100MB // 消息类型常量 MessageTypeText = 0 // 文本 MessageTypeImage = 1 // 图片 MessageTypeAudio = 2 // 语音 MessageTypeVideo = 3 // 视频 MessageTypeSystem = 4 // 系统消息 MessageTypeFriendNotif = 5 // 好友通知 MessageTypeSignal = 6 // 信令消息 MessageTypeGroupNotif = 7 // 群通知 MessageTypeFile = 8 // 文件 MessageTypeMoments = 9 // 朋友圈通知(预留) // 会话类型常量(chat_conversations.type / chat_rooms.type) ConversationTypeP2P = 1 // 私聊 ConversationTypeGroup = 2 // 群聊 )