feat(feedback): 截图上传 + 我的反馈历史 + 后台管理页
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
后端 - 接入阿里云 OSS(ali-oss SDK),bucket=lnh2etest,目录 /dos/feedback/YYYY-MM-DD/ - POST /api/feedback/upload:单图 multipart 上传,限制 5MB,仅 png/jpeg/webp/gif - bi_user_feedback 增加 screenshots(JSON)、reply_content/reply_user/reply_at、user_id 索引 老表通过 try-catch 自动 ALTER 兼容 - POST /api/feedback/submit:增加 screenshots[] 字段 - GET /api/feedback/mine:当前用户自己的反馈历史 - PATCH /api/feedback/:id:更新状态 + 回复 - GET /api/feedback/list:增加 status 过滤 前端 - FeedbackFab 改为悬浮按钮 + 弹出菜单:「提个建议」/「我的反馈」 - 弹窗 Step 2 增加截图区:点击选择 / 多张 / 直接 Ctrl+V 粘贴 缩略图预览 + 单张移除,最多 6 张,上传中转圈 - FeedbackHistoryDrawer 新组件:底部抽屉展示自己的反馈 含状态徽章(待处理/处理中/已完成/已忽略)、截图缩略图、产品同学回复区 - 新增隐藏后台管理页 /admin/feedback(或 #/admin/feedback) 状态分类计数 + 列表 + 详情弹窗(改状态 + 写回复,状态选项含徽章色) 待处理项有快捷按钮(标记处理中 / 忽略) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
38
src/server/routes/feedback/oss.ts
Normal file
38
src/server/routes/feedback/oss.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import OSS from 'ali-oss';
|
||||
|
||||
let client: OSS | null = null;
|
||||
|
||||
function getClient(): OSS {
|
||||
if (client) return client;
|
||||
const region = process.env.OSS_REGION || 'oss-cn-shanghai';
|
||||
const accessKeyId = process.env.OSS_ACCESS_KEY_ID || '';
|
||||
const accessKeySecret = process.env.OSS_ACCESS_KEY_SECRET || '';
|
||||
const bucket = process.env.OSS_BUCKET || '';
|
||||
if (!accessKeyId || !accessKeySecret || !bucket) {
|
||||
throw new Error('OSS 未配置:OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET / OSS_BUCKET');
|
||||
}
|
||||
client = new OSS({ region, accessKeyId, accessKeySecret, bucket, secure: true });
|
||||
return client;
|
||||
}
|
||||
|
||||
function safeExt(filename: string, fallback = 'png'): string {
|
||||
const m = /\.([a-zA-Z0-9]{1,8})$/.exec(filename);
|
||||
return m ? m[1].toLowerCase() : fallback;
|
||||
}
|
||||
|
||||
function randId(len = 8): string {
|
||||
return Math.random().toString(36).slice(2, 2 + len);
|
||||
}
|
||||
|
||||
/** 上传 buffer 到 OSS,返回公开访问的 URL */
|
||||
export async function uploadFeedbackImage(filename: string, buf: Buffer, mimetype: string): Promise<string> {
|
||||
const c = getClient();
|
||||
const baseDir = (process.env.OSS_BASE_DIR || '/dos').replace(/^\/+|\/+$/g, '');
|
||||
const ymd = new Date().toISOString().slice(0, 10);
|
||||
const key = `${baseDir}/feedback/${ymd}/${Date.now().toString(36)}-${randId()}.${safeExt(filename, mimetype.split('/')[1] || 'png')}`;
|
||||
await c.put(key, buf, {
|
||||
headers: { 'Content-Type': mimetype, 'x-oss-object-acl': 'public-read' },
|
||||
});
|
||||
const host = (process.env.OSS_HOST || `https://${process.env.OSS_BUCKET}.${process.env.OSS_ENDPOINT}/`).replace(/\/+$/, '/');
|
||||
return host + key;
|
||||
}
|
||||
Reference in New Issue
Block a user