fix: 修复3个高优先级Bug + 5个中低优先级问题
BUG-01: recurring /sync next_date 更新逻辑错误(应用循环后的 next) BUG-02: bills onReachBottom loading 缺 .value BUG-03: group store switchToPersonal 缺少 await (3处) OPT-04: notification requireNotificationEditAuth 加 try-catch OPT-05: category 迁移源分类条件改为 is_custom=1 AND user_id=? OPT-06: user 旧头像删除加 try-catch + 日志 CODE-02: format.ts 跨年日期显示年份 CODE-05: notification isValidUrl 去掉相对路径允许
This commit is contained in:
@@ -242,7 +242,7 @@ async function onDelete(item: any) {
|
|||||||
|
|
||||||
// 触底加载更多
|
// 触底加载更多
|
||||||
onReachBottom(() => {
|
onReachBottom(() => {
|
||||||
if (noMore.value || loading) return
|
if (noMore.value || loading.value) return
|
||||||
page.value++
|
page.value++
|
||||||
loadTx(false)
|
loadTx(false)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const useGroupStore = defineStore('group', () => {
|
|||||||
if (currentGroupId.value !== null) {
|
if (currentGroupId.value !== null) {
|
||||||
const stillValid = groups.value.some(g => g.id === currentGroupId.value)
|
const stillValid = groups.value.some(g => g.id === currentGroupId.value)
|
||||||
if (!stillValid) {
|
if (!stillValid) {
|
||||||
switchToPersonal()
|
await switchToPersonal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -83,7 +83,7 @@ export const useGroupStore = defineStore('group', () => {
|
|||||||
async function leaveGroup(groupId: number) {
|
async function leaveGroup(groupId: number) {
|
||||||
await api.leaveGroup(groupId)
|
await api.leaveGroup(groupId)
|
||||||
if (currentGroupId.value === groupId) {
|
if (currentGroupId.value === groupId) {
|
||||||
switchToPersonal()
|
await switchToPersonal()
|
||||||
}
|
}
|
||||||
await fetchGroups()
|
await fetchGroups()
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ export const useGroupStore = defineStore('group', () => {
|
|||||||
async function deleteGroup(groupId: number) {
|
async function deleteGroup(groupId: number) {
|
||||||
await api.deleteGroup(groupId)
|
await api.deleteGroup(groupId)
|
||||||
if (currentGroupId.value === groupId) {
|
if (currentGroupId.value === groupId) {
|
||||||
switchToPersonal()
|
await switchToPersonal()
|
||||||
}
|
}
|
||||||
await fetchGroups()
|
await fetchGroups()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ export function formatDate(date: string | Date): string {
|
|||||||
if (diff === 1) return '昨天'
|
if (diff === 1) return '昨天'
|
||||||
if (diff === 2) return '前天'
|
if (diff === 2) return '前天'
|
||||||
|
|
||||||
return `${d.getMonth() + 1}月${d.getDate()}日`
|
// 跨年显示年份
|
||||||
|
const isCurrentYear = d.getFullYear() === now.getFullYear()
|
||||||
|
return isCurrentYear
|
||||||
|
? `${d.getMonth() + 1}月${d.getDate()}日`
|
||||||
|
: `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatMonth(date: string): string {
|
export function formatMonth(date: string): string {
|
||||||
|
|||||||
@@ -124,9 +124,9 @@ router.post('/:id/migrate', async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
await conn.beginTransaction()
|
await conn.beginTransaction()
|
||||||
|
|
||||||
// 验证源分类存在且属于当前用户
|
// 验证源分类存在且属于当前用户(仅允许迁移自定义分类)
|
||||||
const [sourceRows] = await conn.query(
|
const [sourceRows] = await conn.query(
|
||||||
'SELECT id, type FROM categories WHERE id = ? AND (user_id = 0 OR user_id = ?)',
|
'SELECT id, type FROM categories WHERE id = ? AND is_custom = 1 AND user_id = ?',
|
||||||
[req.params.id, req.userId]
|
[req.params.id, req.userId]
|
||||||
)
|
)
|
||||||
const sourceCategory = (sourceRows as any[])[0]
|
const sourceCategory = (sourceRows as any[])[0]
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const upload = multer({
|
|||||||
|
|
||||||
/** 公告编辑权限检查(管理员可编辑所有公告,群主可编辑自己群组的公告) */
|
/** 公告编辑权限检查(管理员可编辑所有公告,群主可编辑自己群组的公告) */
|
||||||
async function requireNotificationEditAuth(req: AuthRequest, res: Response, next: NextFunction) {
|
async function requireNotificationEditAuth(req: AuthRequest, res: Response, next: NextFunction) {
|
||||||
|
try {
|
||||||
const [rows] = await pool.query(
|
const [rows] = await pool.query(
|
||||||
'SELECT type, group_id FROM notifications WHERE id = ?',
|
'SELECT type, group_id FROM notifications WHERE id = ?',
|
||||||
[req.params.id]
|
[req.params.id]
|
||||||
@@ -75,6 +76,10 @@ async function requireNotificationEditAuth(req: AuthRequest, res: Response, next
|
|||||||
}
|
}
|
||||||
|
|
||||||
return res.status(403).json({ code: 40300, message: '需要管理员或群主权限' })
|
return res.status(403).json({ code: 40300, message: '需要管理员或群主权限' })
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Notification] editAuth error:', err)
|
||||||
|
return res.status(500).json({ code: 50000, message: '服务器错误' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取通知列表 */
|
/** 获取通知列表 */
|
||||||
@@ -283,8 +288,8 @@ function isValidUrl(url: string): boolean {
|
|||||||
const parsed = new URL(url)
|
const parsed = new URL(url)
|
||||||
return ['http:', 'https:'].includes(parsed.protocol)
|
return ['http:', 'https:'].includes(parsed.protocol)
|
||||||
} catch {
|
} catch {
|
||||||
// 相对路径也允许
|
// 非标准 URL 格式,小程序不支持相对路径,拒绝
|
||||||
return url.startsWith('/') || url.startsWith('./')
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,21 +177,10 @@ router.post('/sync', async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新 next_date 到下一周期
|
// 更新 next_date:循环结束后 next 已推进到今天之后的下一周期,直接使用
|
||||||
let newNext: Date
|
|
||||||
if (t.frequency === 'weekly') {
|
|
||||||
newNext = new Date(new Date(t.next_date).getTime() + 7 * 86400000)
|
|
||||||
} else if (t.frequency === 'monthly') {
|
|
||||||
const d = new Date(t.next_date)
|
|
||||||
newNext = new Date(d.getFullYear(), d.getMonth() + 1, d.getDate())
|
|
||||||
} else {
|
|
||||||
const d = new Date(t.next_date)
|
|
||||||
newNext = new Date(d.getFullYear() + 1, d.getMonth(), d.getDate())
|
|
||||||
}
|
|
||||||
|
|
||||||
await pool.execute(
|
await pool.execute(
|
||||||
'UPDATE recurring_templates SET next_date = ? WHERE id = ?',
|
'UPDATE recurring_templates SET next_date = ? WHERE id = ?',
|
||||||
[newNext.toISOString().slice(0, 10), t.id]
|
[next.toISOString().slice(0, 10), t.id]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,11 @@ router.post('/avatar', (req: AuthRequest, res: Response) => {
|
|||||||
if (oldUrl) {
|
if (oldUrl) {
|
||||||
const oldPath = path.join(AVATAR_DIR, oldUrl)
|
const oldPath = path.join(AVATAR_DIR, oldUrl)
|
||||||
if (fs.existsSync(oldPath)) {
|
if (fs.existsSync(oldPath)) {
|
||||||
|
try {
|
||||||
fs.unlinkSync(oldPath)
|
fs.unlinkSync(oldPath)
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[User] 删除旧头像失败:', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user