修复routes文件下sql语句执行前入参变量类型没有字符串化导致的执行异常

This commit is contained in:
ZTMYO
2025-09-05 15:51:29 +08:00
parent 4b064be4ba
commit 8e8ebbe8f8
25 changed files with 396 additions and 2325 deletions

View File

@@ -1,7 +1,4 @@
#!/usr/bin/env pwsh
# 小石榴图文社区 Docker 一键部署脚本
# PowerShell 版本
#!/usr/bin/env pwsh
param(
[switch]$Build,
[switch]$Stop,
@@ -11,7 +8,6 @@ param(
[switch]$Help
)
# 颜色输出函数
function Write-ColorOutput {
param(
[string]$Message,
@@ -20,11 +16,10 @@ function Write-ColorOutput {
Write-Host $Message -ForegroundColor $Color
}
# 显示帮助信息
function Show-Help {
Write-ColorOutput "小石榴图文社区 Docker 部署脚本" "Cyan"
Write-ColorOutput "用法: .\deploy.ps1 [选项]" "Yellow"
Write-ColorOutput ""
Write-ColorOutput ""
Write-ColorOutput "选项:" "Green"
Write-ColorOutput " -Build 强制重新构建镜像并启动服务" "White"
Write-ColorOutput " -Stop 停止所有服务" "White"
@@ -32,14 +27,8 @@ function Show-Help {
Write-ColorOutput " -Logs 查看服务日志" "White"
Write-ColorOutput " -Status 查看服务状态" "White"
Write-ColorOutput " -Help 显示此帮助信息" "White"
Write-ColorOutput ""
Write-ColorOutput "示例:" "Green"
Write-ColorOutput " .\deploy.ps1 # 启动服务" "White"
Write-ColorOutput " .\deploy.ps1 -Build # 重新构建并启动" "White"
Write-ColorOutput " .\deploy.ps1 -Stop # 停止服务" "White"
}
# 检查Docker是否安装
function Test-Docker {
try {
docker --version | Out-Null
@@ -48,94 +37,20 @@ function Test-Docker {
}
catch {
Write-ColorOutput "错误: 未找到 Docker 或 Docker Compose" "Red"
Write-ColorOutput "请先安装 Docker Desktop: https://www.docker.com/products/docker-desktop" "Yellow"
return $false
}
}
# 检查环境变量文件
function Test-EnvFile {
if (-not (Test-Path ".env")) {
if (Test-Path ".env.docker") {
Write-ColorOutput "复制 .env.docker 到 .env" "Yellow"
Copy-Item ".env.docker" ".env"
} else {
Write-ColorOutput "警告: 未找到 .env 文件,将使用默认配置" "Yellow"
}
}
}
# 启动服务
function Start-Services {
Write-ColorOutput "启动小石榴图文社区服务..." "Green"
if ($Build) {
Write-ColorOutput "重新构建镜像..." "Yellow"
docker-compose down
docker-compose build --no-cache
}
docker-compose up -d
docker-compose -p xiaoshiliu up -d
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "服务启动成功!" "Green"
Write-ColorOutput ""
Write-ColorOutput "访问地址:" "Cyan"
Write-ColorOutput " 前端: http://localhost" "White"
Write-ColorOutput " 后端API: http://localhost:3001" "White"
Write-ColorOutput " 数据库: localhost:3306" "White"
Write-ColorOutput ""
Write-ColorOutput "使用 '.\deploy.ps1 -Logs' 查看日志" "Yellow"
Write-ColorOutput "使用 '.\deploy.ps1 -Status' 查看服务状态" "Yellow"
} else {
Write-ColorOutput "服务启动失败!" "Red"
Write-ColorOutput "请检查日志: docker-compose logs" "Yellow"
}
}
# 停止服务
function Stop-Services {
Write-ColorOutput "停止小石榴图文社区服务..." "Yellow"
docker-compose down
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "服务已停止" "Green"
} else {
Write-ColorOutput "停止服务时出现错误" "Red"
}
}
# 清理资源
function Clean-Resources {
Write-ColorOutput "警告: 此操作将删除所有容器、镜像和数据!" "Red"
$confirm = Read-Host "确认继续? (y/N)"
if ($confirm -eq "y" -or $confirm -eq "Y") {
Write-ColorOutput "清理Docker资源..." "Yellow"
docker-compose down -v --rmi all
docker system prune -f
Write-ColorOutput "清理完成" "Green"
} else {
Write-ColorOutput "操作已取消" "Yellow"
}
}
# 查看日志
function Show-Logs {
Write-ColorOutput "查看服务日志 (按 Ctrl+C 退出):" "Cyan"
docker-compose logs -f
}
# 查看状态
function Show-Status {
Write-ColorOutput "服务状态:" "Cyan"
docker-compose ps
Write-ColorOutput ""
Write-ColorOutput "资源使用情况:" "Cyan"
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
}
# 主逻辑
if ($Help) {
Show-Help
exit 0
@@ -145,14 +60,9 @@ if (-not (Test-Docker)) {
exit 1
}
Test-EnvFile
switch ($true) {
$Stop { Stop-Services }
$Clean { Clean-Resources }
$Logs { Show-Logs }
$Status { Show-Status }
$Stop { Write-ColorOutput "停止服务..." "Yellow"; docker-compose -p xiaoshiliu down }
default { Start-Services }
}
Write-ColorOutput "操作完成" "Green"
Write-ColorOutput "操作完成" "Green"

View File

@@ -2,7 +2,7 @@
## 项目信息
- **项目名称**: 小石榴图文社区
- **版本**: v1.0.0
- **版本**: v1.0.1
- **基础URL**: `http://localhost:3001`
- **数据库**: xiaoshiliu (MySQL)
- **更新时间**: 2025-09-03

View File

@@ -1,8 +1,9 @@
version: '3.8'
name: xiaoshiliu
services:
# MySQL数据库服务
mysql:
# 使用官方MySQL镜像
image: mysql:8.0
container_name: xiaoshiliu-mysql
restart: unless-stopped
@@ -54,7 +55,7 @@ services:
mysql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/api/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"]
interval: 30s
timeout: 10s
retries: 3

View File

@@ -8,7 +8,7 @@ WORKDIR /app
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
RUN npm install --only=production
# 复制应用源代码
COPY . .
@@ -25,8 +25,8 @@ USER nodejs
EXPOSE 3001
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3001/api/health || exit 1
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3001/api/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"
# 启动应用
CMD ["npm", "start"]

View File

@@ -45,6 +45,16 @@ app.options('*', cors(corsOptions)); // 显式处理OPTIONS请求
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ extended: true, limit: '50mb' }));
// 健康检查路由
app.get('/api/health', (req, res) => {
res.status(200).json({
code: 200,
message: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
// 路由配置
app.use('/api/auth', authRoutes);
app.use('/api/users', usersRoutes);

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@ const postsCrudConfig = {
const { user_id, images, image_urls, tags } = data
// 检查用户是否存在
const [userResult] = await pool.execute('SELECT id FROM users WHERE id = ?', [user_id])
const [userResult] = await pool.execute('SELECT id FROM users WHERE id = ?', [String(user_id)])
if (userResult.length === 0) {
throw new Error('用户不存在')
}
@@ -118,7 +118,7 @@ const postsCrudConfig = {
if (cleanUrl && !cleanUrl.startsWith('data:image/')) {
await pool.execute(
'INSERT INTO post_images (post_id, image_url) VALUES (?, ?)',
[postId, cleanUrl]
[String(postId), cleanUrl]
)
}
}
@@ -141,14 +141,14 @@ const postsCrudConfig = {
)
if (existingTag.length > 0) {
tagId = existingTag[0].id
tagId = String(existingTag[0].id)
} else {
// 创建新标签
const [tagResult] = await pool.execute(
'INSERT INTO tags (name) VALUES (?)',
[tagName]
)
tagId = tagResult.insertId
tagId = String(tagResult.insertId)
}
} else {
// 处理对象格式的标签(向后兼容)
@@ -163,13 +163,13 @@ const postsCrudConfig = {
)
if (existingTag.length > 0) {
tagId = existingTag[0].id
tagId = String(existingTag[0].id)
} else {
const [tagResult] = await pool.execute(
'INSERT INTO tags (name) VALUES (?)',
[tag.name]
)
tagId = tagResult.insertId
tagId = String(tagResult.insertId)
}
}
}
@@ -177,13 +177,13 @@ const postsCrudConfig = {
// 关联笔记和标签
await pool.execute(
'INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)',
[postId, tagId]
[String(postId), String(tagId)]
)
// 更新标签使用次数
await pool.execute(
'UPDATE tags SET use_count = use_count + 1 WHERE id = ?',
[tagId]
[String(tagId)]
)
}
}
@@ -211,7 +211,7 @@ const postsCrudConfig = {
// 更新图片信息
if (images !== undefined || image_urls !== undefined) {
// 删除原有图片
await pool.execute('DELETE FROM post_images WHERE post_id = ?', [postId])
await pool.execute('DELETE FROM post_images WHERE post_id = ?', [String(postId)])
// 使用Set来避免重复的图片URL
const allImagesSet = new Set()
@@ -353,13 +353,13 @@ const postsCrudConfig = {
// 关联笔记和标签
await pool.execute(
'INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)',
[postId, tagId]
[String(postId), String(tagId)]
)
// 更新标签使用次数
await pool.execute(
'UPDATE tags SET use_count = use_count + 1 WHERE id = ?',
[tagId]
[String(tagId)]
)
}
}
@@ -371,12 +371,12 @@ const postsCrudConfig = {
// 获取笔记关联的标签,减少标签使用次数
const [tagResult] = await pool.execute(
'SELECT tag_id FROM post_tags WHERE post_id = ?',
[id]
[String(id)]
)
// 减少标签使用次数
for (const tag of tagResult) {
await pool.execute('UPDATE tags SET use_count = use_count - 1 WHERE id = ?', [tag.tag_id])
await pool.execute('UPDATE tags SET use_count = use_count - 1 WHERE id = ?', [String(tag.tag_id)])
}
},
@@ -387,12 +387,12 @@ const postsCrudConfig = {
// 获取所有笔记关联的标签,减少标签使用次数
const [tagResult] = await pool.execute(
`SELECT tag_id FROM post_tags WHERE post_id IN (${placeholders})`,
ids
ids.map(id => String(id))
)
// 减少标签使用次数
for (const tag of tagResult) {
await pool.execute('UPDATE tags SET use_count = use_count - 1 WHERE id = ?', [tag.tag_id])
await pool.execute('UPDATE tags SET use_count = use_count - 1 WHERE id = ?', [String(tag.tag_id)])
}
},
@@ -410,7 +410,7 @@ const postsCrudConfig = {
FROM posts p
LEFT JOIN users u ON p.user_id = u.id
WHERE p.id = ?
`, [postId])
`, [String(postId)])
if (postResult.length === 0) {
return null
@@ -419,7 +419,7 @@ const postsCrudConfig = {
const post = postResult[0]
// 获取笔记图片
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [postId])
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [String(postId)])
post.images = images.map(img => img.image_url)
// 获取笔记标签
@@ -428,7 +428,7 @@ const postsCrudConfig = {
FROM tags t
INNER JOIN post_tags pt ON t.id = pt.tag_id
WHERE pt.post_id = ?
`, [postId])
`, [String(postId)])
post.tags = tags
return post
@@ -498,11 +498,11 @@ const postsCrudConfig = {
${orderClause}
LIMIT ? OFFSET ?
`
const [posts] = await pool.execute(dataQuery, [...params, limit, offset])
const [posts] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
// 为每个笔记获取图片信息和标签信息
for (let post of posts) {
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id])
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [String(post.id)])
post.images = images.map(img => img.image_url)
// 获取笔记标签
@@ -511,7 +511,7 @@ const postsCrudConfig = {
FROM tags t
INNER JOIN post_tags pt ON t.id = pt.tag_id
WHERE pt.post_id = ?
`, [post.id])
`, [String(post.id)])
post.tags = tags
}
@@ -599,20 +599,20 @@ const commentsCrudConfig = {
const { user_id, post_id, parent_id } = data
// 检查用户是否存在
const [userResult] = await pool.execute('SELECT id FROM users WHERE id = ?', [user_id])
const [userResult] = await pool.execute('SELECT id FROM users WHERE id = ?', [String(user_id)])
if (userResult.length === 0) {
return { isValid: false, message: '用户不存在' }
}
// 检查笔记是否存在
const [postResult] = await pool.execute('SELECT id FROM posts WHERE id = ?', [post_id])
const [postResult] = await pool.execute('SELECT id FROM posts WHERE id = ?', [String(post_id)])
if (postResult.length === 0) {
return { isValid: false, message: '笔记不存在' }
}
// 如果是回复评论,检查父评论是否存在
if (parent_id) {
const [parentResult] = await pool.execute('SELECT id FROM comments WHERE id = ?', [parent_id])
const [parentResult] = await pool.execute('SELECT id FROM comments WHERE id = ?', [String(parent_id)])
if (parentResult.length === 0) {
return { isValid: false, message: '父评论不存在' }
}
@@ -684,7 +684,7 @@ const commentsCrudConfig = {
${orderClause}
LIMIT ? OFFSET ?
`
const [comments] = await pool.execute(dataQuery, [...params, limit, offset])
const [comments] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
return {
data: comments,
@@ -849,7 +849,7 @@ const likesCrudConfig = {
${orderClause}
LIMIT ? OFFSET ?
`
const [likes] = await pool.execute(dataQuery, [...params, limit, offset])
const [likes] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
return {
data: likes,
@@ -945,7 +945,7 @@ const collectionsCrudConfig = {
const { pool } = require('../config/database')
const [existing] = await pool.execute(
'SELECT id FROM collections WHERE user_id = ? AND post_id = ?',
[data.user_id, data.post_id]
[String(data.user_id), String(data.post_id)]
)
if (existing.length > 0) {
return {
@@ -1023,7 +1023,7 @@ const collectionsCrudConfig = {
${orderClause}
LIMIT ? OFFSET ?
`
const [collections] = await pool.execute(dataQuery, [...params, limit, offset])
const [collections] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
return {
data: collections,
@@ -1089,7 +1089,7 @@ const followsCrudConfig = {
const { pool } = require('../config/database')
const [existing] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[data.follower_id, data.following_id]
[String(data.follower_id), String(data.following_id)]
)
if (existing.length > 0) {
return {
@@ -1106,7 +1106,7 @@ const followsCrudConfig = {
if (data.following_id) {
// 获取当前记录的关注者ID
const { pool } = require('../config/database')
const [current] = await pool.execute('SELECT follower_id FROM follows WHERE id = ?', [id])
const [current] = await pool.execute('SELECT follower_id FROM follows WHERE id = ?', [String(id)])
if (current.length === 0) {
return {
isValid: false,
@@ -1178,7 +1178,7 @@ const followsCrudConfig = {
${orderClause}
LIMIT ? OFFSET ?
`
const [follows] = await pool.execute(dataQuery, [...params, limit, offset])
const [follows] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
return {
data: follows,
@@ -1292,7 +1292,7 @@ const notificationsCrudConfig = {
${orderClause}
LIMIT ? OFFSET ?
`
const [notifications] = await pool.execute(dataQuery, [...params, limit, offset])
const [notifications] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
return {
data: notifications,
@@ -1378,7 +1378,7 @@ const sessionsCrudConfig = {
if (req.query.user_id) {
whereClause += whereClause ? ' AND s.user_id = ?' : 'WHERE s.user_id = ?'
params.push(req.query.user_id)
params.push(String(req.query.user_id))
}
if (req.query.is_active !== undefined) {
@@ -1417,7 +1417,7 @@ const sessionsCrudConfig = {
${orderClause}
LIMIT ? OFFSET ?
`
const [sessions] = await pool.execute(dataQuery, [...params, limit, offset])
const [sessions] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
return {
data: sessions,
@@ -1498,11 +1498,11 @@ const usersCrudConfig = {
// 如果没有提供密码设置默认哈希密码123456的SHA256哈希值
if (!data.password) {
// 使用MySQL的SHA2函数生成默认密码的哈希值
const [result] = await pool.execute('SELECT SHA2(?, 256) as hashed_password', ['123456'])
const [result] = await pool.execute('SELECT SHA2(?, 256) as hashed_password', [String('123456')])
data.password = result[0].hashed_password
} else {
// 如果提供了密码,进行哈希处理
const [result] = await pool.execute('SELECT SHA2(?, 256) as hashed_password', [data.password])
const [result] = await pool.execute('SELECT SHA2(?, 256) as hashed_password', [String(data.password)])
data.password = result[0].hashed_password
}
data.avatar = data.avatar || ''

View File

@@ -33,7 +33,7 @@ router.post('/register', async (req, res) => {
// 检查用户ID是否已存在
const [existingUser] = await pool.execute(
'SELECT id FROM users WHERE user_id = ?',
[user_id]
[user_id.toString()]
);
if (existingUser.length > 0) {
@@ -68,13 +68,13 @@ router.post('/register', async (req, res) => {
// 保存会话
await pool.execute(
'INSERT INTO user_sessions (user_id, token, refresh_token, expires_at, user_agent, is_active) VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 7 DAY), ?, 1)',
[userId, accessToken, refreshToken, userAgent]
[userId.toString(), accessToken, refreshToken, userAgent]
);
// 获取完整用户信息
const [userRows] = await pool.execute(
'SELECT id, user_id, nickname, avatar, bio, location, follow_count, fans_count, like_count FROM users WHERE id = ?',
[userId]
[userId.toString()]
);
console.log(`用户注册成功 - 用户ID: ${userId}, 小石榴号: ${userRows[0].user_id}`);
@@ -108,7 +108,7 @@ router.post('/login', async (req, res) => {
// 查找用户
const [userRows] = await pool.execute(
'SELECT id, user_id, nickname, password, avatar, bio, location, follow_count, fans_count, like_count, is_active, gender, zodiac_sign, mbti, education, major, interests FROM users WHERE user_id = ?',
[user_id]
[user_id.toString()]
);
if (userRows.length === 0) {
@@ -124,7 +124,7 @@ router.post('/login', async (req, res) => {
// 验证密码(哈希比较)
const [passwordCheck] = await pool.execute(
'SELECT 1 FROM users WHERE id = ? AND password = SHA2(?, 256)',
[user.id, password]
[user.id.toString(), password]
);
if (passwordCheck.length === 0) {
@@ -143,14 +143,14 @@ router.post('/login', async (req, res) => {
const ipLocation = await getIPLocation(userIP);
await pool.execute(
'UPDATE users SET location = ? WHERE id = ?',
[ipLocation, user.id]
[ipLocation, user.id.toString()]
);
// 清除旧会话并保存新会话
await pool.execute('UPDATE user_sessions SET is_active = 0 WHERE user_id = ?', [user.id]);
await pool.execute('UPDATE user_sessions SET is_active = 0 WHERE user_id = ?', [user.id.toString()]);
await pool.execute(
'INSERT INTO user_sessions (user_id, token, refresh_token, expires_at, user_agent, is_active) VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 7 DAY), ?, 1)',
[user.id, accessToken, refreshToken, userAgent]
[user.id.toString(), accessToken, refreshToken, userAgent]
);
// 更新用户对象中的location字段
@@ -205,7 +205,7 @@ router.post('/refresh', async (req, res) => {
// 检查会话是否有效
const [sessionRows] = await pool.execute(
'SELECT id FROM user_sessions WHERE user_id = ? AND refresh_token = ? AND is_active = 1 AND expires_at > NOW()',
[decoded.userId, refresh_token]
[decoded.userId.toString(), refresh_token]
);
if (sessionRows.length === 0) {
@@ -224,13 +224,13 @@ router.post('/refresh', async (req, res) => {
const ipLocation = await getIPLocation(userIP);
await pool.execute(
'UPDATE users SET location = ? WHERE id = ?',
[ipLocation, decoded.userId]
[ipLocation, decoded.userId.toString()]
);
// 更新会话
await pool.execute(
'UPDATE user_sessions SET token = ?, refresh_token = ?, expires_at = DATE_ADD(NOW(), INTERVAL 7 DAY), user_agent = ? WHERE id = ?',
[newAccessToken, newRefreshToken, userAgent, sessionRows[0].id]
[newAccessToken, newRefreshToken, userAgent, sessionRows[0].id.toString()]
);
res.json({
@@ -257,7 +257,7 @@ router.post('/logout', authenticateToken, async (req, res) => {
// 将当前会话设为无效
await pool.execute(
'UPDATE user_sessions SET is_active = 0 WHERE user_id = ? AND token = ?',
[userId, token]
[userId.toString(), token]
);
console.log(`用户退出成功 - 用户ID: ${userId}`);
@@ -279,7 +279,7 @@ router.get('/me', authenticateToken, async (req, res) => {
const [userRows] = await pool.execute(
'SELECT id, user_id, nickname, avatar, bio, location, follow_count, fans_count, like_count, is_active, created_at, gender, zodiac_sign, mbti, education, major, interests FROM users WHERE id = ?',
[userId]
[userId.toString()]
);
if (userRows.length === 0) {
@@ -334,7 +334,7 @@ router.post('/admin/login', async (req, res) => {
// 验证密码(哈希比较)
const [passwordCheck] = await pool.execute(
'SELECT 1 FROM admin WHERE id = ? AND password = SHA2(?, 256)',
[admin.id, password]
[admin.id.toString(), password]
);
if (passwordCheck.length === 0) {
@@ -386,7 +386,7 @@ router.get('/admin/me', authenticateToken, async (req, res) => {
const [adminRows] = await pool.execute(
'SELECT id, username FROM admin WHERE id = ?',
[adminId]
[adminId.toString()]
);
if (adminRows.length === 0) {
@@ -443,7 +443,7 @@ router.get('/admin/admins', authenticateToken, async (req, res) => {
ORDER BY ${sortField} ${sortOrder}
LIMIT ? OFFSET ?
`;
const [adminRows] = await pool.execute(dataQuery, [...params, limit, offset]);
const [adminRows] = await pool.execute(dataQuery, [...params, String(limit), String(offset)]);
res.json({
code: 200,
@@ -602,7 +602,7 @@ router.put('/admin/admins/:id/password', authenticateToken, async (req, res) =>
// 检查管理员是否存在
const [adminRows] = await pool.execute(
'SELECT id FROM admin WHERE id = ?',
[adminId]
[adminId.toString()]
);
if (adminRows.length === 0) {
@@ -612,7 +612,7 @@ router.put('/admin/admins/:id/password', authenticateToken, async (req, res) =>
// 更新密码使用SHA2哈希加密
await pool.execute(
'UPDATE admin SET password = SHA2(?, 256) WHERE id = ?',
[password, adminId]
[password, adminId.toString()]
);
res.json({
@@ -644,7 +644,7 @@ router.put('/admin/admins/:id/status', authenticateToken, async (req, res) => {
// 检查管理员是否存在
const [adminRows] = await pool.execute(
'SELECT id FROM admin WHERE id = ?',
[adminId]
[adminId.toString()]
);
if (adminRows.length === 0) {
@@ -659,7 +659,7 @@ router.put('/admin/admins/:id/status', authenticateToken, async (req, res) => {
// 更新状态
await pool.execute(
'UPDATE admin SET status = ? WHERE id = ?',
[status, adminId]
[String(status), adminId.toString()]
);
res.json({

View File

@@ -10,7 +10,7 @@ async function deleteCommentRecursive(commentId) {
let deletedCount = 0;
// 获取所有子评论
const [children] = await pool.execute('SELECT id FROM comments WHERE parent_id = ?', [commentId]);
const [children] = await pool.execute('SELECT id FROM comments WHERE parent_id = ?', [commentId.toString()]);
// 递归删除子评论
for (const child of children) {
@@ -18,10 +18,10 @@ async function deleteCommentRecursive(commentId) {
}
// 删除当前评论的点赞记录
await pool.execute('DELETE FROM likes WHERE target_type = 2 AND target_id = ?', [commentId]);
await pool.execute('DELETE FROM likes WHERE target_type = 2 AND target_id = ?', [commentId.toString()]);
// 删除当前评论
await pool.execute('DELETE FROM comments WHERE id = ?', [commentId]);
await pool.execute('DELETE FROM comments WHERE id = ?', [commentId.toString()]);
// 当前评论也算一个
deletedCount += 1;
@@ -50,7 +50,7 @@ router.get('/', optionalAuth, async (req, res) => {
WHERE c.post_id = ? AND c.parent_id IS NULL
ORDER BY c.created_at DESC
LIMIT ? OFFSET ?`,
[postId, limit, offset]
[postId.toString(), limit.toString(), offset.toString()]
);
// 为每个评论检查点赞状态
@@ -58,7 +58,7 @@ router.get('/', optionalAuth, async (req, res) => {
if (currentUserId) {
const [likeResult] = await pool.execute(
'SELECT id FROM likes WHERE user_id = ? AND target_type = 2 AND target_id = ?',
[currentUserId, comment.id]
[currentUserId.toString(), comment.id.toString()]
);
comment.liked = likeResult.length > 0;
} else {
@@ -68,7 +68,7 @@ router.get('/', optionalAuth, async (req, res) => {
// 获取子评论数量
const [childCount] = await pool.execute(
'SELECT COUNT(*) as count FROM comments WHERE parent_id = ?',
[comment.id]
[comment.id.toString()]
);
comment.reply_count = childCount[0].count;
}
@@ -76,7 +76,7 @@ router.get('/', optionalAuth, async (req, res) => {
// 获取总数
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM comments WHERE post_id = ? AND parent_id IS NULL',
[postId]
[postId.toString()]
);
const total = countResult[0].total;
@@ -111,14 +111,14 @@ router.post('/', authenticateToken, async (req, res) => {
}
// 验证笔记是否存在
const [postRows] = await pool.execute('SELECT id FROM posts WHERE id = ?', [post_id]);
const [postRows] = await pool.execute('SELECT id FROM posts WHERE id = ?', [post_id.toString()]);
if (postRows.length === 0) {
return res.status(404).json({ code: 404, message: '笔记不存在' });
}
// 如果是回复评论,验证父评论是否存在
if (parent_id) {
const [parentRows] = await pool.execute('SELECT id FROM comments WHERE id = ?', [parent_id]);
const [parentRows] = await pool.execute('SELECT id FROM comments WHERE id = ?', [parent_id.toString()]);
if (parentRows.length === 0) {
return res.status(404).json({ code: 404, message: '父评论不存在' });
}
@@ -127,18 +127,18 @@ router.post('/', authenticateToken, async (req, res) => {
// 插入评论
const [result] = await pool.execute(
'INSERT INTO comments (post_id, user_id, content, parent_id) VALUES (?, ?, ?, ?)',
[post_id, userId, content, parent_id || null]
[post_id.toString(), userId.toString(), content, parent_id ? parent_id.toString() : null]
);
const commentId = result.insertId;
// 更新笔记评论数
await pool.execute('UPDATE posts SET comment_count = comment_count + 1 WHERE id = ?', [post_id]);
await pool.execute('UPDATE posts SET comment_count = comment_count + 1 WHERE id = ?', [post_id.toString()]);
// 创建通知
if (parent_id) {
// 回复评论,给被回复的评论作者发通知
const [parentCommentResult] = await pool.execute('SELECT user_id FROM comments WHERE id = ?', [parent_id]);
const [parentCommentResult] = await pool.execute('SELECT user_id FROM comments WHERE id = ?', [parent_id.toString()]);
if (parentCommentResult.length > 0) {
const parentUserId = parentCommentResult[0].user_id;
// 不给自己发通知
@@ -149,7 +149,7 @@ router.post('/', authenticateToken, async (req, res) => {
}
} else {
// 评论笔记,给笔记作者发通知
const [postResult] = await pool.execute('SELECT user_id FROM posts WHERE id = ?', [post_id]);
const [postResult] = await pool.execute('SELECT user_id FROM posts WHERE id = ?', [post_id.toString()]);
if (postResult.length > 0) {
const postUserId = postResult[0].user_id;
// 不给自己发通知
@@ -223,7 +223,7 @@ router.get('/:id/replies', optionalAuth, async (req, res) => {
WHERE c.parent_id = ?
ORDER BY c.created_at ASC
LIMIT ? OFFSET ?`,
[parentId, limit, offset]
[parentId.toString(), limit.toString(), offset.toString()]
);
// 为每个评论检查点赞状态
@@ -231,7 +231,7 @@ router.get('/:id/replies', optionalAuth, async (req, res) => {
if (currentUserId) {
const [likeResult] = await pool.execute(
'SELECT id FROM likes WHERE user_id = ? AND target_type = 2 AND target_id = ?',
[currentUserId, comment.id]
[currentUserId.toString(), comment.id.toString()]
);
comment.liked = likeResult.length > 0;
} else {
@@ -242,7 +242,7 @@ router.get('/:id/replies', optionalAuth, async (req, res) => {
// 获取总数
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM comments WHERE parent_id = ?',
[parentId]
[parentId.toString()]
);
const total = countResult[0].total;
@@ -277,7 +277,7 @@ router.delete('/:id', authenticateToken, async (req, res) => {
// 验证评论是否存在并且是当前用户发布的
const [commentRows] = await pool.execute(
'SELECT id, post_id, user_id, parent_id FROM comments WHERE id = ?',
[commentId]
[commentId.toString()]
);
if (commentRows.length === 0) {
@@ -295,7 +295,7 @@ router.delete('/:id', authenticateToken, async (req, res) => {
const deletedCount = await deleteCommentRecursive(commentId);
// 根据实际删除的评论数量更新笔记的评论计数
await pool.execute('UPDATE posts SET comment_count = comment_count - ? WHERE id = ?', [deletedCount, comment.post_id]);
await pool.execute('UPDATE posts SET comment_count = comment_count - ? WHERE id = ?', [deletedCount.toString(), comment.post_id.toString()]);
console.log(`删除评论成功 - 用户ID: ${userId}, 评论ID: ${commentId}`);

View File

@@ -23,29 +23,29 @@ router.post('/', authenticateToken, async (req, res) => {
// 检查是否已经点赞
const [existingLike] = await pool.execute(
'SELECT id FROM likes WHERE user_id = ? AND target_type = ? AND target_id = ?',
[userId, target_type, target_id]
[String(userId), String(target_type), String(target_id)]
);
if (existingLike.length > 0) {
// 已点赞,执行取消点赞
await pool.execute(
'DELETE FROM likes WHERE user_id = ? AND target_type = ? AND target_id = ?',
[userId, target_type, target_id]
[String(userId), String(target_type), String(target_id)]
);
// 更新对应表的点赞数
if (target_type == 1) {
// 笔记
await pool.execute('UPDATE posts SET like_count = like_count - 1 WHERE id = ?', [target_id]);
await pool.execute('UPDATE posts SET like_count = like_count - 1 WHERE id = ?', [String(target_id)]);
// 更新笔记作者的获赞数
await pool.execute(
'UPDATE users SET like_count = like_count - 1 WHERE id = (SELECT user_id FROM posts WHERE id = ?)',
[target_id]
[String(target_id)]
);
} else if (target_type == 2) {
// 评论
await pool.execute('UPDATE comments SET like_count = like_count - 1 WHERE id = ?', [target_id]);
await pool.execute('UPDATE comments SET like_count = like_count - 1 WHERE id = ?', [String(target_id)]);
}
console.log(`取消点赞成功 - 用户ID: ${userId}`);
@@ -54,7 +54,7 @@ router.post('/', authenticateToken, async (req, res) => {
// 未点赞,执行点赞
await pool.execute(
'INSERT INTO likes (user_id, target_type, target_id) VALUES (?, ?, ?)',
[userId, target_type, target_id]
[String(userId), String(target_type), String(target_id)]
);
// 更新对应表的点赞数
@@ -63,16 +63,16 @@ router.post('/', authenticateToken, async (req, res) => {
if (target_type == 1) {
// 笔记
await pool.execute('UPDATE posts SET like_count = like_count + 1 WHERE id = ?', [target_id]);
await pool.execute('UPDATE posts SET like_count = like_count + 1 WHERE id = ?', [String(target_id)]);
// 更新笔记作者的获赞数
await pool.execute(
'UPDATE users SET like_count = like_count + 1 WHERE id = (SELECT user_id FROM posts WHERE id = ?)',
[target_id]
[String(target_id)]
);
// 获取笔记作者ID用于创建通知
const [postResult] = await pool.execute('SELECT user_id FROM posts WHERE id = ?', [target_id]);
const [postResult] = await pool.execute('SELECT user_id FROM posts WHERE id = ?', [String(target_id)]);
if (postResult.length > 0) {
targetUserId = postResult[0].user_id;
}
@@ -80,10 +80,10 @@ router.post('/', authenticateToken, async (req, res) => {
notificationTargetId = target_id;
} else if (target_type == 2) {
// 评论
await pool.execute('UPDATE comments SET like_count = like_count + 1 WHERE id = ?', [target_id]);
await pool.execute('UPDATE comments SET like_count = like_count + 1 WHERE id = ?', [String(target_id)]);
// 获取评论作者ID和所属笔记ID用于创建通知
const [commentResult] = await pool.execute('SELECT user_id, post_id FROM comments WHERE id = ?', [target_id]);
const [commentResult] = await pool.execute('SELECT user_id, post_id FROM comments WHERE id = ?', [String(target_id)]);
if (commentResult.length > 0) {
targetUserId = commentResult[0].user_id;
// 点赞评论时通知的target_id应该是评论所属的笔记ID这样点击通知可以跳转到笔记页面
@@ -131,7 +131,7 @@ router.delete('/', authenticateToken, async (req, res) => {
// 删除点赞记录
const [result] = await pool.execute(
'DELETE FROM likes WHERE user_id = ? AND target_type = ? AND target_id = ?',
[userId, target_type, target_id]
[String(userId), String(target_type), String(target_id)]
);
if (result.affectedRows === 0) {
@@ -141,16 +141,16 @@ router.delete('/', authenticateToken, async (req, res) => {
// 更新对应表的点赞数
if (target_type == 1) {
// 笔记
await pool.execute('UPDATE posts SET like_count = like_count - 1 WHERE id = ?', [target_id]);
await pool.execute('UPDATE posts SET like_count = like_count - 1 WHERE id = ?', [String(target_id)]);
// 更新笔记作者的获赞数
await pool.execute(
'UPDATE users SET like_count = like_count - 1 WHERE id = (SELECT user_id FROM posts WHERE id = ?)',
[target_id]
[String(target_id)]
);
} else if (target_type == 2) {
// 评论
await pool.execute('UPDATE comments SET like_count = like_count - 1 WHERE id = ?', [target_id]);
await pool.execute('UPDATE comments SET like_count = like_count - 1 WHERE id = ?', [String(target_id)]);
}
console.log(`取消点赞成功 - 用户ID: ${userId}`);

View File

@@ -41,12 +41,12 @@ router.get('/comments', authenticateToken, async (req, res) => {
ORDER BY n.created_at DESC LIMIT ? OFFSET ?
`;
const [rows] = await pool.execute(query, [userId, userId, limit, offset]);
const [rows] = await pool.execute(query, [userId.toString(), userId.toString(), limit.toString(), offset.toString()]);
// 获取总数
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM notifications WHERE user_id = ? AND type IN (4, 5, 7)',
[userId]
[userId.toString()]
);
const total = countResult[0].total;
@@ -92,12 +92,12 @@ router.get('/likes', authenticateToken, async (req, res) => {
ORDER BY n.created_at DESC LIMIT ? OFFSET ?
`;
const [rows] = await pool.execute(query, [userId, limit, offset]);
const [rows] = await pool.execute(query, [userId.toString(), limit.toString(), offset.toString()]);
// 获取总数
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM notifications WHERE user_id = ? AND type IN (1, 2)',
[userId]
[userId.toString()]
);
const total = countResult[0].total;
@@ -140,12 +140,12 @@ router.get('/follows', authenticateToken, async (req, res) => {
ORDER BY n.created_at DESC LIMIT ? OFFSET ?
`;
const [rows] = await pool.execute(query, [userId, limit, offset]);
const [rows] = await pool.execute(query, [userId.toString(), limit.toString(), offset.toString()]);
// 获取总数
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM notifications WHERE user_id = ? AND type = ?',
[userId, 6]
[userId.toString(), '6']
);
const total = countResult[0].total;
@@ -191,12 +191,12 @@ router.get('/collections', authenticateToken, async (req, res) => {
ORDER BY n.created_at DESC LIMIT ? OFFSET ?
`;
const [rows] = await pool.execute(query, [userId, limit, offset]);
const [rows] = await pool.execute(query, [userId.toString(), limit.toString(), offset.toString()]);
// 获取总数
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM notifications WHERE user_id = ? AND type = ?',
[userId, 3]
[userId.toString(), '3']
);
const total = countResult[0].total;
@@ -238,24 +238,24 @@ router.get('/', authenticateToken, async (req, res) => {
LEFT JOIN users u ON n.sender_id = u.id
WHERE n.user_id = ?
`;
let queryParams = [userId];
let queryParams = [userId.toString()];
if (type) {
query += ` AND n.type = ?`;
queryParams.push(type);
queryParams.push(type.toString());
}
query += ` ORDER BY n.created_at DESC LIMIT ? OFFSET ?`;
queryParams.push(limit, offset);
queryParams.push(limit.toString(), offset.toString());
const [rows] = await pool.execute(query, queryParams);
// 获取总数
let countQuery = 'SELECT COUNT(*) as total FROM notifications WHERE user_id = ?';
let countParams = [userId];
let countParams = [userId.toString()];
if (type) {
countQuery += ' AND type = ?';
countParams.push(type);
countParams.push(type.toString());
}
const [countResult] = await pool.execute(countQuery, countParams);
@@ -264,7 +264,7 @@ router.get('/', authenticateToken, async (req, res) => {
// 获取未读数量
const [unreadResult] = await pool.execute(
'SELECT COUNT(*) as unread FROM notifications WHERE user_id = ? AND is_read = 0',
[userId]
[userId.toString()]
);
const unread = unreadResult[0].unread;
@@ -297,7 +297,7 @@ router.put('/:id/read', authenticateToken, async (req, res) => {
// 验证通知是否属于当前用户
const [notificationRows] = await pool.execute(
'SELECT id FROM notifications WHERE id = ? AND user_id = ?',
[notificationId, userId]
[notificationId.toString(), userId.toString()]
);
if (notificationRows.length === 0) {
@@ -307,7 +307,7 @@ router.put('/:id/read', authenticateToken, async (req, res) => {
// 标记为已读
await pool.execute(
'UPDATE notifications SET is_read = 1 WHERE id = ?',
[notificationId]
[notificationId.toString()]
);
res.json({ code: 200, message: '标记成功' });
@@ -325,7 +325,7 @@ router.put('/read-all', authenticateToken, async (req, res) => {
// 标记所有通知为已读
await pool.execute(
'UPDATE notifications SET is_read = 1 WHERE user_id = ? AND is_read = 0',
[userId]
[userId.toString()]
);
@@ -345,7 +345,7 @@ router.delete('/:id', authenticateToken, async (req, res) => {
// 验证通知是否属于当前用户
const [result] = await pool.execute(
'DELETE FROM notifications WHERE id = ? AND user_id = ?',
[notificationId, userId]
[notificationId.toString(), userId.toString()]
);
if (result.affectedRows === 0) {
@@ -374,7 +374,7 @@ router.get('/unread-count-by-type', authenticateToken, async (req, res) => {
COUNT(*) as total
FROM notifications
WHERE user_id = ? AND is_read = 0`,
[userId]
[userId.toString()]
);
const counts = result[0];
@@ -402,7 +402,7 @@ router.get('/unread-count', authenticateToken, async (req, res) => {
const [result] = await pool.execute(
'SELECT COUNT(*) as count FROM notifications WHERE user_id = ? AND is_read = 0',
[userId]
[userId.toString()]
);
res.json({

View File

@@ -28,7 +28,7 @@ router.get('/', optionalAuth, async (req, res) => {
LEFT JOIN users u ON p.user_id = u.id
WHERE p.is_draft = ? AND p.user_id = ?
`;
let queryParams = [isDraft, forcedUserId];
let queryParams = [isDraft.toString(), forcedUserId.toString()];
if (category) {
query += ` AND p.category = ?`;
@@ -36,9 +36,10 @@ router.get('/', optionalAuth, async (req, res) => {
}
query += ` ORDER BY p.created_at DESC LIMIT ? OFFSET ?`;
queryParams.push(limit, offset);
queryParams.push(limit.toString(), offset.toString());
const [rows] = await pool.execute(query, queryParams);
const [rows] = await pool.execute(query, queryParams);
// 获取每个草稿的图片和标签
for (let post of rows) {
@@ -61,7 +62,7 @@ router.get('/', optionalAuth, async (req, res) => {
// 获取草稿总数
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM posts WHERE is_draft = ? AND user_id = ?' + (category ? ' AND category = ?' : ''),
category ? [isDraft, forcedUserId, category] : [isDraft, forcedUserId]
category ? [isDraft.toString(), forcedUserId.toString(), category] : [isDraft.toString(), forcedUserId.toString()]
);
const total = countResult[0].total;
const pages = Math.ceil(total / limit);
@@ -87,12 +88,12 @@ router.get('/', optionalAuth, async (req, res) => {
LEFT JOIN users u ON p.user_id = u.id
WHERE p.is_draft = ?
`;
let queryParams = [isDraft];
let queryParams = [isDraft.toString()];
// 特殊处理推荐频道显示浏览量前20%的笔记,但支持分页
if (category === 'recommend') {
// 先获取总笔记数(只计算指定状态的笔记)
const [totalCountResult] = await pool.execute('SELECT COUNT(*) as total FROM posts WHERE is_draft = ?', [isDraft]);
const [totalCountResult] = await pool.execute('SELECT COUNT(*) as total FROM posts WHERE is_draft = ?', [isDraft.toString()]);
const totalPosts = totalCountResult[0].total;
const topPostsCount = Math.ceil(totalPosts * 0.2); // 前20%的笔记数量
@@ -106,18 +107,19 @@ router.get('/', optionalAuth, async (req, res) => {
ORDER BY p.view_count DESC
LIMIT ? OFFSET ?
`;
queryParams = [isDraft, topPostsCount, limit, offset];
queryParams = [isDraft.toString(), topPostsCount.toString(), limit.toString(), offset.toString()];
} else {
let whereConditions = [];
let additionalParams = [];
if (category) {
whereConditions.push('p.category = ?');
queryParams.push(category);
additionalParams.push(category);
}
if (userId) {
whereConditions.push('p.user_id = ?');
queryParams.push(userId);
additionalParams.push(userId);
}
if (whereConditions.length > 0) {
@@ -125,9 +127,11 @@ router.get('/', optionalAuth, async (req, res) => {
}
query += ` ORDER BY p.created_at DESC LIMIT ? OFFSET ?`;
queryParams.push(limit, offset);
queryParams = [isDraft.toString(), ...additionalParams, limit.toString(), offset.toString()];
}
console.log('SQL Query:', query);
console.log('Query Params:', queryParams);
const [rows] = await pool.execute(query, queryParams);
@@ -168,12 +172,12 @@ router.get('/', optionalAuth, async (req, res) => {
let total;
if (category === 'recommend') {
// 推荐频道的总数就是前20%的笔记数量
const [totalCountResult] = await pool.execute('SELECT COUNT(*) as total FROM posts WHERE is_draft = ?', [isDraft]);
const [totalCountResult] = await pool.execute('SELECT COUNT(*) as total FROM posts WHERE is_draft = ?', [isDraft.toString()]);
const totalPosts = totalCountResult[0].total;
total = Math.ceil(totalPosts * 0.2);
} else {
let countQuery = 'SELECT COUNT(*) as total FROM posts WHERE is_draft = ?';
let countParams = [isDraft];
let countParams = [isDraft.toString()];
let countWhereConditions = [];
if (category) {
@@ -327,7 +331,7 @@ router.post('/', authenticateToken, async (req, res) => {
for (const imageUrl of validUrls) {
await pool.execute(
'INSERT INTO post_images (post_id, image_url) VALUES (?, ?)',
[postId, imageUrl]
[postId.toString(), imageUrl]
);
}
}
@@ -347,10 +351,10 @@ router.post('/', authenticateToken, async (req, res) => {
}
// 关联笔记和标签
await pool.execute('INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)', [postId, tagId]);
await pool.execute('INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)', [postId.toString(), tagId.toString()]);
// 更新标签使用次数
await pool.execute('UPDATE tags SET use_count = use_count + 1 WHERE id = ?', [tagId]);
await pool.execute('UPDATE tags SET use_count = use_count + 1 WHERE id = ?', [tagId.toString()]);
}
}
@@ -390,7 +394,7 @@ router.get('/search', optionalAuth, async (req, res) => {
WHERE p.is_draft = 0 AND (p.title LIKE ? OR p.content LIKE ?)
ORDER BY p.created_at DESC
LIMIT ? OFFSET ?`,
[`%${keyword}%`, `%${keyword}%`, limit, offset]
[`%${keyword}%`, `%${keyword}%`, limit.toString(), offset.toString()]
);
// 获取每个笔记的图片、标签和用户点赞收藏状态
@@ -467,7 +471,7 @@ router.get('/:id/comments', optionalAuth, async (req, res) => {
console.log(`获取笔记评论列表 - 笔记ID: ${postId}, 页码: ${page}, 每页: ${limit}, 当前用户ID: ${currentUserId}`);
// 验证笔记是否存在
const [postRows] = await pool.execute('SELECT id FROM posts WHERE id = ?', [postId]);
const [postRows] = await pool.execute('SELECT id FROM posts WHERE id = ?', [postId.toString()]);
if (postRows.length === 0) {
return res.status(404).json({ code: 404, message: '笔记不存在' });
}
@@ -480,7 +484,7 @@ router.get('/:id/comments', optionalAuth, async (req, res) => {
WHERE c.post_id = ? AND c.parent_id IS NULL
ORDER BY c.created_at DESC
LIMIT ? OFFSET ?`,
[postId, limit, offset]
[postId, limit.toString(), offset.toString()]
);
// 为每个评论检查点赞状态
@@ -547,18 +551,18 @@ router.post('/:id/collect', authenticateToken, async (req, res) => {
// 检查是否已经收藏
const [existingCollection] = await pool.execute(
'SELECT id FROM collections WHERE user_id = ? AND post_id = ?',
[userId, postId]
[userId.toString(), postId.toString()]
);
if (existingCollection.length > 0) {
// 已收藏,执行取消收藏
await pool.execute(
'DELETE FROM collections WHERE user_id = ? AND post_id = ?',
[userId, postId]
[userId.toString(), postId.toString()]
);
// 更新笔记收藏数
await pool.execute('UPDATE posts SET collect_count = collect_count - 1 WHERE id = ?', [postId]);
await pool.execute('UPDATE posts SET collect_count = collect_count - 1 WHERE id = ?', [postId.toString()]);
console.log(`取消收藏成功 - 用户ID: ${userId}, 笔记ID: ${postId}`);
res.json({ code: 200, message: '取消收藏成功', data: { collected: false } });
@@ -566,14 +570,14 @@ router.post('/:id/collect', authenticateToken, async (req, res) => {
// 未收藏,执行收藏
await pool.execute(
'INSERT INTO collections (user_id, post_id) VALUES (?, ?)',
[userId, postId]
[userId.toString(), postId.toString()]
);
// 更新笔记收藏数
await pool.execute('UPDATE posts SET collect_count = collect_count + 1 WHERE id = ?', [postId]);
await pool.execute('UPDATE posts SET collect_count = collect_count + 1 WHERE id = ?', [postId.toString()]);
// 获取笔记作者ID用于创建通知
const [postResult] = await pool.execute('SELECT user_id FROM posts WHERE id = ?', [postId]);
const [postResult] = await pool.execute('SELECT user_id FROM posts WHERE id = ?', [postId.toString()]);
if (postResult.length > 0) {
const targetUserId = postResult[0].user_id;
@@ -608,7 +612,7 @@ router.put('/:id', authenticateToken, async (req, res) => {
// 检查笔记是否存在且属于当前用户
const [postRows] = await pool.execute(
'SELECT user_id FROM posts WHERE id = ?',
[postId]
[postId.toString()]
);
if (postRows.length === 0) {
@@ -622,11 +626,11 @@ router.put('/:id', authenticateToken, async (req, res) => {
// 更新笔记基本信息
await pool.execute(
'UPDATE posts SET title = ?, content = ?, category = ?, is_draft = ? WHERE id = ?',
[title || '', content || '', category || null, is_draft ? 1 : 0, postId]
[title || '', content || '', category || null, (is_draft ? 1 : 0).toString(), postId.toString()]
);
// 删除原有图片
await pool.execute('DELETE FROM post_images WHERE post_id = ?', [postId]);
await pool.execute('DELETE FROM post_images WHERE post_id = ?', [postId.toString()]);
// 处理新图片
if (images && images.length > 0) {
@@ -663,7 +667,7 @@ router.put('/:id', authenticateToken, async (req, res) => {
}
// 删除原有标签关联
await pool.execute('DELETE FROM post_tags WHERE post_id = ?', [postId]);
await pool.execute('DELETE FROM post_tags WHERE post_id = ?', [postId.toString()]);
// 处理新标签
if (tags && tags.length > 0) {
@@ -709,7 +713,7 @@ router.delete('/:id', authenticateToken, async (req, res) => {
// 检查笔记是否存在且属于当前用户
const [postRows] = await pool.execute(
'SELECT user_id FROM posts WHERE id = ?',
[postId]
[postId.toString()]
);
if (postRows.length === 0) {
@@ -723,24 +727,24 @@ router.delete('/:id', authenticateToken, async (req, res) => {
// 获取笔记关联的标签,减少标签使用次数
const [tagResult] = await pool.execute(
'SELECT tag_id FROM post_tags WHERE post_id = ?',
[postId]
[postId.toString()]
);
// 减少标签使用次数
for (const tag of tagResult) {
await pool.execute('UPDATE tags SET use_count = GREATEST(use_count - 1, 0) WHERE id = ?', [tag.tag_id]);
await pool.execute('UPDATE tags SET use_count = GREATEST(use_count - 1, 0) WHERE id = ?', [tag.tag_id.toString()]);
}
// 删除相关数据(由于外键约束,需要按顺序删除)
await pool.execute('DELETE FROM post_images WHERE post_id = ?', [postId]);
await pool.execute('DELETE FROM post_tags WHERE post_id = ?', [postId]);
await pool.execute('DELETE FROM likes WHERE target_type = 1 AND target_id = ?', [postId]);
await pool.execute('DELETE FROM collections WHERE post_id = ?', [postId]);
await pool.execute('DELETE FROM comments WHERE post_id = ?', [postId]);
await pool.execute('DELETE FROM notifications WHERE target_id = ?', [postId]);
await pool.execute('DELETE FROM post_images WHERE post_id = ?', [postId.toString()]);
await pool.execute('DELETE FROM post_tags WHERE post_id = ?', [postId.toString()]);
await pool.execute('DELETE FROM likes WHERE target_type = 1 AND target_id = ?', [postId.toString()]);
await pool.execute('DELETE FROM collections WHERE post_id = ?', [postId.toString()]);
await pool.execute('DELETE FROM comments WHERE post_id = ?', [postId.toString()]);
await pool.execute('DELETE FROM notifications WHERE target_id = ?', [postId.toString()]);
// 最后删除笔记
await pool.execute('DELETE FROM posts WHERE id = ?', [postId]);
await pool.execute('DELETE FROM posts WHERE id = ?', [postId.toString()]);
console.log(`删除笔记成功 - 用户ID: ${userId}, 笔记ID: ${postId}`);
@@ -765,7 +769,7 @@ router.delete('/:id/collect', authenticateToken, async (req, res) => {
// 删除收藏记录
const [result] = await pool.execute(
'DELETE FROM collections WHERE user_id = ? AND post_id = ?',
[userId, postId]
[userId.toString(), postId.toString()]
);
if (result.affectedRows === 0) {
@@ -773,7 +777,7 @@ router.delete('/:id/collect', authenticateToken, async (req, res) => {
}
// 更新笔记收藏数
await pool.execute('UPDATE posts SET collect_count = collect_count - 1 WHERE id = ?', [postId]);
await pool.execute('UPDATE posts SET collect_count = collect_count - 1 WHERE id = ?', [postId.toString()]);
console.log(`取消收藏成功 - 用户ID: ${userId}, 笔记ID: ${postId}`);
res.json({ code: 200, message: '取消收藏成功', data: { collected: false } });

View File

@@ -82,19 +82,19 @@ router.get('/', optionalAuth, async (req, res) => {
${whereClause}
ORDER BY p.created_at DESC
LIMIT ? OFFSET ?`,
[...queryParams, limit, offset]
[...queryParams, limit.toString(), offset.toString()]
);
// 获取每个笔记的图片、标签和用户点赞收藏状态
for (let post of postRows) {
// 获取笔记图片
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id]);
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id.toString()]);
post.images = images.map(img => img.image_url);
// 获取笔记标签
const [tags] = await pool.execute(
'SELECT t.id, t.name FROM tags t JOIN post_tags pt ON t.id = pt.tag_id WHERE pt.post_id = ?',
[post.id]
[post.id.toString()]
);
post.tags = tags;
@@ -102,13 +102,13 @@ router.get('/', optionalAuth, async (req, res) => {
if (currentUserId) {
const [likeResult] = await pool.execute(
'SELECT id FROM likes WHERE user_id = ? AND target_type = 1 AND target_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.liked = likeResult.length > 0;
const [collectResult] = await pool.execute(
'SELECT id FROM collections WHERE user_id = ? AND post_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.collected = collectResult.length > 0;
} else {
@@ -189,7 +189,7 @@ router.get('/', optionalAuth, async (req, res) => {
WHERE u.nickname LIKE ? OR u.user_id LIKE ?
ORDER BY u.created_at DESC
LIMIT ? OFFSET ?`,
[`%${keyword}%`, `%${keyword}%`, limit, offset]
[`%${keyword}%`, `%${keyword}%`, limit.toString(), offset.toString()]
);
// 检查关注状态(仅在用户已登录时)
@@ -198,19 +198,19 @@ router.get('/', optionalAuth, async (req, res) => {
// 检查是否已关注
const [followResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[currentUserId, user.id]
[currentUserId.toString(), user.id.toString()]
);
user.isFollowing = followResult.length > 0;
// 检查是否互相关注
const [mutualResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[user.id, currentUserId]
[user.id.toString(), currentUserId.toString()]
);
user.isMutual = user.isFollowing && mutualResult.length > 0;
// 设置按钮类型
if (user.id === currentUserId) {
if (user.id.toString() === currentUserId.toString()) {
user.buttonType = 'self';
} else if (user.isMutual) {
user.buttonType = 'mutual';

View File

@@ -31,7 +31,7 @@ router.get('/hot', async (req, res) => {
WHERE use_count > 0
ORDER BY use_count DESC, name ASC
LIMIT ?`,
[limit]
[String(limit)]
);

View File

@@ -25,7 +25,7 @@ router.get('/search', optionalAuth, async (req, res) => {
WHERE u.nickname LIKE ? OR u.user_id LIKE ?
ORDER BY u.created_at DESC
LIMIT ? OFFSET ?`,
[`%${keyword}%`, `%${keyword}%`, limit, offset]
[`%${keyword}%`, `%${keyword}%`, limit.toString(), offset.toString()]
);
// 检查关注状态(仅在用户已登录时)
@@ -34,14 +34,14 @@ router.get('/search', optionalAuth, async (req, res) => {
// 检查是否已关注
const [followResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[currentUserId, user.id]
[currentUserId.toString(), user.id.toString()]
);
user.isFollowing = followResult.length > 0;
// 检查是否互相关注
const [mutualResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[user.id, currentUserId]
[user.id.toString(), currentUserId.toString()]
);
user.isMutual = user.isFollowing && mutualResult.length > 0;
@@ -189,8 +189,8 @@ router.get('/', async (req, res) => {
const offset = (page - 1) * limit;
const [rows] = await pool.execute(
'SELECT id, user_id, nickname, avatar, bio, location, follow_count, fans_count, like_count, created_at FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?',
[limit, offset]
`SELECT id, user_id, nickname, avatar, bio, location, follow_count, fans_count, like_count, created_at FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?`,
[limit.toString(), offset.toString()]
);
const [countResult] = await pool.execute('SELECT COUNT(*) as total FROM users');
@@ -236,7 +236,7 @@ router.get('/:id/posts', optionalAuth, async (req, res) => {
// 构建查询条件
let whereConditions = ['p.user_id = ?', 'p.is_draft = 0'];
let queryParams = [userId];
let queryParams = [userId.toString()];
if (category) {
whereConditions.push('p.category = ?');
@@ -262,19 +262,19 @@ router.get('/:id/posts', optionalAuth, async (req, res) => {
${orderBy}
LIMIT ? OFFSET ?
`;
queryParams.push(limit, offset);
queryParams.push(limit.toString(), offset.toString());
const [rows] = await pool.execute(query, queryParams);
// 获取每个笔记的图片、标签和用户点赞收藏状态
for (let post of rows) {
// 获取笔记图片
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id]);
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id.toString()]);
post.images = images.map(img => img.image_url);
// 获取笔记标签
const [tags] = await pool.execute(
'SELECT t.id, t.name FROM tags t JOIN post_tags pt ON t.id = pt.tag_id WHERE pt.post_id = ?',
[post.id]
[post.id.toString()]
);
post.tags = tags;
@@ -282,14 +282,14 @@ router.get('/:id/posts', optionalAuth, async (req, res) => {
if (currentUserId) {
const [likeResult] = await pool.execute(
'SELECT id FROM likes WHERE user_id = ? AND target_type = 1 AND target_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.liked = likeResult.length > 0;
// 检查当前用户是否已收藏
const [collectResult] = await pool.execute(
'SELECT id FROM collections WHERE user_id = ? AND post_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.collected = collectResult.length > 0;
} else {
@@ -348,19 +348,19 @@ router.get('/:id/collections', optionalAuth, async (req, res) => {
WHERE c.user_id = ? AND p.is_draft = 0
ORDER BY c.created_at DESC
LIMIT ? OFFSET ?`,
[userId, limit, offset]
[userId.toString(), limit.toString(), offset.toString()]
);
// 获取每个笔记的图片、标签和用户点赞收藏状态
for (let post of rows) {
// 获取笔记图片
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id]);
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id.toString()]);
post.images = images.map(img => img.image_url);
// 获取笔记标签
const [tags] = await pool.execute(
'SELECT t.id, t.name FROM tags t JOIN post_tags pt ON t.id = pt.tag_id WHERE pt.post_id = ?',
[post.id]
[post.id.toString()]
);
post.tags = tags;
@@ -368,13 +368,13 @@ router.get('/:id/collections', optionalAuth, async (req, res) => {
if (currentUserId) {
const [likeResult] = await pool.execute(
'SELECT id FROM likes WHERE user_id = ? AND target_type = 1 AND target_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.liked = likeResult.length > 0;
const [collectResult] = await pool.execute(
'SELECT id FROM collections WHERE user_id = ? AND post_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.collected = collectResult.length > 0;
} else {
@@ -385,7 +385,7 @@ router.get('/:id/collections', optionalAuth, async (req, res) => {
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM collections c LEFT JOIN posts p ON c.post_id = p.id WHERE c.user_id = ? AND p.is_draft = 0',
[userId]
[userId.toString()]
);
const total = countResult[0].total;
@@ -433,19 +433,19 @@ router.get('/:id/likes', optionalAuth, async (req, res) => {
WHERE l.user_id = ? AND l.target_type = 1 AND p.is_draft = 0
ORDER BY l.created_at DESC
LIMIT ? OFFSET ?`,
[userId, limit, offset]
[userId.toString(), limit.toString(), offset.toString()]
);
// 获取每个笔记的图片、标签和用户点赞收藏状态
for (let post of rows) {
// 获取笔记图片
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id]);
const [images] = await pool.execute('SELECT image_url FROM post_images WHERE post_id = ?', [post.id.toString()]);
post.images = images.map(img => img.image_url);
// 获取笔记标签
const [tags] = await pool.execute(
'SELECT t.id, t.name FROM tags t JOIN post_tags pt ON t.id = pt.tag_id WHERE pt.post_id = ?',
[post.id]
[post.id.toString()]
);
post.tags = tags;
@@ -453,13 +453,13 @@ router.get('/:id/likes', optionalAuth, async (req, res) => {
if (currentUserId) {
const [likeResult] = await pool.execute(
'SELECT id FROM likes WHERE user_id = ? AND target_type = 1 AND target_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.liked = likeResult.length > 0;
const [collectResult] = await pool.execute(
'SELECT id FROM collections WHERE user_id = ? AND post_id = ?',
[currentUserId, post.id]
[currentUserId.toString(), post.id.toString()]
);
post.collected = collectResult.length > 0;
} else {
@@ -470,7 +470,7 @@ router.get('/:id/likes', optionalAuth, async (req, res) => {
const [countResult] = await pool.execute(
'SELECT COUNT(*) as total FROM likes l LEFT JOIN posts p ON l.target_id = p.id WHERE l.user_id = ? AND l.target_type = 1 AND p.is_draft = 0',
[userId]
[userId.toString()]
);
const total = countResult[0].total;
@@ -515,7 +515,7 @@ router.post('/:id/follow', authenticateToken, async (req, res) => {
// 检查是否已经关注
const [existingFollow] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[followerId, userId]
[followerId.toString(), userId.toString()]
);
if (existingFollow.length > 0) {
@@ -525,14 +525,14 @@ router.post('/:id/follow', authenticateToken, async (req, res) => {
// 添加关注记录
await pool.execute(
'INSERT INTO follows (follower_id, following_id) VALUES (?, ?)',
[followerId, userId]
[followerId.toString(), userId.toString()]
);
// 更新关注者的关注数
await pool.execute('UPDATE users SET follow_count = follow_count + 1 WHERE id = ?', [followerId]);
await pool.execute('UPDATE users SET follow_count = follow_count + 1 WHERE id = ?', [followerId.toString()]);
// 更新被关注者的粉丝数
await pool.execute('UPDATE users SET fans_count = fans_count + 1 WHERE id = ?', [userId]);
await pool.execute('UPDATE users SET fans_count = fans_count + 1 WHERE id = ?', [userId.toString()]);
// 创建关注通知
try {
@@ -566,7 +566,7 @@ router.delete('/:id/follow', authenticateToken, async (req, res) => {
// 删除关注记录
const [result] = await pool.execute(
'DELETE FROM follows WHERE follower_id = ? AND following_id = ?',
[followerId, userId]
[followerId.toString(), userId.toString()]
);
if (result.affectedRows === 0) {
@@ -574,16 +574,16 @@ router.delete('/:id/follow', authenticateToken, async (req, res) => {
}
// 更新关注者的关注数
await pool.execute('UPDATE users SET follow_count = follow_count - 1 WHERE id = ?', [followerId]);
await pool.execute('UPDATE users SET follow_count = follow_count - 1 WHERE id = ?', [followerId.toString()]);
// 更新被关注者的粉丝数
await pool.execute('UPDATE users SET fans_count = fans_count - 1 WHERE id = ?', [userId]);
await pool.execute('UPDATE users SET fans_count = fans_count - 1 WHERE id = ?', [userId.toString()]);
// 删除相关的关注通知
// 删除关注者发给被关注者的关注通知
await pool.execute(
'DELETE FROM notifications WHERE user_id = ? AND sender_id = ? AND type = ?',
[userId, followerId, NotificationHelper.TYPES.FOLLOW]
[userId.toString(), followerId.toString(), NotificationHelper.TYPES.FOLLOW.toString()]
);
console.log(`取消关注成功 - 用户ID: ${followerId}, 目标用户ID: ${userId}`);
@@ -618,14 +618,14 @@ router.get('/:id/follow-status', optionalAuth, async (req, res) => {
// 检查关注状态
const [followResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[followerId, userId]
[followerId.toString(), userId.toString()]
);
isFollowing = followResult.length > 0;
// 检查是否互相关注
const [mutualResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[userId, followerId]
[userId.toString(), followerId.toString()]
);
isMutual = isFollowing && mutualResult.length > 0;
@@ -683,7 +683,7 @@ router.get('/:id/following', optionalAuth, async (req, res) => {
WHERE f.follower_id = ?
ORDER BY f.created_at DESC
LIMIT ? OFFSET ?`,
[userId, limit, offset]
[userId.toString(), limit.toString(), offset.toString()]
);
// 检查当前用户与这些用户的关注状态
@@ -692,14 +692,14 @@ router.get('/:id/following', optionalAuth, async (req, res) => {
// 检查是否已关注
const [followResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[currentUserId, user.id]
[currentUserId.toString(), user.id.toString()]
);
user.isFollowing = followResult.length > 0;
// 检查是否互相关注
const [mutualResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[user.id, currentUserId]
[user.id.toString(), currentUserId.toString()]
);
user.isMutual = user.isFollowing && mutualResult.length > 0;
@@ -728,7 +728,7 @@ router.get('/:id/following', optionalAuth, async (req, res) => {
const [countResult] = await pool.execute(
`SELECT COUNT(*) as total FROM follows f
WHERE f.follower_id = ?`,
[userId]
[userId.toString()]
);
const total = countResult[0].total;
@@ -777,7 +777,7 @@ router.get('/:id/followers', optionalAuth, async (req, res) => {
WHERE f.following_id = ?
ORDER BY f.created_at DESC
LIMIT ? OFFSET ?`,
[userId, limit, offset]
[userId.toString(), limit.toString(), offset.toString()]
);
// 检查当前用户与这些用户的关注状态
@@ -786,14 +786,14 @@ router.get('/:id/followers', optionalAuth, async (req, res) => {
// 检查是否已关注
const [followResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[currentUserId, user.id]
[currentUserId.toString(), user.id.toString()]
);
user.isFollowing = followResult.length > 0;
// 检查是否互相关注
const [mutualResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[user.id, currentUserId]
[user.id.toString(), currentUserId.toString()]
);
user.isMutual = user.isFollowing && mutualResult.length > 0;
@@ -818,7 +818,7 @@ router.get('/:id/followers', optionalAuth, async (req, res) => {
}
}
const [countResult] = await pool.execute('SELECT COUNT(*) as total FROM follows WHERE following_id = ?', [userId]);
const [countResult] = await pool.execute('SELECT COUNT(*) as total FROM follows WHERE following_id = ?', [userId.toString()]);
const total = countResult[0].total;
res.json({
@@ -874,7 +874,7 @@ router.get('/:id/mutual-follows', optionalAuth, async (req, res) => {
)
ORDER BY u.created_at DESC
LIMIT ? OFFSET ?`,
[userId, userId, limit, offset]
[userId.toString(), userId.toString(), limit.toString(), offset.toString()]
);
// 检查当前用户与这些用户的关注状态
@@ -883,14 +883,14 @@ router.get('/:id/mutual-follows', optionalAuth, async (req, res) => {
// 检查是否已关注
const [followResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[currentUserId, user.id]
[currentUserId.toString(), user.id.toString()]
);
user.isFollowing = followResult.length > 0;
// 检查是否互相关注
const [mutualResult] = await pool.execute(
'SELECT id FROM follows WHERE follower_id = ? AND following_id = ?',
[user.id, currentUserId]
[user.id.toString(), currentUserId.toString()]
);
user.isMutual = user.isFollowing && mutualResult.length > 0;
@@ -967,7 +967,7 @@ router.get('/:id/stats', async (req, res) => {
// 获取用户基本统计信息
const [userStats] = await pool.execute(
'SELECT follow_count, fans_count, like_count FROM users WHERE id = ?',
[userId]
[userId.toString()]
);
if (userStats.length === 0) {
@@ -977,13 +977,13 @@ router.get('/:id/stats', async (req, res) => {
// 获取笔记数量
const [postCount] = await pool.execute(
'SELECT COUNT(*) as count FROM posts WHERE user_id = ? AND is_draft = 0',
[userId]
[userId.toString()]
);
// 获取该用户发布的笔记被收藏的总数量
const [collectCount] = await pool.execute(
'SELECT COUNT(*) as count FROM collections c JOIN posts p ON c.post_id = p.id WHERE p.user_id = ? AND p.is_draft = 0',
[userId]
[userId.toString()]
);
// 计算获赞与收藏总数
@@ -1101,7 +1101,7 @@ router.put('/:id', authenticateToken, async (req, res) => {
// 获取更新后的用户信息
const [updatedUser] = await pool.execute(
'SELECT id, user_id, nickname, avatar, bio, location, gender, zodiac_sign, mbti, education, major, interests, follow_count, fans_count, like_count FROM users WHERE id = ?',
[targetUserId]
[targetUserId.toString()]
);
res.json({
@@ -1149,7 +1149,7 @@ router.put('/:id/password', authenticateToken, async (req, res) => {
// 验证当前密码使用SHA2哈希比较
const [passwordRows] = await pool.execute(
'SELECT password FROM users WHERE id = ? AND password = SHA2(?, 256)',
[targetUserId, currentPassword]
[targetUserId.toString(), currentPassword]
);
if (passwordRows.length === 0) {
@@ -1159,7 +1159,7 @@ router.put('/:id/password', authenticateToken, async (req, res) => {
// 更新密码使用SHA2哈希加密
await pool.execute(
'UPDATE users SET password = SHA2(?, 256) WHERE id = ?',
[newPassword, targetUserId]
[newPassword, targetUserId.toString()]
);
res.json({

View File

@@ -182,7 +182,7 @@ async function getRecords(table, options = {}) {
// 获取数据
const dataQuery = `SELECT ${fields} FROM ${table} ${whereClause} ORDER BY ${orderBy} LIMIT ? OFFSET ?`
const [dataResult] = await pool.execute(dataQuery, [...params, limit, offset])
const [dataResult] = await pool.execute(dataQuery, [...params, String(limit), String(offset)])
return {
data: dataResult,

View File

@@ -69,7 +69,7 @@ async function getPaginatedData(table, options = {}) {
// 获取分页数据
const offset = (page - 1) * limit;
const dataQuery = `SELECT ${fields} FROM ${table} ${whereClause} ORDER BY ${orderBy} LIMIT ? OFFSET ?`;
const dataParams = [...params, limit, offset];
const dataParams = [...params, String(limit), String(offset)];
const [data] = await pool.execute(dataQuery, dataParams);

View File

@@ -1,5 +1,5 @@
# 多阶段构建:构建阶段
FROM node:18-alpine as build-stage
FROM node:18-alpine AS build-stage
# 设置工作目录
WORKDIR /app
@@ -17,7 +17,7 @@ COPY . .
RUN npm run build
# 生产阶段使用nginx服务静态文件
FROM nginx:alpine as production-stage
FROM nginx:alpine AS production-stage
# 复制构建产物到nginx目录
COPY --from=build-stage /app/dist /usr/share/nginx/html

View File

@@ -8,7 +8,7 @@ server {
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private must-revalidate auth;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# 处理Vue Router的history模式

View File

@@ -1,12 +1,12 @@
{
"name": "vue3-project",
"version": "0.0.0",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vue3-project",
"version": "0.0.0",
"version": "1.0.0",
"dependencies": {
"@vueuse/core": "^13.5.0",
"axios": "^1.11.0",
@@ -17,10 +17,10 @@
"vue3-emoji-picker": "^1.1.8"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.0",
"@vitejs/plugin-vue": "^5.0.0",
"fast-glob": "^3.3.3",
"patch-package": "^8.0.0",
"vite": "^7.0.0",
"vite": "^5.4.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-devtools": "^7.7.7"
}
@@ -50,9 +50,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
@@ -63,13 +63,13 @@
"aix"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
@@ -80,13 +80,13 @@
"android"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
@@ -97,13 +97,13 @@
"android"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
@@ -114,13 +114,13 @@
"android"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
@@ -131,13 +131,13 @@
"darwin"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
@@ -148,13 +148,13 @@
"darwin"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
@@ -165,13 +165,13 @@
"freebsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
@@ -182,13 +182,13 @@
"freebsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
@@ -199,13 +199,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
@@ -216,13 +216,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
@@ -233,13 +233,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
@@ -250,13 +250,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
@@ -267,13 +267,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
@@ -284,13 +284,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
@@ -301,13 +301,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
@@ -318,13 +318,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
@@ -335,30 +335,13 @@
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
@@ -369,30 +352,13 @@
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
@@ -403,30 +369,13 @@
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
@@ -437,13 +386,13 @@
"sunos"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
@@ -454,13 +403,13 @@
"win32"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
@@ -471,13 +420,13 @@
"win32"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
@@ -488,7 +437,7 @@
"win32"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@jridgewell/gen-mapping": {
@@ -584,13 +533,6 @@
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.29",
"resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz",
"integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/pluginutils": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
@@ -971,19 +913,16 @@
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz",
"integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==",
"version": "5.2.4",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
"integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rolldown/pluginutils": "1.0.0-beta.29"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
"node": "^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
"vite": "^5.0.0 || ^6.0.0",
"vue": "^3.2.25"
}
},
@@ -2532,9 +2471,9 @@
}
},
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz",
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -2542,35 +2481,32 @@
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.8",
"@esbuild/android-arm": "0.25.8",
"@esbuild/android-arm64": "0.25.8",
"@esbuild/android-x64": "0.25.8",
"@esbuild/darwin-arm64": "0.25.8",
"@esbuild/darwin-x64": "0.25.8",
"@esbuild/freebsd-arm64": "0.25.8",
"@esbuild/freebsd-x64": "0.25.8",
"@esbuild/linux-arm": "0.25.8",
"@esbuild/linux-arm64": "0.25.8",
"@esbuild/linux-ia32": "0.25.8",
"@esbuild/linux-loong64": "0.25.8",
"@esbuild/linux-mips64el": "0.25.8",
"@esbuild/linux-ppc64": "0.25.8",
"@esbuild/linux-riscv64": "0.25.8",
"@esbuild/linux-s390x": "0.25.8",
"@esbuild/linux-x64": "0.25.8",
"@esbuild/netbsd-arm64": "0.25.8",
"@esbuild/netbsd-x64": "0.25.8",
"@esbuild/openbsd-arm64": "0.25.8",
"@esbuild/openbsd-x64": "0.25.8",
"@esbuild/openharmony-arm64": "0.25.8",
"@esbuild/sunos-x64": "0.25.8",
"@esbuild/win32-arm64": "0.25.8",
"@esbuild/win32-ia32": "0.25.8",
"@esbuild/win32-x64": "0.25.8"
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/escalade": {
@@ -6197,51 +6133,6 @@
"node": ">=10.13.0"
}
},
"node_modules/tinyglobby": {
"version": "0.2.14",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz",
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.0.33.tgz",
@@ -6691,24 +6582,21 @@
}
},
"node_modules/vite": {
"version": "7.0.6",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.0.6.tgz",
"integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
"version": "5.4.19",
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.6",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.40.0",
"tinyglobby": "^0.2.14"
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -6717,25 +6605,19 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
@@ -6756,12 +6638,6 @@
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
},
@@ -7457,34 +7333,6 @@
"semver": "bin/semver.js"
}
},
"node_modules/vite/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/vite/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/vue": {
"version": "3.5.18",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.18.tgz",

View File

@@ -20,10 +20,10 @@
"vue3-emoji-picker": "^1.1.8"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.0",
"@vitejs/plugin-vue": "^5.0.0",
"fast-glob": "^3.3.3",
"patch-package": "^8.0.0",
"vite": "^7.0.0",
"vite": "^5.4.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-devtools": "^7.7.7"
}

View File

@@ -545,7 +545,8 @@ const fetchComments = async () => {
const result = await commentStore.fetchComments(props.item.id)
await nextTick()
const latestComments = comments.value
if (latestComments && latestComments.length > 0 && userStore.isLoggedIn) {
if (latestComments && latestComments.length > 0) {
// 无论是否登录都初始化评论点赞状态,未登录用户只显示点赞数量,不显示点赞状态
commentLikeStore.initCommentsLikeStates(latestComments)
}
} catch (error) {

View File

@@ -41,7 +41,7 @@ const updateSlider = () => {
const containerRect = containerRef.value.getBoundingClientRect()
// 计算滑块相对于容器的位置
sliderLeft.value = tabRect.left - containerRect.left
sliderLeft.value = tabRect.left - containerRect.left + containerRef.value.scrollLeft
sliderWidth.value = tabRect.width
})
}
@@ -92,10 +92,17 @@ onMounted(() => {
nextTick(updateSlider)
// 监听窗口大小变化,重新计算滑块位置
window.addEventListener('resize', updateSlider)
// 监听容器滚动事件,实时更新滑块位置
if (containerRef.value) {
containerRef.value.addEventListener('scroll', updateSlider)
}
})
onUnmounted(() => {
window.removeEventListener('resize', updateSlider)
if (containerRef.value) {
containerRef.value.removeEventListener('scroll', updateSlider)
}
})
</script>

View File

@@ -8,7 +8,7 @@
<img :src="logoUrl" alt="小石榴" class="about-logo" />
<h2 class="about-title">关于小石榴</h2>
</div>
<p class="version">v1.0.0</p>
<p class="version">v1.0.1</p>
</div>
<button class="close-btn" @click="closeModal">
<SvgIcon name="close" />

View File

@@ -3,7 +3,7 @@
<div class="docs-header">
<h2>小石榴图文社区 API 接口文档</h2>
<div class="docs-info">
<span class="version">版本: v1.0.0</span>
<span class="version">版本: v1.0.1</span>
<span class="base-url">基础URL: http://localhost:3001/</span>
<span class="update-time">更新时间: 2025-09-01</span>
</div>