thread

package
v1.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 4, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ThreadStatusActive   = 1 // 活跃
	ThreadStatusArchived = 2 // 已归档
	ThreadStatusDeleted  = 3 // 已删除
)

Thread 状态

View Source
const (
	MemberRoleNormal  = 0 // 普通成员
	MemberRoleCreator = 1 // 创建者
)

成员角色

View Source
const (
	DefaultThreadPageSize int64 = 15
	MaxThreadPageSize     int64 = 100
)

子区列表分页默认值

View Source
const (
	ListStatusActive   = "active"
	ListStatusArchived = "archived"
	ListStatusAll      = "all"
)

listThreads ?status= 入参合法值。

View Source
const ChannelIDSeparator = "____"

ChannelID 分隔符

View Source
const (
	ContentTypeThreadCreated = 1100 // 子区创建通知
)

消息类型(与客户端约定)

View Source
const ThreadSeqKey = "thread"

Sequence Key

Variables

View Source
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

func BuildChannelID(groupNo, shortID string) string

BuildChannelID 构建 channelID

func IsValidGroupNo

func IsValidGroupNo(groupNo string) bool

IsValidGroupNo 验证 groupNo 格式(32位十六进制)

func IsValidShortID

func IsValidShortID(shortID string) bool

IsValidShortID 验证 shortID 格式(snowflake ID: 纯数字,15-20位)

func ParseChannelID

func ParseChannelID(channelID string) (groupNo, shortID string, err error)

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

type ArchiveWorker struct {
	log.Log
	// contains filtered or unexported fields
}

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 再启动新的。

func (*ArchiveWorker) Stop

func (w *ArchiveWorker) Stop()

Stop 通知 worker 退出并等待当前 RunOnce 跑完。

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 NewDB

func NewDB(ctx *config.Context) *DB

NewDB 创建数据库操作实例

func (*DB) ArchiveStaleBatch

func (d *DB) ArchiveStaleBatch(threshold time.Time, batchSize int, version int64) (int64, error)

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

func (d *DB) CountByGroupNo(groupNo string) (int64, error)

CountByGroupNo 统计群下活跃子区总数。

func (*DB) CountByGroupNoWithStatus

func (d *DB) CountByGroupNoWithStatus(groupNo string, statuses []int) (int64, error)

CountByGroupNoWithStatus 统计群下指定 status 集合的子区总数。 statuses 为空时返回 0,与 QueryByGroupNoWithStatus 对齐。

func (*DB) CountMembers

func (d *DB) CountMembers(threadID int64) (int, error)

CountMembers 统计子区成员数量

func (*DB) CountMembersBatch

func (d *DB) CountMembersBatch(threadIDs []int64) (map[int64]int, error)

CountMembersBatch 批量统计子区成员数量

func (*DB) DeleteMember

func (d *DB) DeleteMember(threadID int64, uid string) error

DeleteMember 删除子区成员

func (*DB) DeleteThreadAndMembers added in v1.8.0

func (d *DB) DeleteThreadAndMembers(threadID int64) error

DeleteThreadAndMembers 删除子区及其所有成员记录(用于并发创建后父群解散的清理场景)

func (*DB) DeleteThreadMd

func (d *DB) DeleteThreadMd(groupNo, shortID, deletedBy string) (int64, error)

DeleteThreadMd 删除子区 GROUP.md 内容,保留删除者 UID,返回新版本号

func (*DB) ExistByGroupNoAndShortID

func (d *DB) ExistByGroupNoAndShortID(groupNo, shortID string) (bool, error)

ExistByGroupNoAndShortID 检查群下的子区是否存在

func (*DB) ExistByShortID

func (d *DB) ExistByShortID(shortID string) (bool, error)

ExistByShortID 检查子区是否存在

func (*DB) ExistMember

func (d *DB) ExistMember(threadID int64, uid string) (bool, error)

ExistMember 检查是否是子区成员

func (*DB) Insert

func (d *DB) Insert(m *Model) error

Insert 插入子区

func (*DB) InsertMember

func (d *DB) InsertMember(m *MemberModel) error

