기능구현중

This commit is contained in:
2026-01-11 14:41:41 +09:00
parent 0205e8d437
commit 17852cc5dc
7 changed files with 434 additions and 115 deletions

View File

@@ -0,0 +1,100 @@
import { query, queryOne, insertReturning } from '../../../../utils/db'
import { requireAuth } from '../../../../utils/session'
import { getValidGoogleToken } from '../../../../utils/google-token'
/**
* 주간보고 그룹 공유 (Gmail 발송)
* POST /api/report/weekly/[id]/share
*/
export default defineEventHandler(async (event) => {
const user = await requireAuth(event)
const reportId = parseInt(getRouterParam(event, 'id') || '0')
const body = await readBody(event)
if (!reportId) {
throw createError({ statusCode: 400, message: '보고서 ID가 필요합니다.' })
}
const groupIds = body.groupIds as number[]
if (!groupIds?.length) {
throw createError({ statusCode: 400, message: '공유할 그룹을 선택해주세요.' })
}
// 보고서 조회
const report = await queryOne<any>(`
SELECT r.*, e.employee_name, e.employee_email,
p.project_name, p.project_code
FROM wr_weekly_report r
JOIN wr_employee_info e ON r.employee_id = e.employee_id
LEFT JOIN wr_project_info p ON r.project_id = p.project_id
WHERE r.report_id = $1
`, [reportId])
if (!report) {
throw createError({ statusCode: 404, message: '보고서를 찾을 수 없습니다.' })
}
// Google 토큰 확인
const accessToken = await getValidGoogleToken(user.employeeId)
if (!accessToken) {
throw createError({ statusCode: 401, message: 'Google 계정 연결이 필요합니다.' })
}
// 선택된 그룹 조회
const groups = await query<any>(`
SELECT group_id, group_email, group_name
FROM wr_google_group WHERE group_id = ANY($1) AND is_active = true
`, [groupIds])
if (!groups.length) {
throw createError({ statusCode: 400, message: '유효한 그룹이 없습니다.' })
}
// 이메일 제목 및 본문 생성
const weekInfo = `${report.report_year}${report.report_week}주차`
const subject = `[주간보고] ${report.project_name || '개인'} - ${weekInfo} (${report.employee_name})`
const emailBody = buildEmailBody(report)
// 각 그룹에 발송
const results: any[] = []
for (const group of groups) {
try {
const rawEmail = createRawEmail({
to: group.group_email, subject, body: emailBody, from: user.employeeEmail
})
const sendRes = await fetch('https://gmail.googleapis.com/gmail/v1/users/me/messages/send', {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ raw: rawEmail })
})
if (sendRes.ok) {
results.push({ groupId: group.group_id, groupName: group.group_name, success: true })
} else {
const err = await sendRes.json()
results.push({ groupId: group.group_id, groupName: group.group_name, success: false, error: err.error?.message })
}
} catch (e: any) {
results.push({ groupId: group.group_id, groupName: group.group_name, success: false, error: e.message })
}
}
return { success: results.some(r => r.success), message: `${results.filter(r => r.success).length}/${groups.length}개 그룹에 공유됨`, results }
})
function buildEmailBody(report: any): string {
return `<html><body style="font-family:sans-serif;line-height:1.6">
<h2>📋 주간보고 - ${report.report_year}${report.report_week}주차</h2>
<p><b>작성자:</b> ${report.employee_name} | <b>프로젝트:</b> ${report.project_name || '개인'}</p>
<hr><h3>📌 금주 실적</h3><div style="background:#f5f5f5;padding:15px;border-radius:5px">${(report.this_week_work || '').replace(/\n/g, '<br>')}</div>
<h3>📅 차주 계획</h3><div style="background:#f5f5f5;padding:15px;border-radius:5px">${(report.next_week_plan || '').replace(/\n/g, '<br>')}</div>
${report.issues ? `<h3>⚠️ 이슈</h3><div style="background:#fff3cd;padding:15px;border-radius:5px">${report.issues.replace(/\n/g, '<br>')}</div>` : ''}
<hr><p style="color:#666;font-size:12px">주간업무보고 시스템에서 발송</p></body></html>`
}
function createRawEmail(opts: { to: string; subject: string; body: string; from: string }): string {
const email = [`From: ${opts.from}`, `To: ${opts.to}`, `Subject: =?UTF-8?B?${Buffer.from(opts.subject).toString('base64')}?=`, 'MIME-Version: 1.0', 'Content-Type: text/html; charset=UTF-8', '', opts.body].join('\r\n')
return Buffer.from(email).toString('base64url')
}