Help us shape the future of Go! Take the 2026 Go Developer Survey

anonymous

package
v0.32.26 Latest Latest
Warning

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

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

Documentation

Overview

Package anonymous 实现匿名取件 service(仿 vastsa/PigeonBox UX)。

核心流程:

  1. 上传方:上传文件后,调用 GenerateCode 获取 6 位取件码(建立 pickup_code → share_code 映射)
  2. 取件方:输入 6 位码 + 可选密码,按码取文件
  3. 系统:校验(DB 为准)→ 扣减次数(DB 原子)→ 返回下载信息

设计要点(DB 为唯一真相源):

  • 6 位取件码落 file_codes.pickup_code 列(真相源,唯一索引防撞码); Redis 仅存 pickup_code → share_code 映射 + 展示信息作为加速缓存 (TTL=分享过期;永久分享不写 KV,经 DB 回退路径解析,重启不失联)
  • 过期时间、剩余次数、密码哈希全部以 file_codes 表为准
  • 取件码字符表去掉易混淆字符 0/O/1/I/L

Index

Constants

This section is empty.

Variables

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

type AnonymousShareParams struct {
	FilePath       string
	FileName       string
	FileSize       int64
	ExpireAt       *time.Time // 必填,决定 DB 过期时间 + Redis TTL
	MaxPickupCount int        // -1=无限, 0=默认无限, >0=限制
	Password       string     // 空=无密码
}

AnonymousShareParams 匿名分享端到端参数

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 {
	ShareCode   string // 真实 file_code
	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

func NewService(rdb *redis.Client, gormDB *gorm.DB) *Service

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

func (s *Service) Cancel(ctx context.Context, code string) error

Cancel 作废取件码(KV 映射与 DB pickup_code 列同步清除,真相源一致)

func (*Service) CreateAnonymousShare

func (s *Service) CreateAnonymousShare(ctx context.Context, p AnonymousShareParams) (string, error)

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

func (s *Service) Peek(ctx context.Context, code string) (*CodeMeta, *model.FileCode, error)

Peek 按取件码查询分享信息(不扣次数、不校验密码),仅供展示。 返回展示信息 CodeMeta + DB 记录(含剩余次数、过期时间等)。

func (*Service) ResolvePlaceholder added in v0.14.5

func (s *Service) ResolvePlaceholder(ctx context.Context, pickupCode string) (string, error)

ResolvePlaceholder 校验取件码对应一个"待回填"的占位分享(/anonymous/generate 创建、FilePath 为空),返回其 8 位分享码。供 presign 直传绑定通道在 Init 时 预检、Complete 时定位回填目标。 非占位(已完成上传)/不存在/已过期 → 显式错误,防把对象绑到任意分享上。

func (*Service) Retrieve

func (s *Service) Retrieve(ctx context.Context, code, password, clientIP string) (*CodeMeta, error)

Retrieve 按取件码取件(校验 + 扣减次数,DB 为准)。 返回展示信息 CodeMeta。每次成功调用扣减一次剩余次数。 clientIP 为可信代理解析后的取件方 IP(handler 注入,防爆破锁定键维度)。

Jump to

Keyboard shortcuts

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