InsertMember 添加子区成员

func (*DB) InsertMemberTx

func (d *DB) InsertMemberTx(m *MemberModel, tx *dbr.Tx) error

InsertMemberTx 事务添加子区成员

func (*DB) InsertTx

func (d *DB) InsertTx(m *Model, tx *dbr.Tx) error

InsertTx 事务插入子区

func (*DB) InsertTxReturningID

func (d *DB) InsertTxReturningID(m *Model, tx *dbr.Tx) (int64, error)

InsertTxReturningID 事务插入子区并返回 ID

func (*DB) MarkDeleted

func (d *DB) MarkDeleted(shortID string, newVersion func() (int64, error)) error

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

func (d *DB) QueryActiveShortIDs(shortIDs []string) ([]string, error)

QueryActiveShortIDs 批量查询 status=active 的子区 shortID。 用于 /v1/conversation/sync 过滤路径:archived/deleted 子区都不返回给客户端。 archived 子区由 server-side cron (#1376) 维护;收到消息时通过 RecordMessageAndReactivate 自动复活为 active,重新出现在 sync 列表里。

func (*DB) QueryByGroupNo

func (d *DB) QueryByGroupNo(groupNo string, offset, limit int64) ([]*Model, error)

QueryByGroupNo 分页查询群下的活跃子区。 为兼容旧调用方保留 active-only 语义;如需按其他状态查询请用 QueryByGroupNoWithStatus。

func (*DB) QueryByGroupNoAndShortID

func (d *DB) QueryByGroupNoAndShortID(groupNo, shortID string) (*Model, error)

QueryByGroupNoAndShortID 根据群编号和 shortID 查询子区

func (*DB) QueryByGroupNoWithStatus

func (d *DB) QueryByGroupNoWithStatus(groupNo string, statuses []int, offset, limit int64) ([]*Model, error)

QueryByGroupNoWithStatus 分页查询群下指定 status 集合的子区。 statuses 为空时返回空列表,避免误拉所有数据(含 deleted)。

func (*DB) QueryByID

func (d *DB) QueryByID(id int64) (*Model, error)

QueryByID 根据 ID 查询子区

func (*DB) QueryByShortID

func (d *DB) QueryByShortID(shortID string) (*Model, error)

QueryByShortID 根据 shortID 查询子区

func (*DB) QueryByShortIDs deprecated

func (d *DB) QueryByShortIDs(shortIDs []string) (map[string]*Model, error)

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

func (d *DB) QueryMemberUIDs(threadID int64) ([]string, error)

QueryMemberUIDs 查询子区成员 UID 列表

func (*DB) QueryMembers

func (d *DB) QueryMembers(threadID int64) ([]*MemberModel, error)

QueryMembers 查询子区成员

func (*DB) QueryMessageFromUID

func (d *DB) QueryMessageFromUID(channelID string, messageID int64) (string, error)

QueryMessageFromUID 根据 channelID 和 messageID 查询消息发送者

func (*DB) QueryNonDeletedShortIDs

func (d *DB) QueryNonDeletedShortIDs(shortIDs []string) ([]string, error)

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

func (d *DB) QuerySourceMessageIDsByShortIDs(shortIDs []string) (map[string]*int64, error)

QuerySourceMessageIDsByShortIDs 批量查询子区的 source_message_id 返回 map[shortID]*int64,nil 值表示无源消息

func (*DB) QueryThreadIDByShortID

func (d *DB) QueryThreadIDByShortID(shortID string) (int64, error)

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) Update

func (d *DB) Update(m *Model) error

Update 更新子区信息

func (*DB) UpdateMessageStats

func (d *DB) UpdateMessageStats(shortID string, content string, senderUID string) error

UpdateMessageStats 原子更新消息统计(收到消息时调用)

func (*DB) UpdateName

func (d *DB) UpdateName(shortID string, name string, newVersion func() (int64, error)) error

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

func (d *DB) UpdateThreadMd(groupNo, shortID, content, updatedBy string) (int64, error)

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 子区服务接口

func NewService

func NewService(ctx *config.Context) IService

