Files
insurance/server/src/routes/diagnosis.ts
T
chpark f78949c21a
Build & Deploy / build-and-deploy (push) Failing after 9s
feat: 실제 동작하는 백엔드 + DB + 카카오 로그인
Backend (server/):
- Fastify + Prisma + PostgreSQL 16
- JWT 인증 (bcrypt) + 카카오 OAuth (/auth/kakao — kapi.kakao.com 호출)
- REST API: auth, users, family, policies, claims, score, notifications, diagnosis, consults
- 실제 보험점수 알고리즘 (카테고리별 가중치·최소보장 기반)
- Multipart 업로드 (영수증/진단서 → 디스크 persistence)
- Swagger UI /docs

Client:
- api/client.ts + api/endpoints.ts (fetch 래퍼 + AsyncStorage 토큰)
- 인증 스토어 (hydrate/login/register/kakao/logout)
- 로그인/회원가입 화면 + 카카오 버튼
- 홈/내보험/가족/점수/청구 API 연동 (pull-to-refresh)
- 보험 추가 모달 + 가족 구성원 추가 모달
- 로그인 전/후 스택 분기 (RootNavigator)

Infra:
- docker-compose.yml (로컬 Postgres+API)
- server/Dockerfile (Prisma migrate deploy + node)
- deploy/k8s/postgres.yaml (StatefulSet + 10Gi PVC)
- deploy/k8s/api.yaml (Deployment + Ingress api.insurance.junggomoa.com)
- CI workflow 확장 (web + api 동시 빌드·배포)
- POSTGRES_PASSWORD / JWT_SECRET Gitea Secrets 추가 필요
- 반응형 웹 레이아웃 (max-width 480px 폰 프레임)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 00:32:44 +09:00

62 lines
1.9 KiB
TypeScript

import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { computeScore } from './score';
const DiagnosisBody = z.object({
answers: z.record(z.string(), z.string()),
});
export async function diagnosisRoutes(app: FastifyInstance) {
app.addHook('onRequest', app.authenticate);
app.get('/', async (req) => {
return app.prisma.diagnosis.findMany({
where: { userId: req.user.sub },
orderBy: { createdAt: 'desc' },
take: 10,
});
});
app.post('/', async (req, reply) => {
const body = DiagnosisBody.parse(req.body);
const score = await computeScore(app, req.user.sub);
const recommendations = recommend(body.answers, score.breakdown);
const saved = await app.prisma.diagnosis.create({
data: {
userId: req.user.sub,
answers: body.answers,
score: score.total,
breakdown: { categories: score.breakdown, recommendations },
},
});
reply.code(201);
return saved;
});
}
function recommend(answers: Record<string, string>, breakdown: Array<{ label: string; status: string }>) {
const recs: Array<{ name: string; reason: string; priority: '필수' | '권장' | '선택' }> = [];
const missing = breakdown.filter((b) => b.status === 'none' || b.status === 'bad');
missing.forEach((m) => {
recs.push({
name: m.label,
reason: `현재 ${m.status === 'none' ? '미가입' : '보장 부족'}`,
priority: m.label === '실손보험' ? '필수' : '권장',
});
});
if (answers.family === '있음') {
recs.push({ name: '암보험 진단비 강화', reason: '가족력 있음', priority: '필수' });
}
if (answers.smoke === '흡연') {
recs.push({ name: '뇌혈관/심장 특약', reason: '흡연자 고위험군', priority: '권장' });
}
if (answers.hospital === '3회 이상') {
recs.push({ name: '입원일당 특약', reason: '잦은 입원 이력', priority: '권장' });
}
return recs.slice(0, 6);
}