revert: restore v1.1.14 (37a9f303)
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
shishengliang
2026-09-16 09:29:30 +08:00
parent c49a02e3d9
commit 6ea1b3d5dd
210 changed files with 4448 additions and 36179 deletions
-104
View File
@@ -1,104 +0,0 @@
import type { ResultSetHeader, RowDataPacket } from 'mysql2';
/**
* 反馈表的全部 SQL 出口。
*
* SQL 与参数顺序在此固定:路由只做校验与组装,不再拼 SQL。
* 接口由 mock pool 的契约测试锁定(见 routes.test.ts)。
*/
/** 只需要 query 能力的最小依赖,便于在测试中注入 mock。 */
export interface Database {
query<T = any>(sql: string, values?: unknown[]): Promise<[T, ...any[]]>;
}
// 写入时间戳一律用东八区 CST,避免依赖 MySQL/容器时区设置。
const CST_NOW = `DATE_ADD(UTC_TIMESTAMP(), INTERVAL 8 HOUR)`;
// 两个列表接口的列集合刻意不同:管理列表额外返回 user_id / user_name。
const USER_LIST_COLUMNS = `id, type, module, content, contact, screenshots, status,
reply_content, reply_user, reply_at, created_at`;
const ADMIN_LIST_COLUMNS = `id, type, module, content, contact, screenshots, user_id, user_name, status,
reply_content, reply_user, reply_at, created_at`;
export interface NewFeedback {
type: string;
module: string | null;
content: string;
contact: string | null;
screenshotsJson: string;
userId: string | null;
userName: string | null;
userAgent: string | null;
}
export async function insertFeedback(db: Database, row: NewFeedback): Promise<number> {
const [result] = await db.query<ResultSetHeader>(
`INSERT INTO bi_user_feedback (type, module, content, contact, screenshots, user_id, user_name, user_agent, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ${CST_NOW})`,
[row.type, row.module, row.content, row.contact, row.screenshotsJson, row.userId, row.userName, row.userAgent],
);
return result.insertId;
}
/** 当前用户的反馈历史(最多 100 条)。 */
export async function listFeedbackForUser(db: Database, userId: string): Promise<RowDataPacket[]> {
const [rows] = await db.query<RowDataPacket[]>(
`SELECT ${USER_LIST_COLUMNS}
FROM bi_user_feedback
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT 100`,
[userId],
);
return rows;
}
/** 管理列表:可选状态过滤 + 条数上限。 */
export async function listFeedbackForAdmin(
db: Database,
options: { status: string | null; limit: number },
): Promise<RowDataPacket[]> {
const where: string[] = ['1=1'];
const params: (string | number)[] = [];
if (options.status) {
where.push('status = ?');
params.push(options.status);
}
const [rows] = await db.query<RowDataPacket[]>(
`SELECT ${ADMIN_LIST_COLUMNS}
FROM bi_user_feedback
WHERE ${where.join(' AND ')}
ORDER BY created_at DESC
LIMIT ?`,
[...params, options.limit],
);
return rows;
}
export interface FeedbackUpdate {
status?: string;
/** 传 undefined 表示不更新回复;传 null/'' 表示清空。 */
reply?: string | null;
replyUser?: string | null;
}
/** 管理更新。返回 false 表示没有任何可更新字段。 */
export async function updateFeedback(db: Database, id: number, update: FeedbackUpdate): Promise<boolean> {
const fields: string[] = [];
const params: (string | number | null)[] = [];
if (update.status !== undefined) {
fields.push('status = ?');
params.push(update.status);
}
if (update.reply !== undefined) {
fields.push('reply_content = ?', 'reply_user = ?', `reply_at = ${CST_NOW}`);
params.push(update.reply || null, update.replyUser ?? null);
}
if (fields.length === 0) return false;
await db.query(
`UPDATE bi_user_feedback SET ${fields.join(', ')} WHERE id = ?`,
[...params, id],
);
return true;
}