Documentation
¶
Overview ¶
Package anonymous 实现匿名取件 service(仿 vastsa/PigeonBox UX)。
核心流程:
- 上传方:上传文件后,调用 GenerateCode 获取 6 位取件码(建立 pickup_code → share_code 映射)
- 取件方:输入 6 位码 + 可选密码,按码取文件
- 系统:校验(DB 为准)→ 扣减次数(DB 原子)→ 返回下载信息
设计要点(DB 为唯一真相源):
- 6 位取件码落 file_codes.pickup_code 列(真相源,唯一索引防撞码); Redis 仅存 pickup_code → share_code 映射 + 展示信息作为加速缓存 (TTL=分享过期;永久分享不写 KV,经 DB 回退路径解析,重启不失联)
- 过期时间、剩余次数、密码哈希全部以 file_codes 表为准
- 取件码字符表去掉易混淆字符 0/O/1/I/L
Index ¶
- Variables
- type AnonymousShareParams
- type BlockedError
- type CodeMeta
- type Service
- func (s *Service) BindFilePath(ctx context.Context, shareCode, filePath string, size int64, note string) error
- func (s *Service) Cancel(ctx context.Context, code string) error
- func (s *Service) CreateAnonymousShare(ctx context.Context, p AnonymousShareParams) (string, error)
- func (s *Service) GenerateCode(ctx context.Context, meta CodeMeta, expireAt time.Time) (string, error)
- func (s *Service) MintForShare(ctx context.Context, shareCode, fileName string, fileSize int64, ...) (string, error)
- func (s *Service) Peek(ctx context.Context, code string) (*CodeMeta, *model.FileCode, error)
- func (s *Service) ResolvePlaceholder(ctx context.Context, pickupCode string) (string, error)
- func (s *Service) Retrieve(ctx context.Context, code, password, clientIP string) (*CodeMeta, error)
Constants ¶
This section is empty.
Variables ¶
var ( ErrCodeNotFound = errors.New("pickup code not found") ErrCodeExpired = errors.New("pickup code expired") ErrCodeExhausted = errors.New("pickup code exhausted") ErrPasswordWrong = errors.New("password wrong") ErrNotReady = errors.New("share upload incomplete") // 登记占位未回填物理文件 )
错误哨兵
Functions ¶
This section is empty.
Types ¶
type BlockedError ¶
type BlockedError = pkgerrors.ShareBlocked
BlockedError 分享处于管控拒绝态(治理状态机)。 handler 侧 errors.As 后按 ErrCode 透传(20012 blocked / 20013 pending_review)。 实现收口 pkg/errors.ShareBlocked——历史上 share.ShareBlockedError 持有一份 逐字相同的拷贝(本域还用字面量而非常量,已漂移),2026-10-10 合并; 别名保持域内构造点与 handler 用法不变。
type CodeMeta ¶
type CodeMeta struct {
FileName string
FileSize int64
RequireAuth bool // 仅展示"是否需要密码"
// ExpireAt 分享过期时间(Unix 秒;0=未设/长期)。Retrieve 成功回填,
// 供取件结果页展示(此前 handler 恒发 0,页面"过期时间 -")。
ExpireAt int64
// RemainingCount 本次取件后剩余可取次数:-1=不限次,>0=剩余。
// 与 model.FileCode.ExpiredCount 语义一致(0=耗尽不会出现在成功响应里)。
RemainingCount int
// IsText 文本分享标记(Retrieve 时按 DB 回填,不进 KV meta 串)。
// 文本分享取件后前端跳详情页展示(结果页无文本渲染形态),handler 据此回传 share_code。
IsText bool
}
CodeMeta 取件码展示信息(仅存于 Redis,真实状态查 DB)
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service 匿名取件 service。 Redis 仅存映射 + 展示信息;过期/次数/密码等真实状态全部以 file_codes 表为准。
func NewService ¶
NewService 创建 service(repo 构造注入,去全局化终态)。 rdb nil 归一化:typed-nil 接口会骗过"Redis 可用"守卫(!= nil 判真)。 rdb 为 nil(redis.host 未配置)时进入单机内存模式:映射/展示信息存进程内 TTL KV,匿名取件全功能可用(单进程语义等价;重启丢失、不跨副本共享)。
func (*Service) BindFilePath ¶ added in v0.14.5
func (s *Service) BindFilePath(ctx context.Context, shareCode, filePath string, size int64, note string) error
BindFilePath 把直传完成的对象回填到占位分享(匿名取件码直传通道的最后一公里)。 以实际落盘对象为准更新 file_path 与 size;完成标记由调用方(presign Complete)保证。 note 非空时一并写入(先出码会话随 init 携带的文件备注)。
⚠️ 2026-10-26 审计修复:**原始文件名曾被永久丢弃**。 占位记录把 generate 时的**原始名**寄存在 `uuid_file_name`(因为占位期 file_path 为空 ⇒ 不能写 text:text 非空会让 IsTextShare() 把「待上传的 文件分享」误判成**文本分享**);而绑定时又把 uuid_file_name 清空 ⇒ 原始名彻底消失 ⇒ 展示名退化成存储名(up_<uuid>.pdf)——取件页/管理台/ 用户列表/MCP/传输日志**全部**显示 UUID。 ⇒ 绑定时把原始名**转写进 text**(此时 file_path 已非空 ⇒ 不再误判文本分享), `uuid_file_name` 回归本义(存储名 = 路径末段),一列只担一种语义。
func (*Service) CreateAnonymousShare ¶
CreateAnonymousShare 端到端:建 file_codes 记录 + 生成取件码。 供 gen handler 调用,避免 handler 直接操作 DAO;密码在此 bcrypt 哈希后存 DB。 返回 6 位取件码。
func (*Service) GenerateCode ¶
func (s *Service) GenerateCode(ctx context.Context, meta CodeMeta, expireAt time.Time) (string, error)
GenerateCode 生成 6 位取件码:落库 file_codes.pickup_code(真相源,唯一索引 防跨分享撞码),有 TTL 时另写 KV 映射加速解析。expireAt 零值=永久分享(不写 KV,取件走 DB 回退);过期时间应与 DB 记录对齐,已过期则拒绝铸造。
func (*Service) MintForShare ¶ added in v0.14.5
func (s *Service) MintForShare(ctx context.Context, shareCode, fileName string, fileSize int64, requireAuth bool, expireAt *time.Time) (string, error)
MintForShare 为既有分享铸造 6 位取件码(2026-10-08 起文本/文件/永久分享同权, "只保留 6 位码"呈现;E2E 密文分享由调用方跳过——密钥只随链接,凭码取不到明文)。 真相源落 DB pickup_code 列;有 TTL 时另写 KV 映射加速解析:
- expireAt nil(永久分享)→ 零值传入 GenerateCode:不写 KV(无 TTL 可言), 取件经 lookupShareCode 的 pickup_code 列回退路径解析,重启不失联
- KV 写失败返回错误但调用方应视为非致命(出码主流程不阻断)
func (*Service) ResolvePlaceholder ¶ added in v0.14.5
ResolvePlaceholder 校验取件码对应一个"待回填"的占位分享(/anonymous/generate 创建、FilePath 为空),返回其 8 位分享码。供 presign 直传绑定通道在 Init 时 预检、Complete 时定位回填目标。 非占位(已完成上传)/不存在/已过期 → 显式错误,防把对象绑到任意分享上。