feat: 완료 신청 API 엔드포인트 구현
🔧 백엔드 API 추가: - POST /api/issues/{issue_id}/completion-request - 완료 사진 Base64 업로드 및 저장 - 완료 코멘트 저장 - 완료 신청 시간 및 신청자 기록 📊 데이터 모델 확장: - completion_requested_at: 완료 신청 시간 - completion_requested_by_id: 완료 신청자 ID - completion_photo_path: 완료 사진 경로 - completion_comment: 완료 코멘트 🛡️ 검증 로직: - 진행 중 상태만 완료 신청 가능 - 중복 완료 신청 방지 - 파일 업로드 오류 시 롤백 처리 🔄 프론트엔드 연동: - refreshDashboard() 함수 추가 - 완료 신청 후 현황판 자동 새로고침 - 오류 처리 및 사용자 피드백 Expected Result: ✅ 완료 신청 API 정상 작동 ✅ 완료 사진 업로드 및 저장 ✅ 완료 대기 상태로 변경 ✅ 404 오류 해결
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -138,6 +138,12 @@ class Issue(Base):
|
|||||||
additional_info_updated_at = Column(DateTime) # 추가 정보 입력 시간
|
additional_info_updated_at = Column(DateTime) # 추가 정보 입력 시간
|
||||||
additional_info_updated_by_id = Column(Integer, ForeignKey("users.id")) # 추가 정보 입력자
|
additional_info_updated_by_id = Column(Integer, ForeignKey("users.id")) # 추가 정보 입력자
|
||||||
|
|
||||||
|
# 완료 신청 관련 필드들
|
||||||
|
completion_requested_at = Column(DateTime) # 완료 신청 시간
|
||||||
|
completion_requested_by_id = Column(Integer, ForeignKey("users.id")) # 완료 신청자
|
||||||
|
completion_photo_path = Column(String(500)) # 완료 사진 경로
|
||||||
|
completion_comment = Column(Text) # 완료 코멘트
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
reporter = relationship("User", back_populates="issues", foreign_keys=[reporter_id])
|
reporter = relationship("User", back_populates="issues", foreign_keys=[reporter_id])
|
||||||
reviewer = relationship("User", foreign_keys=[reviewed_by_id], overlaps="reviewed_issues")
|
reviewer = relationship("User", foreign_keys=[reviewed_by_id], overlaps="reviewed_issues")
|
||||||
|
|||||||
@@ -147,6 +147,12 @@ class Issue(IssueBase):
|
|||||||
additional_info_updated_at: Optional[datetime] = None # 추가 정보 입력 시간
|
additional_info_updated_at: Optional[datetime] = None # 추가 정보 입력 시간
|
||||||
additional_info_updated_by_id: Optional[int] = None # 추가 정보 입력자
|
additional_info_updated_by_id: Optional[int] = None # 추가 정보 입력자
|
||||||
|
|
||||||
|
# 완료 신청 관련 필드들
|
||||||
|
completion_requested_at: Optional[datetime] = None # 완료 신청 시간
|
||||||
|
completion_requested_by_id: Optional[int] = None # 완료 신청자
|
||||||
|
completion_photo_path: Optional[str] = None # 완료 사진 경로
|
||||||
|
completion_comment: Optional[str] = None # 완료 코멘트
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
@@ -189,6 +195,11 @@ class AdditionalInfoUpdateRequest(BaseModel):
|
|||||||
responsible_person_detail: Optional[str] = None # 해당자 상세 정보
|
responsible_person_detail: Optional[str] = None # 해당자 상세 정보
|
||||||
cause_detail: Optional[str] = None # 원인 상세 정보
|
cause_detail: Optional[str] = None # 원인 상세 정보
|
||||||
|
|
||||||
|
class CompletionRequestRequest(BaseModel):
|
||||||
|
"""완료 신청 요청"""
|
||||||
|
completion_photo: str # 완료 사진 (Base64)
|
||||||
|
completion_comment: Optional[str] = None # 완료 코멘트
|
||||||
|
|
||||||
class InboxIssue(BaseModel):
|
class InboxIssue(BaseModel):
|
||||||
"""수신함용 부적합 정보 (간소화된 버전)"""
|
"""수신함용 부적합 정보 (간소화된 버전)"""
|
||||||
id: int
|
id: int
|
||||||
|
|||||||
Binary file not shown.
@@ -4,7 +4,7 @@ from typing import List, Optional
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from database.database import get_db
|
from database.database import get_db
|
||||||
from database.models import Issue, IssueStatus, User, UserRole
|
from database.models import Issue, IssueStatus, User, UserRole, ReviewStatus
|
||||||
from database import schemas
|
from database import schemas
|
||||||
from routers.auth import get_current_user, get_current_admin
|
from routers.auth import get_current_user, get_current_admin
|
||||||
from routers.page_permissions import check_page_access
|
from routers.page_permissions import check_page_access
|
||||||
@@ -288,3 +288,58 @@ async def update_issue_management(
|
|||||||
"issue_id": issue.id,
|
"issue_id": issue.id,
|
||||||
"updated_fields": list(update_data.keys())
|
"updated_fields": list(update_data.keys())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@router.post("/{issue_id}/completion-request")
|
||||||
|
async def request_completion(
|
||||||
|
issue_id: int,
|
||||||
|
request: schemas.CompletionRequestRequest,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
완료 신청 - 담당자가 작업 완료 후 완료 신청
|
||||||
|
"""
|
||||||
|
# 이슈 조회
|
||||||
|
issue = db.query(Issue).filter(Issue.id == issue_id).first()
|
||||||
|
if not issue:
|
||||||
|
raise HTTPException(status_code=404, detail="부적합을 찾을 수 없습니다.")
|
||||||
|
|
||||||
|
# 진행 중 상태인지 확인
|
||||||
|
if issue.review_status != ReviewStatus.in_progress:
|
||||||
|
raise HTTPException(status_code=400, detail="진행 중 상태의 부적합만 완료 신청할 수 있습니다.")
|
||||||
|
|
||||||
|
# 이미 완료 신청된 경우 확인
|
||||||
|
if issue.completion_requested_at:
|
||||||
|
raise HTTPException(status_code=400, detail="이미 완료 신청된 부적합입니다.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 완료 사진 저장
|
||||||
|
completion_photo_path = None
|
||||||
|
if request.completion_photo:
|
||||||
|
completion_photo_path = save_base64_image(request.completion_photo, "completion")
|
||||||
|
|
||||||
|
# 완료 신청 정보 업데이트
|
||||||
|
issue.completion_requested_at = datetime.now()
|
||||||
|
issue.completion_requested_by_id = current_user.id
|
||||||
|
issue.completion_photo_path = completion_photo_path
|
||||||
|
issue.completion_comment = request.completion_comment
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.refresh(issue)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"message": "완료 신청이 성공적으로 제출되었습니다.",
|
||||||
|
"issue_id": issue.id,
|
||||||
|
"completion_requested_at": issue.completion_requested_at,
|
||||||
|
"completion_photo_path": completion_photo_path
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db.rollback()
|
||||||
|
# 업로드된 파일이 있다면 삭제
|
||||||
|
if completion_photo_path:
|
||||||
|
try:
|
||||||
|
delete_file(completion_photo_path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
raise HTTPException(status_code=500, detail=f"완료 신청 처리 중 오류가 발생했습니다: {str(e)}")
|
||||||
|
|||||||
@@ -828,6 +828,17 @@
|
|||||||
console.log('✅ API 스크립트 로드 완료 (issues-dashboard.html)');
|
console.log('✅ API 스크립트 로드 완료 (issues-dashboard.html)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 현황판 새로고침 함수
|
||||||
|
function refreshDashboard() {
|
||||||
|
// 현재 선택된 프로젝트 기준으로 다시 로드
|
||||||
|
const selectedProject = document.getElementById('projectFilter').value;
|
||||||
|
if (selectedProject) {
|
||||||
|
filterByProject();
|
||||||
|
} else {
|
||||||
|
loadDashboardData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 완료 신청 관련 함수들
|
// 완료 신청 관련 함수들
|
||||||
let selectedCompletionIssueId = null;
|
let selectedCompletionIssueId = null;
|
||||||
let completionPhotoBase64 = null;
|
let completionPhotoBase64 = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user