- 数据: Project.autoArchive + Task.archived/archivedAt/archiveSource, 迁移 20261026000000_task_archive (两索引) - 领域: server/task-domain/archive.js —— hiddenAtOf 与保留口径精确同源, autoArchiveDue 到期判定, runAutoArchiveForProject (读时惰性) + runAutoArchiveSweep (每小时 cron 兜底) - 接口: POST /tasks/:id/archive|unarchive (连带子任务, 幂等); GET /projects/:id/tasks/archived (keyword/kind/columnId/from/to 过滤, archivedAt+id 游标分页); 项目 PUT 收 autoArchive (枚举校验); 主列表/子任务/项目 taskCount 全部排除归档 - 前端: ⋯ 菜单「归档」(确认含子任务条数); 项目栏「已归档」入口 → ArchivedTasks 弹层 (关键词/类型/列/归档时段过滤 + 加载更多 + 恢复/删除); 项目管理「归档设置」四档 Picker
This commit is contained in:
@@ -41,6 +41,9 @@ export async function ensurePersonalProject(userId) {
|
||||
// 任务类型标签: 项目级自定义, 未配置时用默认四类
|
||||
export const DEFAULT_TASK_KINDS = ['需求', 'Bug', 'FEATURE', '优化']
|
||||
|
||||
// 自动归档档位 (T-ARC-02)
|
||||
export const AUTO_ARCHIVE_OPTIONS = ['3d', '1w', '1m', 'never']
|
||||
|
||||
export function normTaskKinds(v) {
|
||||
if (!Array.isArray(v)) return null
|
||||
const out = []
|
||||
@@ -63,6 +66,7 @@ function projectSummary(p) {
|
||||
updatedAt: p.updatedAt,
|
||||
taskKinds: normTaskKinds(p.taskKinds) || DEFAULT_TASK_KINDS,
|
||||
icon: p.icon || null,
|
||||
autoArchive: AUTO_ARCHIVE_OPTIONS.includes(p.autoArchive) ? p.autoArchive : 'never',
|
||||
memberCount: p._count?.members ?? undefined,
|
||||
taskCount: p._count?.tasks ?? undefined,
|
||||
}
|
||||
@@ -77,7 +81,7 @@ router.get('/', async (req, res, next) => {
|
||||
await ensurePersonalProject(userId)
|
||||
const rows = await prisma.project.findMany({
|
||||
where: { members: { some: { userId } } },
|
||||
include: { _count: { select: { members: true, tasks: true } } },
|
||||
include: { _count: { select: { members: true, tasks: { where: { archived: false } } } } },
|
||||
orderBy: [{ archived: 'asc' }, { updatedAt: 'desc' }],
|
||||
})
|
||||
res.json({ items: rows.map(projectSummary) })
|
||||
@@ -98,7 +102,7 @@ router.post('/', async (req, res, next) => {
|
||||
await createDefaultColumns(project.id)
|
||||
const full = await prisma.project.findUnique({
|
||||
where: { id: project.id },
|
||||
include: { _count: { select: { members: true, tasks: true } } },
|
||||
include: { _count: { select: { members: true, tasks: { where: { archived: false } } } } },
|
||||
})
|
||||
res.json({ item: projectSummary(full) })
|
||||
} catch (e) { next(e) }
|
||||
@@ -135,7 +139,7 @@ router.put('/:id', async (req, res, next) => {
|
||||
const userId = req.user.sub
|
||||
const project = await loadProjectForUser(req.params.id, userId)
|
||||
if (!project) return res.status(404).json({ error: '项目不存在或无权限' })
|
||||
const { name, archived, notes, taskKinds, icon } = req.body || {}
|
||||
const { name, archived, notes, taskKinds, icon, autoArchive } = req.body || {}
|
||||
const data = {}
|
||||
if (name !== undefined) {
|
||||
const t = String(name).trim()
|
||||
@@ -159,6 +163,11 @@ router.put('/:id', async (req, res, next) => {
|
||||
} else if (icon === null) {
|
||||
data.icon = null
|
||||
}
|
||||
// 自动归档档位 (T-ARC-02)
|
||||
if (autoArchive !== undefined) {
|
||||
if (!AUTO_ARCHIVE_OPTIONS.includes(autoArchive)) return res.status(400).json({ error: '自动归档档位非法' })
|
||||
data.autoArchive = autoArchive
|
||||
}
|
||||
if (archived !== undefined) data.archived = !!archived
|
||||
// 项目信息块 (T-PJ-13): 任意多个 { title, content }, 整体替换
|
||||
if (notes !== undefined) {
|
||||
|
||||
Reference in New Issue
Block a user