feat: 分离文章slug与URL字段并支持slug模式匹配查询
- 新增 `post_url` 字段存储文章完整URL,`post_slug` 仅存储标识符 - 更新管理后台评论列表、编辑、统计及域名获取接口以使用新字段 - 修改公共评论查询接口,支持slug精确匹配及带`?`、`#`参数的模糊匹配 - 更新前端管理界面,在评论编辑表单中增加"评论地址"(`postUrl`)字段 - 同步更新API文档,明确字段用途及查询参数说明
This commit is contained in:
@@ -25,15 +25,15 @@ export const getDomains = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
const domains = new Set<string>();
|
||||
|
||||
const { results: commentRows } = await c.env.CWD_DB.prepare(
|
||||
'SELECT post_slug, url FROM Comment'
|
||||
'SELECT post_slug, post_url FROM Comment'
|
||||
).all<{
|
||||
post_slug: string;
|
||||
url: string | null;
|
||||
post_url: string | null;
|
||||
}>();
|
||||
|
||||
for (const row of commentRows) {
|
||||
const domain =
|
||||
extractDomain(row.post_slug) || extractDomain(row.url);
|
||||
extractDomain(row.post_url) || extractDomain(row.post_slug);
|
||||
if (domain) {
|
||||
domains.add(domain);
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ export const getStats = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
const domainFilter = rawDomain.trim().toLowerCase();
|
||||
|
||||
const { results } = await c.env.CWD_DB.prepare(
|
||||
'SELECT created, post_slug, url, status FROM Comment'
|
||||
'SELECT created, post_slug, post_url, status FROM Comment'
|
||||
).all<{
|
||||
created: number;
|
||||
post_slug: string;
|
||||
url: string | null;
|
||||
post_url: string | null;
|
||||
status: string;
|
||||
}>();
|
||||
|
||||
@@ -69,7 +69,7 @@ export const getStats = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
|
||||
for (const row of results) {
|
||||
const domain =
|
||||
extractDomain(row.post_slug) || extractDomain(row.url) || 'unknown';
|
||||
extractDomain(row.post_url) || extractDomain(row.post_slug) || 'unknown';
|
||||
|
||||
let counts = domainMap.get(domain);
|
||||
if (!counts) {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const listComments = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
const params: (string | number)[] = [];
|
||||
if (domain) {
|
||||
const pattern = `%://${domain}/%`;
|
||||
whereSql = 'WHERE post_slug LIKE ? OR url LIKE ?';
|
||||
whereSql = 'WHERE post_slug LIKE ? OR post_url LIKE ?';
|
||||
params.push(pattern, pattern);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,16 @@ export const updateComment = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
}
|
||||
|
||||
const existing = await c.env.CWD_DB.prepare(
|
||||
'SELECT id, status, post_slug, priority FROM Comment WHERE id = ?'
|
||||
'SELECT id, status, post_slug, post_url, priority FROM Comment WHERE id = ?'
|
||||
)
|
||||
.bind(id)
|
||||
.first<{ id: number; status: string; post_slug: string; priority: number | null }>();
|
||||
.first<{
|
||||
id: number;
|
||||
status: string;
|
||||
post_slug: string;
|
||||
post_url: string | null;
|
||||
priority: number | null;
|
||||
}>();
|
||||
|
||||
if (!existing) {
|
||||
return c.json({ message: 'Comment not found' }, 404);
|
||||
@@ -48,6 +54,15 @@ export const updateComment = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
: typeof body.post_slug === 'string'
|
||||
? body.post_slug
|
||||
: '';
|
||||
const hasPostUrlField =
|
||||
Object.prototype.hasOwnProperty.call(body, 'postUrl') ||
|
||||
Object.prototype.hasOwnProperty.call(body, 'post_url');
|
||||
const rawPostUrl =
|
||||
typeof body.postUrl === 'string'
|
||||
? body.postUrl
|
||||
: typeof body.post_url === 'string'
|
||||
? body.post_url
|
||||
: '';
|
||||
|
||||
const contentSource =
|
||||
typeof body.content === 'string'
|
||||
@@ -63,6 +78,9 @@ export const updateComment = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
const postSlug = hasPostSlugField
|
||||
? (rawPostSlug.trim() || existing.post_slug)
|
||||
: existing.post_slug;
|
||||
const postUrl = hasPostUrlField
|
||||
? (rawPostUrl.trim() || null)
|
||||
: existing.post_url;
|
||||
|
||||
let priority: number = typeof existing.priority === 'number' && Number.isFinite(existing.priority)
|
||||
? existing.priority
|
||||
@@ -107,9 +125,9 @@ export const updateComment = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
});
|
||||
|
||||
const { success } = await c.env.CWD_DB.prepare(
|
||||
'UPDATE Comment SET name = ?, email = ?, url = ?, content_text = ?, content_html = ?, status = ?, post_slug = ?, priority = ? WHERE id = ?'
|
||||
'UPDATE Comment SET name = ?, email = ?, url = ?, content_text = ?, content_html = ?, status = ?, post_slug = ?, post_url = ?, priority = ? WHERE id = ?'
|
||||
)
|
||||
.bind(name, email, url, contentText, contentHtml, status, postSlug, priority, id)
|
||||
.bind(name, email, url, contentText, contentHtml, status, postSlug, postUrl, priority, id)
|
||||
.run();
|
||||
|
||||
if (!success) {
|
||||
|
||||
@@ -33,28 +33,39 @@ export const getComments = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
}
|
||||
|
||||
try {
|
||||
let query = `
|
||||
SELECT id, name, email, url, content_text as contentText,
|
||||
const equalSlugs = Array.from(new Set(slugList))
|
||||
const likePatternsSet = new Set<string>()
|
||||
for (const s of equalSlugs) {
|
||||
likePatternsSet.add(`${s}#%`)
|
||||
likePatternsSet.add(`${s}?%`)
|
||||
}
|
||||
const likePatterns = Array.from(likePatternsSet)
|
||||
const whereParts: string[] = []
|
||||
if (equalSlugs.length === 1) {
|
||||
whereParts.push('post_slug = ?')
|
||||
} else if (equalSlugs.length > 1) {
|
||||
const placeholders = equalSlugs.map(() => '?').join(', ')
|
||||
whereParts.push(`post_slug IN (${placeholders})`)
|
||||
}
|
||||
for (let i = 0; i < likePatterns.length; i += 1) {
|
||||
whereParts.push('post_slug LIKE ?')
|
||||
}
|
||||
const whereClause =
|
||||
whereParts.length > 0
|
||||
? `status = "approved" AND (${whereParts.join(' OR ')})`
|
||||
: 'status = "approved"'
|
||||
const query = `
|
||||
SELECT id, name, email, url, content_text as contentText,
|
||||
content_html as contentHtml, created, parent_id as parentId,
|
||||
post_slug as postSlug, post_url as postUrl, priority, COALESCE(likes, 0) as likes
|
||||
FROM Comment
|
||||
WHERE status = "approved" AND post_slug = ?
|
||||
FROM Comment
|
||||
WHERE ${whereClause}
|
||||
ORDER BY priority DESC, created DESC
|
||||
`
|
||||
if (slugList.length > 1) {
|
||||
const placeholders = slugList.map(() => '?').join(', ')
|
||||
query = `
|
||||
SELECT id, name, email, url, content_text as contentText,
|
||||
content_html as contentHtml, created, parent_id as parentId,
|
||||
post_slug as postSlug, post_url as postUrl, priority, COALESCE(likes, 0) as likes
|
||||
FROM Comment
|
||||
WHERE status = "approved" AND post_slug IN (${placeholders})
|
||||
ORDER BY priority DESC, created DESC
|
||||
`
|
||||
}
|
||||
const bindParams: unknown[] = [...equalSlugs, ...likePatterns]
|
||||
|
||||
const [commentsResult, adminEmailRows] = await Promise.all([
|
||||
c.env.CWD_DB.prepare(query).bind(...slugList).all(),
|
||||
c.env.CWD_DB.prepare(query).bind(...bindParams).all(),
|
||||
c.env.CWD_DB.prepare('SELECT key, value FROM Settings WHERE key IN (?, ?)')
|
||||
.bind('comment_admin_email', 'admin_notify_email')
|
||||
.all<{ key: string; value: string }>()
|
||||
|
||||
Reference in New Issue
Block a user