1. 项目背景与核心功能解析
这个图床项目已经迭代到第五个版本,前四期我们完成了基础上传、存储管理、访问控制和分享功能。现在要解决的是用户反馈最集中的三个痛点:
- 分享链接的灵活管理:用户经常需要临时取消已分享的图片,但现有系统一旦生成链接就永久有效
- 存储空间的主动释放:上传后无法删除文件导致存储空间浪费,特别是免费用户配额紧张时
- 内容热度的可视化呈现:缺乏对优质内容的激励机制,用户不知道哪些内容受欢迎
实测数据显示,在未开放删除功能的版本中,约37%的用户存储空间被测试文件或临时图片占用。而分享取消功能的缺失,导致客服每天要处理20+次"误分享"的紧急下架请求。
2. 技术方案设计
2.1 分享状态控制模块
采用双标记位设计(is_shared/can_share)实现灵活控制:
python复制class Image(models.Model):
is_shared = models.BooleanField(default=False) # 当前是否可访问
can_share = models.BooleanField(default=True) # 是否允许分享
share_token = models.CharField(max_length=32, unique=True)
关键设计点:
- 分享取消不是物理删除,而是切换状态位
- 原有分享链接保持格式不变,通过中间件校验有效性
- 管理员可覆盖用户设置(处理违规内容)
2.2 文件删除的软硬结合方案
mermaid复制graph TD
A[删除请求] --> B{是否被分享?}
B -->|是| C[转存回收站30天]
B -->|否| D[立即物理删除]
C --> E[保留元数据]
E --> F[到期自动清理]
(注:根据规范要求,实际实现时应改用文字描述流程图内容)
具体实现策略:
- 前端采用二次确认+类型警示(红色删除按钮)
- 服务端先检查关联数据(分享记录、外链引用)
- 被分享的文件进入保留期,期间可随时恢复
- 定时任务每天凌晨清理过期文件
2.3 热度排行榜算法
采用热度衰减公式保证榜单新鲜度:
code复制热度值 = 访问次数*0.6 + 下载次数*0.3 + (24/(当前小时-首次分享小时+1))*0.1
数据聚合优化方案:
- 使用Redis的ZSET维护实时排名
- 每天零点持久化到MySQL
- 缓存策略:前50名缓存5分钟,50-100名缓存15分钟
3. 关键代码实现
3.1 分享状态切换端点
python复制@api_view(['POST'])
def toggle_share(request, image_id):
image = get_object_or_404(Image, pk=image_id, owner=request.user)
if request.data.get('action') == 'disable':
image.is_shared = False
log_activity(request.user, 'share_disabled', image)
else:
if not image.can_share:
raise PermissionDenied("分享功能已被管理员禁用")
image.is_shared = True
if not image.share_token:
image.share_token = generate_token()
log_activity(request.user, 'share_enabled', image)
image.save()
return Response({'status': image.is_shared})
3.2 文件删除服务
python复制def delete_image_file(image):
try:
if image.is_shared or ShareLog.objects.filter(image=image).exists():
# 进入回收站流程
Trashcan.objects.create(
image=image,
original_owner=image.owner,
delete_at=timezone.now() + timedelta(days=30)
)
image.owner = None
image.save()
return 'moved_to_trash'
else:
# 立即删除
storage.delete(image.path)
image.delete()
return 'permanent_deleted'
except Exception as e:
capture_exception(e)
raise
3.3 排行榜数据聚合
python复制def update_ranking():
hot_images = Image.objects.annotate(
popularity=Count('access_logs')*0.6 +
Count('download_logs')*0.3 +
(24/(ExtractHour(Now())-ExtractHour('created_at')+1))*0.1
).order_by('-popularity')[:100]
with redis_client.pipeline() as pipe:
pipe.delete('image_ranking')
for idx, img in enumerate(hot_images):
pipe.zadd('image_ranking', {img.id: idx+1})
pipe.execute()
4. 性能优化实践
4.1 分享链接校验优化
原始方案每次访问都查数据库:
python复制# 中间件原始版本
def check_share_token(token):
return Image.objects.filter(share_token=token, is_shared=True).exists()
优化后方案:
- 使用Bloom Filter做快速过滤
- 有效token缓存5分钟
- 异步更新布隆过滤器位图
python复制bloom = BloomFilter(capacity=1000000, error_rate=0.001)
def check_share_token(token):
if token not in bloom: # 快速排除
return False
cache_key = f'share_{token}'
if (cached := cache.get(cache_key)) is not None:
return cached
exists = Image.objects.filter(share_token=token, is_shared=True).exists()
cache.set(cache_key, exists, 300)
return exists
4.2 删除操作的批量处理
当用户批量选择删除时:
- 先分组处理(可立即删除 vs 需保留)
- 对回收站文件使用bulk_create
- 物理删除采用异步任务队列
python复制@background_task
def batch_delete_images(image_ids, user_id):
deletable = []
trashable = []
for img in Image.objects.filter(id__in=image_ids, owner_id=user_id):
if img.is_shared or ShareLog.objects.filter(image=img).exists():
trashable.append(img)
else:
deletable.append(img)
if trashable:
Trashcan.objects.bulk_create([
Trashcan(
image=img,
original_owner_id=user_id,
delete_at=timezone.now() + timedelta(days=30)
) for img in trashable
])
Image.objects.filter(id__in=[img.id for img in trashable]).update(owner=None)
if deletable:
paths = [img.path for img in deletable]
storage.delete_many(paths)
Image.objects.filter(id__in=[img.id for img in deletable]).delete()
5. 安全防护措施
5.1 分享链接的安全设计
- token生成算法:
python复制def generate_token():
return hashlib.blake2b(
os.urandom(32),
digest_size=16,
person=b'img_share'
).hexdigest()
- 访问频率限制:
- 同一IP对单个分享链接每分钟不超过60次
- 每日新生成链接上限50个/用户
5.2 删除操作的防护
- 权限校验链:
python复制def delete_permission_check(user, image): if user.is_staff: return True if image.owner != user: return False if not user.has_perm('image.delete_image'): return False return True - 操作日志记录:
- 删除时间、操作者IP
- 文件元数据快照
- 关联分享链接数
6. 前端交互优化
6.1 分享管理面板
实现功能:
- 实时切换分享状态
- 显示链接被访问次数
- 一键复制分享链接
- 设置过期时间(高级功能)
javascript复制// 分享开关组件
const ShareToggle = ({ image }) => {
const [isShared, setShared] = useState(image.isShared);
const handleToggle = async () => {
try {
const res = await api.patch(`/images/${image.id}/share`, {
action: isShared ? 'disable' : 'enable'
});
setShared(res.data.status);
} catch (err) {
showErrorToast(err.message);
}
};
return (
<Switch
checked={isShared}
onChange={handleToggle}
colorScheme="blue"
/>
);
};
6.2 回收站恢复流程
- 单独回收站视图页
- 按删除时���倒序排列
- 悬停显示原属用户和剩余天数
- 批量恢复操作
javascript复制function RestoreButton({ item }) {
const [isRestoring, setRestoring] = useState(false);
const handleRestore = async () => {
setRestoring(true);
try {
await api.post(`/trash/${item.id}/restore`);
removeFromList(item.id);
} finally {
setRestoring(false);
}
};
return (
<Button
size="sm"
onClick={handleRestore}
isLoading={isRestoring}
>
恢复文件
</Button>
);
}
7. 测试策略设计
7.1 分享功能测试矩阵
| 测试场景 | 预期结果 | 校验点 |
|---|---|---|
| 生成新分享链接 | 返回有效token | DB状态、缓存更新 |
| 禁用已分享链接 | 返回404状态 | 中间件拦截 |
| 管理员强制禁用 | 覆盖用户设置 | 权限校验 |
| 过期链接访问 | 显示提示信息 | 时间计算 |
7.2 删除功能测试用例
-
普通文件删除:
- 验证存储空间释放
- 检查数据库记录清除
- 确认关联日志生成
-
已分享文件删除:
- 检查回收站记录
- 验证原链接失效
- 测试恢复流程
-
批量删除边界条件:
- 空选择集
- 混合类型选择
- 超量选择(>100个)
7.3 排行榜压力测试
- 模拟10万张图片的访问数据
- 验证:
- 聚合查询耗时(<500ms)
- Redis内存占用(<50MB)
- 缓存一致性
8. 上线部署方案
8.1 分阶段发布计划
-
灰度阶段(10%流量):
- 先开放删除功能给VIP用户
- 监控存储空间变化
- 收集性能指标
-
全量阶段:
- 先启用分享管理
- 隔天再开放排行榜
- 错峰部署后台任务
8.2 数据库变更
需要新增的表结构:
sql复制CREATE TABLE trashcan (
id BIGSERIAL PRIMARY KEY,
image_id BIGINT NOT NULL,
original_owner_id BIGINT NOT NULL,
delete_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_trashcan_owner ON trashcan(original_owner_id);
CREATE INDEX idx_trashcan_expiry ON trashcan(delete_at);
8.3 后台任务配置
- 回收站清理任务(每日1:00执行):
bash复制
0 1 * * * /usr/bin/python3 manage.py purge_expired_trash - 排行榜更新任务(每15分钟):
bash复制
*/15 * * * * /usr/bin/python3 manage.py update_image_ranking
9. 监控与指标
9.1 关键业务指标
-
分享功能:
- 日新增分享数
- 平均每个分享的访问量
- 取消分享比例
-
删除功能:
- 日删除操作次数
- 回收站使用率
- 恢复率
-
排行榜:
- 榜单点击率
- 上榜图片的后续互动增长
9.2 性能监控项
- 分享链接校验延迟(P99 < 100ms)
- 删除操作耗时(同步部分 < 300ms)
- 排行榜计算时间(< 2秒)
9.3 告警规则配置
- 异常删除激增(>平时3倍)
- 分享链接生成失败率(>1%)
- 排行榜更新超时(>5分钟)
10. 用户引导设计
10.1 新功能提示
在相关界面添加引导标记:
javascript复制// 首次显示删除按钮时
<DeleteButton
data-tour="delete-feature"
onClick={handleDelete}
/>
引导内容配置:
json复制{
"steps": [
{
"target": "[data-tour='delete-feature']",
"content": "现在可以删除不需要的图片了,已分享的文件会进入回收站保留30天"
},
{
"target": ".share-rank-tab",
"content": "查看最受欢迎的分享内容,获得更多曝光"
}
]
}
10.2 帮助文档更新
新增章节:
- "如何管理我的分享链接"
- "文件删除与恢复指南"
- "如何让你的图片上热门榜"
包含常见问题:
- "取消分享后之前的链接会怎样?"
- "为什么有些文件不能立即删除?"
- "排行榜的更新频率是多少?"
