82 lines
3.6 KiB
Go
82 lines
3.6 KiB
Go
// Package store 提供会话存储接口
|
||
// 接口解耦使得可以在测试中用 mock 替换,生产用 Redis
|
||
package store
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
)
|
||
|
||
// Session 存储的会话数据结构(不含敏感字段)
|
||
type Session struct {
|
||
ID string `json:"session_id"`
|
||
ExpiresAt time.Time `json:"expires_at"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
}
|
||
|
||
// ParticipantInfo 参与者在全量快照中的状态
|
||
type ParticipantInfo struct {
|
||
Nickname string `json:"nickname"`
|
||
BrowserID string `json:"browser_id,omitempty"`
|
||
Submitted bool `json:"submitted"`
|
||
Match *bool `json:"match,omitempty"`
|
||
}
|
||
|
||
// SessionSnapshot Get 返回的全量快照
|
||
type SessionSnapshot struct {
|
||
SessionID string `json:"session_id"`
|
||
InitiatorHash string `json:"-"` // 发起方标准答案哈希,不对外暴露
|
||
InitiatorNickname string `json:"initiator_nickname"`
|
||
InitiatorBrowserID string `json:"initiator_browser_id,omitempty"`
|
||
InitiatorSubmitted bool `json:"initiator_submitted"`
|
||
AllowResubmit bool `json:"allow_resubmit"`
|
||
IgnoreWs bool `json:"ignore_ws"` // 是否忽略首尾空白
|
||
Participants []ParticipantInfo `json:"participants"`
|
||
ExpiresAt time.Time `json:"expires_at"`
|
||
}
|
||
|
||
// SessionStore 会话存储接口
|
||
type SessionStore interface {
|
||
// Create 原子创建会话(SET NX EX),同时记录发起方昵称和 browserId
|
||
Create(ctx context.Context, id, initiatorNickname, initiatorBrowserID string, ttl time.Duration) error
|
||
|
||
// SubmitInitiator 发起方提交标准答案(initiator_hash + initiator_nickname + 原始文本)
|
||
// 同时立即为所有已提交的参与者计算 match
|
||
SubmitInitiator(ctx context.Context, sessionID, nickname, hashHex, originalText string) error
|
||
|
||
// SubmitParticipant 参与者提交自己的文本(按 nickname 唯一标识,同时存原始文本)
|
||
// 返回:initiatorSubmitted(发起方是否已提交,是则 match 已算出)、match
|
||
SubmitParticipant(ctx context.Context, sessionID, nickname, hashHex, originalText string) (
|
||
initiatorSubmitted bool, match *bool, err error)
|
||
|
||
// JoinParticipant 参与者加入会话(只登记昵称,不提交文本)
|
||
// browserId 用于识别"同一个人",避免跨 tab 昵称冲突
|
||
JoinParticipant(ctx context.Context, sessionID, nickname, browserID string) error
|
||
|
||
// SetResubmit 设置是否允许参与者重复提交
|
||
SetResubmit(ctx context.Context, sessionID string, allow bool) error
|
||
|
||
// SetIgnoreWs 设置比对时是否忽略首尾空白(1=忽略,0=不忽略)
|
||
SetIgnoreWs(ctx context.Context, sessionID string, ignore bool) error
|
||
|
||
// GetIgnoreWs 获取当前会话的 ignore_ws 设置
|
||
GetIgnoreWs(ctx context.Context, sessionID string) (bool, error)
|
||
|
||
// RecalcMatches 按新的 ignoreWs 规则重新 trim + hash 所有已提交文本并重算 match
|
||
RecalcMatches(ctx context.Context, sessionID string, ignoreWs bool) error
|
||
|
||
// UpdateParticipantNickname 原子重命名参与者:将 participant:{old} → participant:{new}
|
||
// oldBrowserID 用于判断是否与冲突方是同一个浏览器(跳过冲突)
|
||
// 返回业务错误:ErrSessionNotFound / ErrNicknameConflict
|
||
UpdateParticipantNickname(ctx context.Context, sessionID, oldNick, newNick, oldBrowserID string) error
|
||
|
||
// RemoveParticipant 参与者离开时从 Redis 删除其所有字段(hash/text/match)
|
||
RemoveParticipant(ctx context.Context, sessionID, nickname string) error
|
||
|
||
// Get 获取会话当前状态全量快照
|
||
Get(ctx context.Context, sessionID string) (*SessionSnapshot, error)
|
||
|
||
// Delete 删除会话
|
||
Delete(ctx context.Context, sessionID string) error
|
||
}
|