backend: M2 배치/규정검증(PostGIS)·M3 설계·M4 배선/견적·M5 RenderJob + 인증(login/workspaces/accept-invite) 매퍼 배선으로 501 해소. M2 자동배치 3안 생성→선택/병합→규정 재검증. 계약 확장(Violation 좌표·LayoutDto.hall·AutoLayoutOption.booths, additive NON_NULL). 신규 HallMapper·UtilityOrderMapper(기존 테이블). gradlew build 통과. frontend: SCR-02 대시보드·SCR-06 설계스튜디오(4샷+Before/After)·SCR-07 배선뷰(적/청/녹)·SCR-12 갤러리 + CompareSlider(reroomai)·WiringCanvas. tsc/vite build EXIT 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
86 lines
6.7 KiB
Markdown
86 lines
6.7 KiB
Markdown
# 킨텍스 백엔드 — M2~M5 매퍼 배선 + 계약 확장 (Phase C, D-4)
|
||
|
||
> 작성: kintex-backend-dev · 근거: `_workspace/01_backend_contracts.md`·`_workspace/02_db_schema.md`·`_workspace/02_frontend_progress.md`(계약 공백 5건)·IMPLEMENTATION_BACKLOG C-M2~C-M5
|
||
> 대상: **frontend·qa 대조**. 이번 회차는 db-engineer가 완성한 MyBatis 매퍼에 ServiceImpl을 배선해 **M2~M5·인증의 501(NOT_IMPLEMENTED)을 200으로 전환**하고, 프론트가 지적한 계약 공백을 **additive**(하위호환)로 보강한다. 스키마 마이그레이션·2FA·프론트·build.gradle 미변경. 커밋 없음(파일만).
|
||
|
||
---
|
||
|
||
## 1. 501 → 200 전환된 엔드포인트
|
||
|
||
| 모듈 | 메서드 | 엔드포인트 | 배선 요지 |
|
||
|---|---|---|---|
|
||
| 인증 | login | `POST /api/auth/login` | `UserMapper.findAuthByEmail` + BCrypt 대조 + `findEventRoles`→토큰·워크스페이스 |
|
||
| 인증 | listWorkspaces | `GET /api/auth/workspaces` | `findEventRoles(userId)`→WorkspaceDto[] |
|
||
| 인증 | acceptInvite | `POST /api/auth/accept-invite` | 등록업체 검증(`findCompanyRegistered`)→토큰 재발급. ★초대코드 매핑은 후속(아래 TODO) |
|
||
| M2 | getLayout | `GET …/halls/{hallId}/layout` | `findLayout`+`findBooths`(GeoJSON 파싱)+`HallMapper`+검증 요약 |
|
||
| M2 | saveLayout | `PUT …/layout` | 폴리곤→WKT `upsertLayout`+`replaceBooths`, 저장 후 규정 재검증 반영 |
|
||
| M2 | validate | `POST …/layout/validate` | `minAisleWidth`·`countExitsBlocked`+부스 최댓값→룰엔진 M2 |
|
||
| M2 | autoGenerate | `POST …/layout/auto-generate` | 제약 기반 그리드 패킹으로 **정확히 optionCount(기본 3)안** + S7 발행 |
|
||
| M2 | applyOption | `POST …/layout/apply-option` **(신규)** | 선택/병합 부스열→새 버전 저장+규정 재검증 |
|
||
| M3 | getDesign | `GET …/booths/{boothId}/design` | `findDesign`+spec(jsonb) 역직렬화 |
|
||
| M3 | saveDesign | `PUT …/design` | spec 직렬화 `upsertDesign`(버전 next), 최신본 반환 |
|
||
| M4 | computeWiring | `POST …/utility/wiring` | `isAssumedTrench`+단말별 `shortestPath`(LineString 파싱)·색상 규약 |
|
||
| M4 | submitOrder | `POST …/utility/order` | `quote`+`UtilityOrderMapper.insertOrder`(영속)+위치표시도 URL·마감/릴레이 고지 |
|
||
| M4 | getOrder | `GET …/utility` | `UtilityOrderMapper.findOrder`(부스 최신) |
|
||
| M5 | listByBooth | `GET …/render-jobs` | `RenderJobMapper.findByBooth`→갤러리 목록(워터마크 항상 포함) |
|
||
| M5 | publish(보강) | `POST …/render` | 쿼터 정본을 `countSucceededByEvent`(DB DONE)로 전환 + `insertJob` 내구 이력 |
|
||
| M5 | callback(보강) | `POST /api/internal/render/callback` | `updateStatus` 내구 갱신(쿼터는 DONE 집계에서 자동) |
|
||
|
||
---
|
||
|
||
## 2. 계약 확장 (additive · 하위호환 · 프론트 gap 해소)
|
||
|
||
모두 `@JsonInclude(NON_NULL)`로 **기존 필드 불변, 신규 nullable 필드만 추가**.
|
||
|
||
### 2-1. gap #1 — 위반 오버레이 좌표 (`rules.Violation`)
|
||
- 추가 필드: `boothId`(String, nullable) · `anchor`([x,y] 미터, nullable).
|
||
- **부스 단위 규칙(HEIGHT_MAX·FLOOR_LOAD)** 위반에 한해 해당 부스 id + 폴리곤 중심 앵커를 부여. 레이아웃 단위(AISLE_WIDTH_MIN·EXIT_ACCESS)는 null(캔버스에서 목록 핀만).
|
||
- 프론트: `violations[].anchor` 존재 시 캔버스 핀 위치로 사용, 없으면 기존처럼 목록 핀만.
|
||
|
||
```json
|
||
{ "pin":1, "code":"HEIGHT_MAX", "severity":"block", "measured":"측정 5.4m",
|
||
"boothId":"b-102", "anchor":[3.0,1.5] }
|
||
```
|
||
|
||
### 2-2. gap #2 — 홀 치수·트렌치 (`m2.dto.LayoutDto.hall` = `HallInfo`)
|
||
- `LayoutDto`에 `hall` 필드 추가(신규 `HallInfo`). 캔버스 하드코딩(126×90m·트렌치 그리드) 제거 근거.
|
||
- `HallInfo`: `hallId·label·dimsM[w,d]·ceilingM·targetBoothCount·floorLoadTPerM2·assumedTrench·trenches[]·exits[]`.
|
||
- `HallInfo.Trench`: `{trenchId,x,y,assumed,supplies[power|water|air|network|gas]}` · `HallInfo.Exit`: `{x,y,clearanceM}`.
|
||
- 원천: 신규 `HallMapper`(hall·trench·hall_exit read-only). `assumedTrench=true`면 SCR-07 "가정 트렌치" 배지(PLANNING R4).
|
||
|
||
```json
|
||
"hall": { "hallId":"H7","label":"제2전시장 홀7","dimsM":[126,90],"ceilingM":12,
|
||
"targetBoothCount":510,"floorLoadTPerM2":5.0,"assumedTrench":true,
|
||
"trenches":[{"trenchId":"H7-T-3-3","x":3,"y":3,"assumed":true,"supplies":["power","water","air","network","gas"]}],
|
||
"exits":[{"x":63,"y":0,"clearanceM":3.0}] }
|
||
```
|
||
|
||
### 2-3. gap #3 — 자동배치 안의 부스 + 적용 경로 (`AutoLayoutOption.booths` + apply-option)
|
||
- `AutoLayoutOption`에 `booths`(BoothDto[]) 추가 — "이 안으로 편집 시작"/병합 소스.
|
||
- 신규 `POST …/layout/apply-option` (ORGANIZER): body `ApplyOptionRequest {name, booths[]}` → 새 버전 저장 + 규정 재검증된 `LayoutDto`.
|
||
- **3안 흐름 마감**: auto-generate(3안+각 booths) → 클라 선택/구역 병합(booths 합성) → apply-option(영속+재검증). 단일 안 적용도 동일 엔드포인트.
|
||
|
||
---
|
||
|
||
## 3. backend 추가 매퍼 (db-engineer 계약 §3 밖 — 기존 테이블 read/write, 마이그레이션 불변)
|
||
> db-engineer 인수·정본화 요청. 모두 V2~V5 기존 테이블만 사용.
|
||
|
||
| 매퍼 | 메서드 | 테이블 |
|
||
|---|---|---|
|
||
| `m2.mapper.HallMapper` | findHall·findTrenches·findExits | hall·trench·hall_exit (read-only) |
|
||
| `m4.mapper.UtilityOrderMapper` | insertOrder·findOrder | utility_order (V3) |
|
||
| `auth.mapper.UserMapper`(+1) | findCompanyRegistered | company (read-only) |
|
||
|
||
---
|
||
|
||
## 4. 남은 TODO (경계·후속)
|
||
- **초대코드→행사/부스/역할 매핑**: invite 테이블·매퍼 미제공 → acceptInvite는 등록업체 게이트 + 현재 역할 재발급까지만. 역할 upsert는 매핑 매퍼 인수 시 배선(db-engineer).
|
||
- **위치표시도 PDF(M4-3)**: submitOrder는 신청 영속 + 자산 URL(`…/utility/location-diagram`)·고지 반환. 실제 PDF 렌더는 문서 큐(후속). 현재 해당 GET 자산은 미구현(URL만).
|
||
- **wiring 지오메트리 영속**: computeWiring은 산출·반환만. utility_order.wiring(MultiLineString) 저장은 후속.
|
||
- **자동배치 최적화**: 현재 결정적 그리드 패킹(3안=통로폭/규격 변주, 무위반 골격). 제약 솔버 고도화는 후속(흐름은 성립).
|
||
- **트렌치 실측(R4)**: 가정 그리드 유지(is_assumed=true). CAD 추출 교체 시 HallInfo.assumedTrench=false 자동 반영.
|
||
|
||
## 5. 검증
|
||
- `./gradlew compileJava` · `./gradlew build -x test` 통과. 매퍼 id/namespace 정합 확인. PostgreSQL 미기동으로 런타임 왕복은 dev 배포 후.
|
||
- 보안 불변 유지: password_hash/otp 응답 제외, 스택트레이스 미노출(요약 메시지), AI 이미지 워터마크·고지 항상 포함, 워커 에러 요약화.
|