NewService 创建子区服务

type MemberCountResult

type MemberCountResult struct {
	ThreadID int64 `db:"thread_id"`
	Count    int   `db:"count"`
}

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

type Service struct {
	log.Log
	// contains filtered or unexported fields
}

Service 子区服务实现

func (*Service) ArchiveThread

func (s *Service) ArchiveThread(groupNo, shortID, operatorUID string) error

ArchiveThread 归档子区

func (*Service) CanDelete

func (s *Service) CanDelete(groupNo, shortID, uid string) (bool, error)

CanDelete 检查是否可以删除

func (*Service) CanEditThreadMd

func (s *Service) CanEditThreadMd(groupNo, shortID, uid string) (bool, error)

CanEditThreadMd 检查是否有编辑子区 GROUP.md 的权限 权限规则:子区创建者 或 群创建者/管理员 供 API Handler 层在调用 UpdateThreadMd/DeleteThreadMd 前使用

func (*Service) CreateThread

func (s *Service) CreateThread(req *CreateThreadReq) (*ThreadResp, error)

CreateThread 创建子区

func (*Service) DeleteThread

func (s *Service) DeleteThread(groupNo, shortID, operatorUID string) error

DeleteThread 删除子区

func (*Service) DeleteThreadMd

func (s *Service) DeleteThreadMd(groupNo, shortID, deletedBy string) (int64, error)

DeleteThreadMd 删除子区 GROUP.md 纯数据操作透传,权限检查由 API Handler 层完成

func (*Service) ExistThread

func (s *Service) ExistThread(groupNo, shortID string) (bool, error)

ExistThread 检查子区是否存在

func (*Service) GetMemberUIDs

func (s *Service) GetMemberUIDs(groupNo, shortID string) ([]string, error)

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) IsMember

func (s *Service) IsMember(groupNo, shortID, uid string) (bool, error)

IsMember 检查是否是子区成员

func (*Service) JoinThread

func (s *Service) JoinThread(groupNo, shortID, uid string) error

JoinThread 加入子区

func (*Service) LeaveThread

func (s *Service) LeaveThread(groupNo, shortID, uid string) error

LeaveThread 离开子区

func (*Service) UnarchiveThread

func (s *Service) UnarchiveThread(groupNo, shortID, operatorUID string) error

UnarchiveThread 取消归档

func (*Service) UpdateName

func (s *Service) UpdateName(groupNo, shortID, operatorUID, name string) error

UpdateName 修改子区名称

func (*Service) UpdateSetting

func (s *Service) UpdateSetting(groupNo, shortID, uid string, settings map[string]interface{}) error

UpdateSetting 更新用户对某子区的个人设置(目前支持 mute) 权限: 必须是活跃父群成员(排除黑名单); 无需是子区成员(与群聊 setting 行为保持一致) 注意: 这是个人偏好设置,不是群组内容,解散后仍然允许操作

func (*Service) UpdateThreadMd

func (s *Service) UpdateThreadMd(groupNo, shortID, content, updatedBy string) (int64, error)

UpdateThreadMd 更新子区 GROUP.md 纯数据操作透传,权限检查由 API Handler 层完成

type SettingModel

type SettingModel struct {
	GroupNo string
	ShortID string
	UID     string
	Mute    int
	Version int64
	db.BaseModel
}

SettingModel 子区用户设置

type SettingResp

type SettingResp struct {
	UID  string `json:"uid"`
	Mute int    `json:"mute"`
}

SettingResp 子区用户设置响应

type ShortRef

type ShortRef struct {
	GroupNo string
	ShortID string
}

ShortRef 表示 (group_no, short_id) 二元组的轻量引用。 供 QueryActiveByGroupShortIDs 与 auth checker 使用。

type Thread

type Thread struct {
	log.Log
	// contains filtered or unexported fields
}

Thread API 处理器

func New

func New(ctx *config.Context) *Thread

New 创建 Thread API 处理器

func (*Thread) Route

func (t *Thread) Route(r *wkhttp.WKHttp)

Route 注册路由

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 子区响应

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL