- 自动归档改为 doneAt + autoArchive 到期,只判定顶层任务,到期整树归档 - 手动归档/恢复提升到根父任务,子任务完全跟随父任务 - 新增 normalizeArchiveTree 修复历史父子归档不一致 - 移除按列保留窗口的显示过滤(前后端实现删除,T-FL-01~15 废止) - 列 retention 字段仅兼容旧数据保留,不再参与判定 - 已归档查询返回归档子任务,删除提示可正确显示连带条数
This commit is contained in:
+21
-11
@@ -6,7 +6,6 @@ import {
|
||||
loadTaskForUser, loadProjectForUser, loadColumns, loadMemberIds,
|
||||
} from '../task-domain/access.js'
|
||||
import { resolveTaskWrite } from '../task-domain/resolveStatus.js'
|
||||
import { filterVisible } from '../task-domain/retention.js'
|
||||
import { copyTask } from '../task-domain/copyTask.js'
|
||||
import { runAutoArchiveForProject } from '../task-domain/archive.js'
|
||||
import { genUuid, withUuid, withTaskNumber } from '../task-domain/identity.js'
|
||||
@@ -65,24 +64,31 @@ router.get('/projects/:id/tasks', async (req, res, next) => {
|
||||
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'asc' }],
|
||||
})
|
||||
const shaped = rows.map(shape)
|
||||
// 保留选项过滤 (T-FL-*): 已完成任务按所属列配置隐藏
|
||||
const visible = filterVisible(shaped, columns)
|
||||
res.json({ items: visible, columns, total: shaped.length })
|
||||
res.json({ items: shaped, columns, total: shaped.length })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// ---------- 归档 (T-ARC) ----------
|
||||
|
||||
/**
|
||||
* 归档/恢复以整棵树为单位: 子任务不单独变化。
|
||||
* 即使调用方传的是子任务 id, 也提升到根父任务处理。
|
||||
*/
|
||||
async function loadTreeIds(task) {
|
||||
const rootId = task.parentId ? task.parentId : task.id
|
||||
const children = await prisma.task.findMany({ where: { parentId: rootId }, select: { id: true } })
|
||||
return [rootId, ...children.map((c) => c.id)]
|
||||
}
|
||||
|
||||
// POST /api/tasks/:id/archive 手动归档: 连带全部子任务, 幂等
|
||||
router.post('/tasks/:id/archive', async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.user.sub
|
||||
const found = await loadTaskForUser(req.params.id, userId)
|
||||
if (!found) return res.status(404).json({ error: '任务不存在或无权限' })
|
||||
const { task } = found
|
||||
const children = await prisma.task.findMany({ where: { parentId: task.id }, select: { id: true } })
|
||||
const ids = await loadTreeIds(found.task)
|
||||
const r = await prisma.task.updateMany({
|
||||
where: { id: { in: [task.id, ...children.map((c) => c.id)] } },
|
||||
where: { id: { in: ids } },
|
||||
data: { archived: true, archivedAt: new Date(), archiveSource: 'manual' },
|
||||
})
|
||||
res.json({ ok: true, count: r.count })
|
||||
@@ -95,10 +101,9 @@ router.post('/tasks/:id/unarchive', async (req, res, next) => {
|
||||
const userId = req.user.sub
|
||||
const found = await loadTaskForUser(req.params.id, userId)
|
||||
if (!found) return res.status(404).json({ error: '任务不存在或无权限' })
|
||||
const { task } = found
|
||||
const children = await prisma.task.findMany({ where: { parentId: task.id }, select: { id: true } })
|
||||
const ids = await loadTreeIds(found.task)
|
||||
const r = await prisma.task.updateMany({
|
||||
where: { id: { in: [task.id, ...children.map((c) => c.id)] } },
|
||||
where: { id: { in: ids } },
|
||||
data: { archived: false, archivedAt: null, archiveSource: null },
|
||||
})
|
||||
res.json({ ok: true, count: r.count })
|
||||
@@ -135,7 +140,12 @@ router.get('/projects/:id/tasks/archived', async (req, res, next) => {
|
||||
|
||||
const rows = await prisma.task.findMany({
|
||||
where,
|
||||
include: TASK_INCLUDE,
|
||||
// 已归档任务整树归档: 子任务也是 archived=true, 这里要一并取出,
|
||||
// 否则「彻底删除」弹窗无法提示连带条数
|
||||
include: {
|
||||
...TASK_INCLUDE,
|
||||
children: { ...TASK_INCLUDE.children, where: { archived: true } },
|
||||
},
|
||||
orderBy: [{ archivedAt: 'desc' }, { id: 'desc' }],
|
||||
take: limit + 1,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user