기능구현중
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
import { queryOne, query } from '../../../utils/db'
|
||||
|
||||
/**
|
||||
* 프로젝트 상세 조회
|
||||
* GET /api/project/[id]/detail
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const projectId = getRouterParam(event, 'id')
|
||||
|
||||
const project = await queryOne<any>(`
|
||||
SELECT p.*, b.business_name, b.business_code
|
||||
FROM wr_project_info p
|
||||
LEFT JOIN wr_business b ON p.business_id = b.business_id
|
||||
WHERE p.project_id = $1
|
||||
`, [projectId])
|
||||
|
||||
if (!project) {
|
||||
throw createError({ statusCode: 404, message: '프로젝트를 찾을 수 없습니다.' })
|
||||
}
|
||||
|
||||
// 현재 PM/PL
|
||||
const managers = await query(`
|
||||
SELECT * FROM wr_project_manager_current WHERE project_id = $1
|
||||
`, [projectId])
|
||||
|
||||
const pm = managers.find((m: any) => m.role_type === 'PM')
|
||||
const pl = managers.find((m: any) => m.role_type === 'PL')
|
||||
|
||||
return {
|
||||
project: {
|
||||
projectId: project.project_id,
|
||||
projectCode: project.project_code,
|
||||
projectName: project.project_name,
|
||||
projectType: project.project_type || 'SI',
|
||||
clientName: project.client_name,
|
||||
projectDescription: project.project_description,
|
||||
startDate: project.start_date,
|
||||
endDate: project.end_date,
|
||||
contractAmount: project.contract_amount,
|
||||
projectStatus: project.project_status,
|
||||
businessId: project.business_id,
|
||||
businessName: project.business_name,
|
||||
businessCode: project.business_code,
|
||||
createdAt: project.created_at,
|
||||
updatedAt: project.updated_at,
|
||||
currentPm: pm ? { employeeId: pm.employee_id, employeeName: pm.employee_name } : null,
|
||||
currentPl: pl ? { employeeId: pl.employee_id, employeeName: pl.employee_name } : null
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,58 +0,0 @@
|
||||
import { execute, queryOne, insertReturning } from '../../../utils/db'
|
||||
import { formatDate } from '../../../utils/week-calc'
|
||||
import { getClientIp } from '../../../utils/ip'
|
||||
import { getCurrentUserEmail } from '../../../utils/user'
|
||||
|
||||
interface AssignManagerBody {
|
||||
employeeId: number
|
||||
roleType: 'PM' | 'PL'
|
||||
startDate?: string
|
||||
changeReason?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* PM/PL 지정 (기존 담당자 종료 + 신규 등록)
|
||||
* POST /api/project/[id]/manager-assign
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const projectId = getRouterParam(event, 'id')
|
||||
const body = await readBody<AssignManagerBody>(event)
|
||||
const clientIp = getClientIp(event)
|
||||
const userEmail = await getCurrentUserEmail(event)
|
||||
|
||||
if (!body.employeeId || !body.roleType) {
|
||||
throw createError({ statusCode: 400, message: '담당자와 역할을 선택해주세요.' })
|
||||
}
|
||||
|
||||
if (!['PM', 'PL'].includes(body.roleType)) {
|
||||
throw createError({ statusCode: 400, message: '역할은 PM 또는 PL만 가능합니다.' })
|
||||
}
|
||||
|
||||
const today = formatDate(new Date())
|
||||
const startDate = body.startDate || today
|
||||
|
||||
// 기존 담당자 종료 (end_date가 NULL인 경우)
|
||||
await execute(`
|
||||
UPDATE wr_project_manager_history SET
|
||||
end_date = $1,
|
||||
change_reason = COALESCE(change_reason || ' / ', '') || '신규 담당자 지정으로 종료',
|
||||
updated_at = NOW(),
|
||||
updated_ip = $2,
|
||||
updated_email = $3
|
||||
WHERE project_id = $4 AND role_type = $5 AND end_date IS NULL
|
||||
`, [startDate, clientIp, userEmail, projectId, body.roleType])
|
||||
|
||||
// 신규 담당자 등록
|
||||
const history = await insertReturning(`
|
||||
INSERT INTO wr_project_manager_history (
|
||||
project_id, employee_id, role_type, start_date, change_reason,
|
||||
created_ip, created_email, updated_ip, updated_email
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $6, $7)
|
||||
RETURNING *
|
||||
`, [projectId, body.employeeId, body.roleType, startDate, body.changeReason || null, clientIp, userEmail])
|
||||
|
||||
return {
|
||||
success: true,
|
||||
historyId: history.history_id
|
||||
}
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { query } from '../../../utils/db'
|
||||
|
||||
/**
|
||||
* 프로젝트 PM/PL 이력 조회
|
||||
* GET /api/project/[id]/manager-history
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const projectId = getRouterParam(event, 'id')
|
||||
|
||||
const history = await query(`
|
||||
SELECT h.*, e.employee_name, e.employee_email
|
||||
FROM wr_project_manager_history h
|
||||
JOIN wr_employee_info e ON h.employee_id = e.employee_id
|
||||
WHERE h.project_id = $1
|
||||
ORDER BY h.role_type, h.start_date DESC
|
||||
`, [projectId])
|
||||
|
||||
return history.map((h: any) => ({
|
||||
historyId: h.history_id,
|
||||
projectId: h.project_id,
|
||||
employeeId: h.employee_id,
|
||||
employeeName: h.employee_name,
|
||||
employeeEmail: h.employee_email,
|
||||
roleType: h.role_type,
|
||||
startDate: h.start_date,
|
||||
endDate: h.end_date,
|
||||
changeReason: h.change_reason,
|
||||
createdAt: h.created_at
|
||||
}))
|
||||
})
|
||||
@@ -1,71 +0,0 @@
|
||||
import { execute, queryOne } from '../../../utils/db'
|
||||
import { getClientIp } from '../../../utils/ip'
|
||||
import { getCurrentUserEmail } from '../../../utils/user'
|
||||
|
||||
interface UpdateProjectBody {
|
||||
projectName?: string
|
||||
projectType?: string
|
||||
clientName?: string
|
||||
projectDescription?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
contractAmount?: number
|
||||
projectStatus?: string
|
||||
businessId?: number | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 프로젝트 정보 수정
|
||||
* PUT /api/project/[id]/update
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const projectId = getRouterParam(event, 'id')
|
||||
const body = await readBody<UpdateProjectBody>(event)
|
||||
const clientIp = getClientIp(event)
|
||||
const userEmail = await getCurrentUserEmail(event)
|
||||
|
||||
const existing = await queryOne<any>(`
|
||||
SELECT * FROM wr_project_info WHERE project_id = $1
|
||||
`, [projectId])
|
||||
|
||||
if (!existing) {
|
||||
throw createError({ statusCode: 404, message: '프로젝트를 찾을 수 없습니다.' })
|
||||
}
|
||||
|
||||
// 프로젝트 유형 검증
|
||||
if (body.projectType && !['SI', 'SM'].includes(body.projectType)) {
|
||||
throw createError({ statusCode: 400, message: '프로젝트 유형은 SI 또는 SM이어야 합니다.' })
|
||||
}
|
||||
|
||||
await execute(`
|
||||
UPDATE wr_project_info SET
|
||||
project_name = $1,
|
||||
project_type = $2,
|
||||
client_name = $3,
|
||||
project_description = $4,
|
||||
start_date = $5,
|
||||
end_date = $6,
|
||||
contract_amount = $7,
|
||||
project_status = $8,
|
||||
business_id = $9,
|
||||
updated_at = NOW(),
|
||||
updated_ip = $10,
|
||||
updated_email = $11
|
||||
WHERE project_id = $12
|
||||
`, [
|
||||
body.projectName ?? existing.project_name,
|
||||
body.projectType ?? existing.project_type ?? 'SI',
|
||||
body.clientName ?? existing.client_name,
|
||||
body.projectDescription ?? existing.project_description,
|
||||
body.startDate ?? existing.start_date,
|
||||
body.endDate ?? existing.end_date,
|
||||
body.contractAmount ?? existing.contract_amount,
|
||||
body.projectStatus ?? existing.project_status,
|
||||
body.businessId !== undefined ? body.businessId : existing.business_id,
|
||||
clientIp,
|
||||
userEmail,
|
||||
projectId
|
||||
])
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
@@ -1,94 +0,0 @@
|
||||
import { query, insertReturning } from '../../utils/db'
|
||||
import { getClientIp } from '../../utils/ip'
|
||||
import { getCurrentUserEmail } from '../../utils/user'
|
||||
|
||||
interface CreateProjectBody {
|
||||
projectName: string
|
||||
projectType?: string // SI / SM
|
||||
clientName?: string
|
||||
projectDescription?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
contractAmount?: number
|
||||
businessId?: number | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 프로젝트 코드 자동 생성 (년도-일련번호)
|
||||
*/
|
||||
async function generateProjectCode(): Promise<string> {
|
||||
const year = new Date().getFullYear()
|
||||
const prefix = `${year}-`
|
||||
|
||||
// 해당 연도의 마지막 코드 조회
|
||||
const result = await query<{ project_code: string }>(`
|
||||
SELECT project_code FROM wr_project_info
|
||||
WHERE project_code LIKE $1
|
||||
ORDER BY project_code DESC
|
||||
LIMIT 1
|
||||
`, [`${prefix}%`])
|
||||
|
||||
let nextNum = 1
|
||||
if (result.length > 0 && result[0].project_code) {
|
||||
const lastCode = result[0].project_code
|
||||
const lastNum = parseInt(lastCode.split('-')[1]) || 0
|
||||
nextNum = lastNum + 1
|
||||
}
|
||||
|
||||
return `${prefix}${String(nextNum).padStart(3, '0')}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 프로젝트 등록
|
||||
* POST /api/project/create
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<CreateProjectBody>(event)
|
||||
const clientIp = getClientIp(event)
|
||||
const userEmail = await getCurrentUserEmail(event)
|
||||
|
||||
if (!body.projectName) {
|
||||
throw createError({ statusCode: 400, message: '프로젝트명을 입력해주세요.' })
|
||||
}
|
||||
|
||||
// 프로젝트 유형 검증
|
||||
const projectType = body.projectType || 'SI'
|
||||
if (!['SI', 'SM'].includes(projectType)) {
|
||||
throw createError({ statusCode: 400, message: '프로젝트 유형은 SI 또는 SM이어야 합니다.' })
|
||||
}
|
||||
|
||||
// 프로젝트 코드 자동 생성
|
||||
const projectCode = await generateProjectCode()
|
||||
|
||||
const project = await insertReturning(`
|
||||
INSERT INTO wr_project_info (
|
||||
project_code, project_name, project_type, client_name, project_description,
|
||||
start_date, end_date, contract_amount, business_id,
|
||||
created_ip, created_email, updated_ip, updated_email
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $10, $11)
|
||||
RETURNING *
|
||||
`, [
|
||||
projectCode,
|
||||
body.projectName,
|
||||
projectType,
|
||||
body.clientName || null,
|
||||
body.projectDescription || null,
|
||||
body.startDate || null,
|
||||
body.endDate || null,
|
||||
body.contractAmount || null,
|
||||
body.businessId || null,
|
||||
clientIp,
|
||||
userEmail
|
||||
])
|
||||
|
||||
return {
|
||||
success: true,
|
||||
project: {
|
||||
projectId: project.project_id,
|
||||
projectCode: project.project_code,
|
||||
projectName: project.project_name,
|
||||
projectType: project.project_type,
|
||||
businessId: project.business_id
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,70 +0,0 @@
|
||||
import { query } from '../../utils/db'
|
||||
|
||||
/**
|
||||
* 프로젝트 목록 조회
|
||||
* GET /api/project/list
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const queryParams = getQuery(event)
|
||||
const status = queryParams.status as string || null
|
||||
const businessId = queryParams.businessId ? Number(queryParams.businessId) : null
|
||||
|
||||
let sql = `
|
||||
SELECT p.*,
|
||||
b.business_name,
|
||||
(SELECT employee_name FROM wr_employee_info e
|
||||
JOIN wr_project_manager_history pm ON e.employee_id = pm.employee_id
|
||||
WHERE pm.project_id = p.project_id AND pm.role_type = 'PM'
|
||||
AND (pm.end_date IS NULL OR pm.end_date >= CURRENT_DATE)
|
||||
LIMIT 1) as pm_name,
|
||||
(SELECT employee_name FROM wr_employee_info e
|
||||
JOIN wr_project_manager_history pm ON e.employee_id = pm.employee_id
|
||||
WHERE pm.project_id = p.project_id AND pm.role_type = 'PL'
|
||||
AND (pm.end_date IS NULL OR pm.end_date >= CURRENT_DATE)
|
||||
LIMIT 1) as pl_name
|
||||
FROM wr_project_info p
|
||||
LEFT JOIN wr_business b ON p.business_id = b.business_id
|
||||
`
|
||||
|
||||
const conditions: string[] = []
|
||||
const params: any[] = []
|
||||
let paramIndex = 1
|
||||
|
||||
if (status) {
|
||||
conditions.push(`p.project_status = $${paramIndex++}`)
|
||||
params.push(status)
|
||||
}
|
||||
|
||||
if (businessId) {
|
||||
conditions.push(`p.business_id = $${paramIndex++}`)
|
||||
params.push(businessId)
|
||||
}
|
||||
|
||||
if (conditions.length > 0) {
|
||||
sql += ' WHERE ' + conditions.join(' AND ')
|
||||
}
|
||||
|
||||
sql += ' ORDER BY p.created_at DESC'
|
||||
|
||||
const projects = await query(sql, params)
|
||||
|
||||
return {
|
||||
projects: projects.map((p: any) => ({
|
||||
projectId: p.project_id,
|
||||
projectCode: p.project_code,
|
||||
projectName: p.project_name,
|
||||
projectType: p.project_type || 'SI',
|
||||
clientName: p.client_name,
|
||||
projectDescription: p.project_description,
|
||||
startDate: p.start_date,
|
||||
endDate: p.end_date,
|
||||
contractAmount: p.contract_amount,
|
||||
projectStatus: p.project_status,
|
||||
businessId: p.business_id,
|
||||
businessName: p.business_name,
|
||||
pmName: p.pm_name,
|
||||
plName: p.pl_name,
|
||||
createdAt: p.created_at
|
||||
}))
|
||||
}
|
||||
})
|
||||
@@ -1,33 +0,0 @@
|
||||
import { query } from '../../utils/db'
|
||||
import { requireAuth } from '../../utils/session'
|
||||
|
||||
/**
|
||||
* 내가 보고서 작성한 프로젝트 목록
|
||||
* GET /api/project/my-projects
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const userId = await requireAuth(event)
|
||||
|
||||
// 내가 주간보고를 작성한 프로젝트 + 전체 활성 프로젝트
|
||||
const projects = await query(`
|
||||
SELECT DISTINCT p.*,
|
||||
CASE WHEN t.project_id IS NOT NULL THEN true ELSE false END as has_my_report
|
||||
FROM wr_project_info p
|
||||
LEFT JOIN (
|
||||
SELECT DISTINCT t.project_id
|
||||
FROM wr_weekly_report_task t
|
||||
JOIN wr_weekly_report r ON t.report_id = r.report_id
|
||||
WHERE r.author_id = $1
|
||||
) t ON p.project_id = t.project_id
|
||||
WHERE p.project_status = 'ACTIVE'
|
||||
ORDER BY has_my_report DESC, p.project_name
|
||||
`, [userId])
|
||||
|
||||
return projects.map((p: any) => ({
|
||||
projectId: p.project_id,
|
||||
projectCode: p.project_code,
|
||||
projectName: p.project_name,
|
||||
clientName: p.client_name,
|
||||
hasMyReport: p.has_my_report
|
||||
}))
|
||||
})
|
||||
Reference in New Issue
Block a user