Documentation
¶
Index ¶
- Constants
- Variables
- func BuildChannelID(groupNo, shortID string) string
- func IsValidGroupNo(groupNo string) bool
- func IsValidShortID(shortID string) bool
- func ParseChannelID(channelID string) (groupNo, shortID string, err error)
- type ArchiveConfig
- type ArchiveWorker
- type CreateThreadReq
- type DB
- func (d *DB) ArchiveStaleBatch(threshold time.Time, batchSize int, version int64) (int64, error)
- func (d *DB) CountByGroupNo(groupNo string) (int64, error)
- func (d *DB) CountByGroupNoWithStatus(groupNo string, statuses []int) (int64, error)
- func (d *DB) CountMembers(threadID int64) (int, error)
- func (d *DB) CountMembersBatch(threadIDs []int64) (map[int64]int, error)
- func (d *DB) DeleteMember(threadID int64, uid string) error
- func (d *DB) DeleteThreadAndMembers(threadID int64) error
- func (d *DB) DeleteThreadMd(groupNo, shortID, deletedBy string) (int64, error)
- func (d *DB) ExistByGroupNoAndShortID(groupNo, shortID string) (bool, error)
- func (d *DB) ExistByShortID(shortID string) (bool, error)
- func (d *DB) ExistMember(threadID int64, uid string) (bool, error)
- func (d *DB) Insert(m *Model) error
- func (d *DB) InsertMember(m *MemberModel) error
- func (d *DB) InsertMemberTx(m *MemberModel, tx *dbr.Tx) error
- func (d *DB) InsertTx(m *Model, tx *dbr.Tx) error
- func (d *DB) InsertTxReturningID(m *Model, tx *dbr.Tx) (int64, error)
- func (d *DB) MarkDeleted(shortID string, newVersion func() (int64, error)) error
- func (d *DB) QueryActiveByGroupShortIDs(refs []ShortRef) (map[string]*ThreadLite, error)
- func (d *DB) QueryActiveShortIDs(shortIDs []string) ([]string, error)
- func (d *DB) QueryByGroupNo(groupNo string, offset, limit int64) ([]*Model, error)
- func (d *DB) QueryByGroupNoAndShortID(groupNo, shortID string) (*Model, error)
- func (d *DB) QueryByGroupNoWithStatus(groupNo string, statuses []int, offset, limit int64) ([]*Model, error)
- func (d *DB) QueryByID(id int64) (*Model, error)
- func (d *DB) QueryByShortID(shortID string) (*Model, error)
- func (d *DB) QueryByShortIDs(shortIDs []string) (map[string]*Model, error)deprecated
- func (d *DB) QueryMemberUIDs(threadID int64) ([]string, error)
- func (d *DB) QueryMembers(threadID int64) ([]*MemberModel, error)
- func (d *DB) QueryMessageFromUID(channelID string, messageID int64) (string, error)
- func (d *DB) QueryNonDeletedShortIDs(shortIDs []string) ([]string, error)
- func (d *DB) QuerySetting(groupNo, shortID, uid string) (*SettingModel, error)
- func (d *DB) QuerySettingsWithUIDs(groupNo, shortID string, uids []string) ([]*SettingModel, error)
- func (d *DB) QuerySourceMessageIDsByShortIDs(shortIDs []string) (map[string]*int64, error)
- func (d *DB) QueryThreadIDByShortID(shortID string) (int64, error)
- func (d *DB) QueryThreadMd(groupNo, shortID string) (*ThreadMdResult, error)
- func (d *DB) QueryThreadMetaByShortIDs(shortIDs []string) (map[string]*ThreadMetaRow, error)
- func (d *DB) RecordMessageAndReactivate(shortID, content, senderUID string, newVersion func() (int64, error)) error
- func (d *DB) Update(m *Model) error
- func (d *DB) UpdateMessageStats(shortID string, content string, senderUID string) error
- func (d *DB) UpdateName(shortID string, name string, newVersion func() (int64, error)) error
- func (d *DB) UpdateStatusFrom(shortID string, expectedStatus, newStatus int, ...) error
- func (d *DB) UpdateThreadMd(groupNo, shortID, content, updatedBy string) (int64, error)
- func (d *DB) UpsertSetting(m *SettingModel) error
- type IService
- type MemberCountResult
- type MemberModel
- type MemberResp
- type Model
- type Service
- func (s *Service) ArchiveThread(groupNo, shortID, operatorUID string) error
- func (s *Service) CanDelete(groupNo, shortID, uid string) (bool, error)
- func (s *Service) CanEditThreadMd(groupNo, shortID, uid string) (bool, error)
- func (s *Service) CreateThread(req *CreateThreadReq) (*ThreadResp, error)
- func (s *Service) DeleteThread(groupNo, shortID, operatorUID string) error
- func (s *Service) DeleteThreadMd(groupNo, shortID, deletedBy string) (int64, error)
- func (s *Service) ExistThread(groupNo, shortID string) (bool, error)
- func (s *Service) GetMemberUIDs(groupNo, shortID string) ([]string, error)
- func (s *Service) GetMembers(groupNo, shortID string) ([]*MemberResp, error)
- func (s *Service) GetSettingsWithUIDs(groupNo, shortID string, uids []string) ([]*SettingResp, error)
- func (s *Service) GetThread(groupNo, shortID, loginUID string) (*ThreadResp, error)
- func (s *Service) GetThreadMd(groupNo, shortID string) (*ThreadMdResult, error)
- func (s *Service) GetThreads(groupNo string, statuses []int, pageIndex, pageSize int64) ([]*ThreadResp, int64, error)
- func (s *Service) IsMember(groupNo, shortID, uid string) (bool, error)
- func (s *Service) JoinThread(groupNo, shortID, uid string) error
- func (s *Service) LeaveThread(groupNo, shortID, uid string) error
- func (s *Service) UnarchiveThread(groupNo, shortID, operatorUID string) error
- func (s *Service) UpdateName(groupNo, shortID, operatorUID, name string) error
- func (s *Service) UpdateSetting(groupNo, shortID, uid string, settings map[string]interface{}) error
- func (s *Service) UpdateThreadMd(groupNo, shortID, content, updatedBy string) (int64, error)
- type SettingModel
- type SettingResp
- type ShortRef
- type Thread
- type ThreadLite
- type ThreadMdResult
- type ThreadMetaRow
- type ThreadResp
Constants ¶
const ( ThreadStatusActive = 1 // 活跃 ThreadStatusArchived = 2 // 已归档 ThreadStatusDeleted = 3 // 已删除 )
Thread 状态
const ( MemberRoleNormal = 0 // 普通成员 MemberRoleCreator = 1 // 创建者 )
成员角色
const ( DefaultThreadPageSize int64 = 15 MaxThreadPageSize int64 = 100 )
子区列表分页默认值
const ( ListStatusActive = "active" ListStatusArchived = "archived" ListStatusAll = "all" )
listThreads ?status= 入参合法值。
const ChannelIDSeparator = "____"
ChannelID 分隔符
const (
ContentTypeThreadCreated = 1100 // 子区创建通知
)
消息类型(与客户端约定)
const ThreadSeqKey = "thread"
Sequence Key
Variables ¶
var ( // ErrThreadNotFound 子区不存在(从未被创建或被物理删除)。 ErrThreadNotFound = errors.New("thread not found") // ErrThreadDeleted 子区当前 status=ThreadStatusDeleted。不允许 archive/unarchive/改名。 ErrThreadDeleted = errors.New("thread deleted") // ErrThreadStatusMismatch 行当前 status 与期望不符且不是目标状态。 // 例如 ArchiveThread 期望 active,但行已被并发改为 archived/deleted。 ErrThreadStatusMismatch = errors.New("thread status mismatch") // ErrThreadCASExhausted CAS 重试次数耗尽。基本不会触发。 ErrThreadCASExhausted = errors.New("thread CAS retry exhausted") )
CAS 写路径的 sentinel errors,让 service 层能精确区分"行不存在"、"行被并发删了"、 "行不在期望状态"等场景,而不是把所有"无变更"都当成成功。
Functions ¶
func BuildChannelID ¶
BuildChannelID 构建 channelID
func IsValidGroupNo ¶
IsValidGroupNo 验证 groupNo 格式(32位十六进制)
func IsValidShortID ¶
IsValidShortID 验证 shortID 格式(snowflake ID: 纯数字,15-20位)
func ParseChannelID ¶
ParseChannelID 解析 channelID
Types ¶
type ArchiveConfig ¶
type ArchiveConfig struct {
// Enabled 总开关。disabled 时 worker 不启动 ticker。
Enabled bool
// Threshold 子区"陈旧"判定阈值(自 last_message_at 起算)。0 视为禁用。
Threshold time.Duration
// Interval 两次 cron tick 之间的间隔。<=0 视为禁用。
Interval time.Duration
// BatchSize 单次 UPDATE 的最大行数。<=0 时回退默认值。
BatchSize int
// BatchSleep 两次批之间的 sleep,给 DB 喘息。<0 视为 0。
BatchSleep time.Duration
}
ArchiveConfig 子区自动归档 worker 的运行参数。
func LoadArchiveConfig ¶
func LoadArchiveConfig() ArchiveConfig
LoadArchiveConfig 从环境变量装载配置。 错误 / 越界值一律回退默认值,避免运维误填导致 worker 行为失控。 `DM_THREAD_AUTO_ARCHIVE_DAYS=0` 显式禁用阈值(threshold 归零),但 Enabled 仍可为 true。
type ArchiveWorker ¶
ArchiveWorker 周期性扫描 thread 表,把过期 active 子区切到 archived。
func NewArchiveWorker ¶
func NewArchiveWorker(ctx *config.Context, cfg ArchiveConfig) *ArchiveWorker
NewArchiveWorker 构造 worker。生产路径用 thread.NewDB 和 config.Context 注入。
func (*ArchiveWorker) RunOnce ¶
func (w *ArchiveWorker) RunOnce(ctx context.Context) (int64, error)
RunOnce 执行一轮归档循环:批量 UPDATE 直到一批返回 < batchSize 或 ctx 取消。 返回本轮累计归档行数。
安全保护:threshold<=0 / batchSize<=0 视为禁用,直接返回 (0, nil); ctx 取消时返回 ctx.Err() 让上层日志可区分"正常停机"vs"异常"。
func (*ArchiveWorker) Start ¶
func (w *ArchiveWorker) Start(ctx context.Context)
Start 启动后台 ticker。Enabled=false 或参数非法时不启动新 goroutine,但仍会 先停掉可能存在的旧 goroutine——避免未来热更新(enabled: true→false 再 Start) 留下孤儿 ticker。 重复调用幂等:先 stop 旧 goroutine 再启动新的。
type CreateThreadReq ¶
type CreateThreadReq struct {
GroupNo string
Name string
CreatorUID string
CreatorName string
SourceMessageID *int64
SourceMessagePayload json.RawMessage // 源消息原始 payload,用于拷贝到子区
}
CreateThreadReq 创建子区请求
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB 数据库操作
func (*DB) ArchiveStaleBatch ¶
ArchiveStaleBatch 批量把 status=ThreadStatusActive 且 last_message_at < threshold 的子区切到 ThreadStatusArchived,单次最多 batchSize 行,返回实际归档行数。
SQL 关键点:
- WHERE 加 version < ? 防赛跑:cron 拿到版本 V 后,若任何人(手动 archive/unarchive、 收消息 auto-unarchive)把同一行的版本号推到 >= V,本批不再触它,避免 cron 用旧 版本号覆盖更新的版本号,让 sync 客户端漏拉。
- ORDER BY last_message_at, id:保证 MySQL 复制(statement-based / mixed)确定性, 且和 idx_status_last_msg_id 三列索引同序,避免 filesort。
- last_message_at IS NULL 的子区(从未发过消息)一律保留,避免误归档新建空子区。
整批共享同一个 version(来自 caller 的 GenSeq):sync API 按 version 单调递增拉取, 一批同 version 不影响 cursor 推进。
func (*DB) CountByGroupNo ¶
CountByGroupNo 统计群下活跃子区总数。
func (*DB) CountByGroupNoWithStatus ¶
CountByGroupNoWithStatus 统计群下指定 status 集合的子区总数。 statuses 为空时返回 0,与 QueryByGroupNoWithStatus 对齐。
func (*DB) CountMembers ¶
CountMembers 统计子区成员数量
func (*DB) CountMembersBatch ¶
CountMembersBatch 批量统计子区成员数量
func (*DB) DeleteMember ¶
DeleteMember 删除子区成员
func (*DB) DeleteThreadAndMembers ¶ added in v1.8.0
DeleteThreadAndMembers 删除子区及其所有成员记录(用于并发创建后父群解散的清理场景)
func (*DB) DeleteThreadMd ¶
DeleteThreadMd 删除子区 GROUP.md 内容,保留删除者 UID,返回新版本号
func (*DB) ExistByGroupNoAndShortID ¶
ExistByGroupNoAndShortID 检查群下的子区是否存在
func (*DB) ExistByShortID ¶
ExistByShortID 检查子区是否存在
func (*DB) ExistMember ¶
ExistMember 检查是否是子区成员
func (*DB) InsertMemberTx ¶
func (d *DB) InsertMemberTx(m *MemberModel, tx *dbr.Tx) error
InsertMemberTx 事务添加子区成员
func (*DB) InsertTxReturningID ¶
InsertTxReturningID 事务插入子区并返回 ID
func (*DB) MarkDeleted ¶
MarkDeleted 把任何非 deleted 状态的子区切到 deleted。幂等:已删除直接返回 nil。
func (*DB) QueryActiveByGroupShortIDs ¶
func (d *DB) QueryActiveByGroupShortIDs(refs []ShortRef) (map[string]*ThreadLite, error)
QueryActiveByGroupShortIDs 按 (group_no, short_id) 批量查询子区。 只返回 status != ThreadStatusDeleted 的行,SELECT 限定最小列集合。 返回 map 的键是 "{groupNo}____{shortID}"(thread channel ID 形式)。 无对应行的键不会出现在 map 中,调用方按零值判定。
PR review Round-3:
- Blocking #3:只按 short_id 匹配可能撞上其他 group 的同名 short_id, 从鉴权角度是重大隐患,必须带上 group_no。
- Important #4:sidebar enrich 路径原先用 SELECT *,这里收窄到最小列。
func (*DB) QueryActiveShortIDs ¶
QueryActiveShortIDs 批量查询 status=active 的子区 shortID。 用于 /v1/conversation/sync 过滤路径:archived/deleted 子区都不返回给客户端。 archived 子区由 server-side cron (#1376) 维护;收到消息时通过 RecordMessageAndReactivate 自动复活为 active,重新出现在 sync 列表里。
func (*DB) QueryByGroupNo ¶
QueryByGroupNo 分页查询群下的活跃子区。 为兼容旧调用方保留 active-only 语义;如需按其他状态查询请用 QueryByGroupNoWithStatus。
func (*DB) QueryByGroupNoAndShortID ¶
QueryByGroupNoAndShortID 根据群编号和 shortID 查询子区
func (*DB) QueryByGroupNoWithStatus ¶
func (d *DB) QueryByGroupNoWithStatus(groupNo string, statuses []int, offset, limit int64) ([]*Model, error)
QueryByGroupNoWithStatus 分页查询群下指定 status 集合的子区。 statuses 为空时返回空列表,避免误拉所有数据(含 deleted)。
func (*DB) QueryByShortID ¶
QueryByShortID 根据 shortID 查询子区
func (*DB) QueryByShortIDs
deprecated
QueryByShortIDs 批量查询子区,返回 map[shortID]*Model。 用于 sidebar 聚合接口补齐子区的 last_message_at。
Deprecated: 仅按 short_id 不带 group_no/status 过滤会跨群命中且包含 deleted 行。新代码应使用 QueryActiveByGroupShortIDs(PR review Round-3 Blocking #3 / Important #4)。残留调用方清零后再行删除。
func (*DB) QueryMemberUIDs ¶
QueryMemberUIDs 查询子区成员 UID 列表
func (*DB) QueryMembers ¶
func (d *DB) QueryMembers(threadID int64) ([]*MemberModel, error)
QueryMembers 查询子区成员
func (*DB) QueryMessageFromUID ¶
QueryMessageFromUID 根据 channelID 和 messageID 查询消息发送者
func (*DB) QueryNonDeletedShortIDs ¶
QueryNonDeletedShortIDs 批量查询未删除的子区 shortID
func (*DB) QuerySetting ¶
func (d *DB) QuerySetting(groupNo, shortID, uid string) (*SettingModel, error)
QuerySetting 按 (groupNo, shortID, uid) 查询单条设置
func (*DB) QuerySettingsWithUIDs ¶
func (d *DB) QuerySettingsWithUIDs(groupNo, shortID string, uids []string) ([]*SettingModel, error)
QuerySettingsWithUIDs 批量查询一批用户对某子区的设置
func (*DB) QuerySourceMessageIDsByShortIDs ¶
QuerySourceMessageIDsByShortIDs 批量查询子区的 source_message_id 返回 map[shortID]*int64,nil 值表示无源消息
func (*DB) QueryThreadIDByShortID ¶
QueryThreadIDByShortID 根据 shortID 查询子区 ID
func (*DB) QueryThreadMd ¶
func (d *DB) QueryThreadMd(groupNo, shortID string) (*ThreadMdResult, error)
QueryThreadMd 查询子区 GROUP.md 内容
func (*DB) QueryThreadMetaByShortIDs ¶
func (d *DB) QueryThreadMetaByShortIDs(shortIDs []string) (map[string]*ThreadMetaRow, error)
QueryThreadMetaByShortIDs 批量查询子区元数据(source_message_id, message_count)
func (*DB) RecordMessageAndReactivate ¶
func (d *DB) RecordMessageAndReactivate(shortID, content, senderUID string, newVersion func() (int64, error)) error
RecordMessageAndReactivate 收到消息时的事务路径:在行锁内决定是否解档。
流程:BEGIN → SELECT ... FOR UPDATE → 看当前 status 决定是否解档 → UPDATE → COMMIT。 关键点:
- GenSeq(即 newVersion 回调)只在锁内、且确认当前确实 archived 时才调用。 避免 listener 在拿锁前预生成的版本号低于 cron 在它前面拿到的版本号, 从而把 thread.version 写"回退"——这是 sync 游标按 version 单调推进的前提。
- active 子区收消息不再消耗 GenSeq,热路径无写放大。
- status=deleted 的行直接 no-op,不被消息复活。
与 ArchiveStaleBatch 的并发收敛:
- cron 先拿锁 → status=archived → 我们 SELECT 读到 archived → 取新版本 → 解档为 active(新版本号严格 > cron 的版本号,因为 GenSeq 是全局单调)。
- 我们先拿锁 → last_message_at=NOW → cron 的 WHERE last_message_at<cutoff 不匹配 → cron 跳过本行。
func (*DB) UpdateMessageStats ¶
UpdateMessageStats 原子更新消息统计(收到消息时调用)
func (*DB) UpdateName ¶
UpdateName 改名。不允许在已删除的子区上改名(返回 ErrThreadDeleted)。
func (*DB) UpdateStatusFrom ¶
func (d *DB) UpdateStatusFrom(shortID string, expectedStatus, newStatus int, newVersion func() (int64, error)) error
UpdateStatusFrom 把 status 从 expectedStatus 原子地切换到 newStatus,带 version CAS guard 和重试。WHERE 同时校验 short_id / status==expectedStatus / version<新版本。
返回值语义:
- nil:成功写入
- nil(特殊):当前 status 已经是 newStatus(重复操作幂等成功)
- ErrThreadNotFound:行不存在
- ErrThreadDeleted:行已被并发删除
- ErrThreadStatusMismatch:行 status 与 expected 不符,也不是 newStatus
- ErrThreadCASExhausted:CAS 三连败(基本不会发生)
比起锁外读 status 再调 UpdateStatus 的旧路径,这里把状态判定整合进 UPDATE 的 WHERE, 闭掉了"读完 status 之后被 delete/cron 改写"的窗口。
func (*DB) UpdateThreadMd ¶
UpdateThreadMd 更新子区 GROUP.md 内容,返回新版本号
func (*DB) UpsertSetting ¶
func (d *DB) UpsertSetting(m *SettingModel) error
UpsertSetting 按 (group_no, short_id, uid) 幂等写入,避免并发 read-then-write 竞态
type IService ¶
type IService interface {
// CreateThread 创建子区
CreateThread(req *CreateThreadReq) (*ThreadResp, error)
// UpdateName 修改子区名称
UpdateName(groupNo, shortID, operatorUID, name string) error
// GetThreads 分页获取群下的子区,同时返回总数。
// statuses 决定包含的 status 集合(active / archived / 二者);nil 或空一律按 active。
GetThreads(groupNo string, statuses []int, pageIndex, pageSize int64) ([]*ThreadResp, int64, error)
// GetThread 获取子区详情,loginUID 非空时填充当前用户的 mute 状态
GetThread(groupNo, shortID, loginUID string) (*ThreadResp, error)
// ArchiveThread 归档子区
ArchiveThread(groupNo, shortID, operatorUID string) error
// UnarchiveThread 取消归档
UnarchiveThread(groupNo, shortID, operatorUID string) error
// DeleteThread 删除子区
DeleteThread(groupNo, shortID, operatorUID string) error
// CanDelete 检查是否可以删除
CanDelete(groupNo, shortID, uid string) (bool, error)
// ExistThread 检查子区是否存在
ExistThread(groupNo, shortID string) (bool, error)
// JoinThread 加入子区
JoinThread(groupNo, shortID, uid string) error
// LeaveThread 离开子区
LeaveThread(groupNo, shortID, uid string) error
// GetMembers 获取子区成员
GetMembers(groupNo, shortID string) ([]*MemberResp, error)
// GetMemberUIDs 获取子区成员 UID 列表
GetMemberUIDs(groupNo, shortID string) ([]string, error)
// IsMember 检查是否是子区成员
IsMember(groupNo, shortID, uid string) (bool, error)
// GetThreadMd 获取子区 GROUP.md
GetThreadMd(groupNo, shortID string) (*ThreadMdResult, error)
// UpdateThreadMd 更新子区 GROUP.md(纯透传,不含权限检查)
UpdateThreadMd(groupNo, shortID, content, updatedBy string) (int64, error)
// DeleteThreadMd 删除子区 GROUP.md(纯透传,不含权限检查)
DeleteThreadMd(groupNo, shortID, deletedBy string) (int64, error)
// CanEditThreadMd 检查是否有编辑子区 GROUP.md 的权限(供 API Handler 层调用)
CanEditThreadMd(groupNo, shortID, uid string) (bool, error)
// UpdateSetting 更新当前用户对子区的个人设置(目前支持 mute)
UpdateSetting(groupNo, shortID, uid string, settings map[string]interface{}) error
// GetSettingsWithUIDs 批量查询一批用户对某子区的设置,无记录则不返回
GetSettingsWithUIDs(groupNo, shortID string, uids []string) ([]*SettingResp, error)
}
IService 子区服务接口
type MemberCountResult ¶
MemberCountResult 成员数量结果
type MemberModel ¶
type MemberModel struct {
ID int64 `json:"id"`
ThreadID int64 `json:"thread_id"`
UID string `json:"uid"`
Role int `json:"role"` // 0=普通成员, 1=创建者
Version int64 `json:"version"`
db.BaseModel
}
MemberModel 子区成员数据模型
type MemberResp ¶
type MemberResp struct {
UID string `json:"uid"`
Name string `json:"name"`
Role int `json:"role"` // 0=普通成员, 1=创建者
CreatedAt string `json:"created_at"`
}
MemberResp 子区成员响应
type Model ¶
type Model struct {
ShortID string `json:"short_id"`
GroupNo string `json:"group_no"`
Name string `json:"name"`
CreatorUID string `json:"creator_uid"`
SourceMessageID *int64 `json:"source_message_id"`
Status int `json:"status"`
Version int64 `json:"version"`
MessageCount int64 `json:"message_count"`
LastMessageAt *time.Time `json:"last_message_at"`
LastMessageContent string `json:"last_message_content"`
LastMessageSenderUID string `json:"last_message_sender_uid"`
// GROUP.md 相关字段
ThreadMd *string `json:"thread_md"`
ThreadMdVersion int64 `json:"thread_md_version"`
ThreadMdUpdatedAt *time.Time `json:"thread_md_updated_at"`
ThreadMdUpdatedBy string `json:"thread_md_updated_by"`
db.BaseModel
}
Model 子区数据模型
type Service ¶
Service 子区服务实现
func (*Service) ArchiveThread ¶
ArchiveThread 归档子区
func (*Service) CanEditThreadMd ¶
CanEditThreadMd 检查是否有编辑子区 GROUP.md 的权限 权限规则:子区创建者 或 群创建者/管理员 供 API Handler 层在调用 UpdateThreadMd/DeleteThreadMd 前使用
func (*Service) CreateThread ¶
func (s *Service) CreateThread(req *CreateThreadReq) (*ThreadResp, error)
CreateThread 创建子区
func (*Service) DeleteThread ¶
DeleteThread 删除子区
func (*Service) DeleteThreadMd ¶
DeleteThreadMd 删除子区 GROUP.md 纯数据操作透传,权限检查由 API Handler 层完成
func (*Service) ExistThread ¶
ExistThread 检查子区是否存在
func (*Service) GetMemberUIDs ¶
GetMemberUIDs 获取子区成员 UID 列表
func (*Service) GetMembers ¶
func (s *Service) GetMembers(groupNo, shortID string) ([]*MemberResp, error)
GetMembers 获取子区成员
func (*Service) GetSettingsWithUIDs ¶
func (s *Service) GetSettingsWithUIDs(groupNo, shortID string, uids []string) ([]*SettingResp, error)
GetSettingsWithUIDs 批量查询一批用户对某子区的设置,无记录不返回
func (*Service) GetThread ¶
func (s *Service) GetThread(groupNo, shortID, loginUID string) (*ThreadResp, error)
GetThread 获取子区详情
func (*Service) GetThreadMd ¶
func (s *Service) GetThreadMd(groupNo, shortID string) (*ThreadMdResult, error)
GetThreadMd 获取子区 GROUP.md
func (*Service) GetThreads ¶
func (s *Service) GetThreads(groupNo string, statuses []int, pageIndex, pageSize int64) ([]*ThreadResp, int64, error)
GetThreads 分页获取群下的子区,同时返回总数。
func (*Service) JoinThread ¶
JoinThread 加入子区
func (*Service) LeaveThread ¶
LeaveThread 离开子区
func (*Service) UnarchiveThread ¶
UnarchiveThread 取消归档
func (*Service) UpdateName ¶
UpdateName 修改子区名称
type SettingModel ¶
type SettingModel struct {
GroupNo string
ShortID string
UID string
Mute int
Version int64
db.BaseModel
}
SettingModel 子区用户设置
type SettingResp ¶
SettingResp 子区用户设置响应
type ShortRef ¶
ShortRef 表示 (group_no, short_id) 二元组的轻量引用。 供 QueryActiveByGroupShortIDs 与 auth checker 使用。
type ThreadLite ¶
type ThreadLite struct {
GroupNo string `db:"group_no"`
ShortID string `db:"short_id"`
Status int `db:"status"`
LastMessageAt *time.Time `db:"last_message_at"`
}
ThreadLite 只保留 sidebar 聚合 / auth check 必需的最小字段集。 不读 thread.Model 的全部列(包括 thread_md 等大文本字段),以最小化 I/O (PR review Round-3 Important #4)。
type ThreadMdResult ¶
type ThreadMdResult struct {
Content string `json:"content"`
Version int64 `json:"version"`
UpdatedAt *time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by"`
}
ThreadMdResult 子区 GROUP.md 查询结果
type ThreadMetaRow ¶
type ThreadMetaRow struct {
ShortID string `json:"short_id"`
SourceMessageID *int64 `json:"source_message_id"`
MessageCount int64 `json:"message_count"`
}
ThreadMetaRow 子区元数据(用于会话列表批量查询)
type ThreadResp ¶
type ThreadResp struct {
ShortID string `json:"short_id"`
GroupNo string `json:"group_no"`
GroupName string `json:"group_name"`
ChannelID string `json:"channel_id"`
ChannelType uint8 `json:"channel_type"`
Name string `json:"name"`
CreatorUID string `json:"creator_uid"`
SourceMessageID *int64 `json:"source_message_id,omitempty"`
Status int `json:"status"`
MemberCount int `json:"member_count"`
MessageCount int64 `json:"message_count"`
LastMessageContent string `json:"last_message_content,omitempty"`
LastMessageSenderName string `json:"last_message_sender_name,omitempty"`
LastMessageAt string `json:"last_message_at"`
// GROUP.md 摘要信息
HasThreadMd bool `json:"has_thread_md"`
ThreadMdVersion int64 `json:"thread_md_version"`
ThreadMdUpdatedAt string `json:"thread_md_updated_at"`
// Mute 当前用户的子区免打扰状态,仅 GetThread 填充。
// nil = 用户未设置(前端应继承父群组 mute);0 = 显式未静音;1 = 显式静音。
Mute *int `json:"mute"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
ThreadResp 子区响应