기능구현중

This commit is contained in:
2026-01-11 13:59:43 +09:00
parent d56154d5d2
commit 10a5dd5f7f
8 changed files with 876 additions and 25 deletions

View File

@@ -0,0 +1,90 @@
import { query } from '../../utils/db'
import { requireAuth } from '../../utils/session'
/**
* 내 주간 커밋 조회 (주간보고 작성용)
* GET /api/commits/my-weekly
*
* Query params:
* - projectId: 프로젝트 ID (필수)
* - startDate: 주 시작일 (YYYY-MM-DD)
* - endDate: 주 종료일 (YYYY-MM-DD)
*/
export default defineEventHandler(async (event) => {
const employeeId = await requireAuth(event)
const queryParams = getQuery(event)
const projectId = queryParams.projectId ? parseInt(queryParams.projectId as string) : null
const startDate = queryParams.startDate as string
const endDate = queryParams.endDate as string
if (!projectId) {
throw createError({ statusCode: 400, message: '프로젝트 ID가 필요합니다.' })
}
// 조건 빌드
const conditions = [
'r.project_id = $1',
'c.employee_id = $2'
]
const values: any[] = [projectId, employeeId]
let paramIndex = 3
if (startDate) {
conditions.push(`c.commit_date >= $${paramIndex++}`)
values.push(startDate)
}
if (endDate) {
conditions.push(`c.commit_date <= $${paramIndex++}::date + INTERVAL '1 day'`)
values.push(endDate)
}
const whereClause = conditions.join(' AND ')
// 내 커밋 목록
const commits = await query(`
SELECT
c.commit_id, c.commit_hash, c.commit_message, c.commit_date,
c.files_changed, c.insertions, c.deletions,
r.repo_id, r.repo_name,
s.server_type
FROM wr_commit_log c
JOIN wr_repository r ON c.repo_id = r.repo_id
JOIN wr_vcs_server s ON r.server_id = s.server_id
WHERE ${whereClause}
ORDER BY c.commit_date DESC
LIMIT 100
`, values)
// 통계
const statsResult = await query(`
SELECT
COUNT(*) as commit_count,
COALESCE(SUM(c.insertions), 0) as total_insertions,
COALESCE(SUM(c.deletions), 0) as total_deletions
FROM wr_commit_log c
JOIN wr_repository r ON c.repo_id = r.repo_id
WHERE ${whereClause}
`, values)
return {
commits: commits.map(c => ({
commitId: c.commit_id,
commitHash: c.commit_hash?.substring(0, 8),
commitMessage: c.commit_message,
commitDate: c.commit_date,
filesChanged: c.files_changed,
insertions: c.insertions,
deletions: c.deletions,
repoId: c.repo_id,
repoName: c.repo_name,
serverType: c.server_type
})),
stats: {
commitCount: parseInt(statsResult[0]?.commit_count || '0'),
totalInsertions: parseInt(statsResult[0]?.total_insertions || '0'),
totalDeletions: parseInt(statsResult[0]?.total_deletions || '0')
}
}
})

View File

@@ -0,0 +1,131 @@
import { query } from '../../../utils/db'
import { requireAuth } from '../../../utils/session'
/**
* 프로젝트 커밋 목록 조회
* GET /api/project/[id]/commits
*
* Query params:
* - startDate: 시작일 (YYYY-MM-DD)
* - endDate: 종료일 (YYYY-MM-DD)
* - repoId: 저장소 ID (옵션)
* - authorId: 작성자 ID (옵션)
* - page: 페이지 번호 (기본 1)
* - limit: 페이지당 개수 (기본 50)
*/
export default defineEventHandler(async (event) => {
await requireAuth(event)
const projectId = parseInt(getRouterParam(event, 'id') || '0')
const queryParams = getQuery(event)
if (!projectId) {
throw createError({ statusCode: 400, message: '프로젝트 ID가 필요합니다.' })
}
const startDate = queryParams.startDate as string
const endDate = queryParams.endDate as string
const repoId = queryParams.repoId ? parseInt(queryParams.repoId as string) : null
const authorId = queryParams.authorId ? parseInt(queryParams.authorId as string) : null
const page = parseInt(queryParams.page as string) || 1
const limit = Math.min(parseInt(queryParams.limit as string) || 50, 100)
const offset = (page - 1) * limit
// 조건 빌드
const conditions = ['r.project_id = $1']
const values: any[] = [projectId]
let paramIndex = 2
if (startDate) {
conditions.push(`c.commit_date >= $${paramIndex++}`)
values.push(startDate)
}
if (endDate) {
conditions.push(`c.commit_date <= $${paramIndex++}::date + INTERVAL '1 day'`)
values.push(endDate)
}
if (repoId) {
conditions.push(`c.repo_id = $${paramIndex++}`)
values.push(repoId)
}
if (authorId) {
conditions.push(`c.employee_id = $${paramIndex++}`)
values.push(authorId)
}
const whereClause = conditions.join(' AND ')
// 커밋 목록 조회
const commits = await query(`
SELECT
c.commit_id, c.commit_hash, c.commit_message, c.commit_author, c.commit_email,
c.commit_date, c.employee_id, c.files_changed, c.insertions, c.deletions,
r.repo_id, r.repo_name, r.repo_path,
s.server_type, s.server_name,
e.employee_name, e.display_name
FROM wr_commit_log c
JOIN wr_repository r ON c.repo_id = r.repo_id
JOIN wr_vcs_server s ON r.server_id = s.server_id
LEFT JOIN wr_employee_info e ON c.employee_id = e.employee_id
WHERE ${whereClause}
ORDER BY c.commit_date DESC
LIMIT $${paramIndex++} OFFSET $${paramIndex++}
`, [...values, limit, offset])
// 전체 개수 조회
const countResult = await query(`
SELECT COUNT(*) as total
FROM wr_commit_log c
JOIN wr_repository r ON c.repo_id = r.repo_id
WHERE ${whereClause}
`, values)
const total = parseInt(countResult[0]?.total || '0')
// 통계 (해당 기간)
const statsResult = await query(`
SELECT
COUNT(*) as commit_count,
COALESCE(SUM(c.insertions), 0) as total_insertions,
COALESCE(SUM(c.deletions), 0) as total_deletions,
COUNT(DISTINCT c.employee_id) as author_count
FROM wr_commit_log c
JOIN wr_repository r ON c.repo_id = r.repo_id
WHERE ${whereClause}
`, values)
return {
commits: commits.map(c => ({
commitId: c.commit_id,
commitHash: c.commit_hash,
commitMessage: c.commit_message,
commitAuthor: c.commit_author,
commitEmail: c.commit_email,
commitDate: c.commit_date,
employeeId: c.employee_id,
employeeName: c.display_name || c.employee_name,
filesChanged: c.files_changed,
insertions: c.insertions,
deletions: c.deletions,
repoId: c.repo_id,
repoName: c.repo_name,
repoPath: c.repo_path,
serverType: c.server_type,
serverName: c.server_name
})),
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit)
},
stats: {
commitCount: parseInt(statsResult[0]?.commit_count || '0'),
totalInsertions: parseInt(statsResult[0]?.total_insertions || '0'),
totalDeletions: parseInt(statsResult[0]?.total_deletions || '0'),
authorCount: parseInt(statsResult[0]?.author_count || '0')
}
}
})

View File

@@ -0,0 +1,25 @@
import { requireAuth } from '../../../../utils/session'
import { syncProjectGitRepositories } from '../../../../utils/git-sync'
/**
* 프로젝트 커밋 새로고침 (모든 저장소 동기화)
* POST /api/project/[id]/commits/refresh
*/
export default defineEventHandler(async (event) => {
await requireAuth(event)
const projectId = parseInt(getRouterParam(event, 'id') || '0')
if (!projectId) {
throw createError({ statusCode: 400, message: '프로젝트 ID가 필요합니다.' })
}
const result = await syncProjectGitRepositories(projectId)
return {
success: result.success,
message: result.success
? `${result.results.length}개 저장소 동기화 완료`
: '일부 저장소 동기화 실패',
results: result.results
}
})

View File

@@ -0,0 +1,38 @@
import { queryOne } from '../../../utils/db'
import { requireAuth } from '../../../utils/session'
import { syncGitRepository } from '../../../utils/git-sync'
/**
* 저장소 동기화 (수동)
* POST /api/repository/[id]/sync
*/
export default defineEventHandler(async (event) => {
await requireAuth(event)
const repoId = parseInt(getRouterParam(event, 'id') || '0')
if (!repoId) {
throw createError({ statusCode: 400, message: '저장소 ID가 필요합니다.' })
}
// 저장소 정보 확인
const repo = await queryOne(`
SELECT r.*, s.server_type
FROM wr_repository r
JOIN wr_vcs_server s ON r.server_id = s.server_id
WHERE r.repo_id = $1
`, [repoId])
if (!repo) {
throw createError({ statusCode: 404, message: '저장소를 찾을 수 없습니다.' })
}
if (repo.server_type === 'GIT') {
const result = await syncGitRepository(repoId)
return result
} else if (repo.server_type === 'SVN') {
// SVN은 별도 구현 예정
return { success: false, message: 'SVN 동기화는 준비 중입니다.' }
}
return { success: false, message: '지원하지 않는 서버 타입입니다.' }
})