feat(528): 신규 프로젝트 그룹 공통 백엔드 — 종류 칸 · 산림청 판정 · D01 · E01 생성 API · 공유 GET 만 · 가입 go.kr = 산림청

- DB 020(추가만): projects 종류 · 사업연도 끝 · 설계속도 · 원본 칸 · plan_route_years · plan_route_assignees · 회사 「산림청」 행
- is_forest_email · is_forest_service · /api/auth/session 에 is_forest_service
- require_project_access 한 곳 — 지정 회사 = GET · HEAD 만 · 그 밖 403
- POST /api/d01/project(산림청만 · PLAN · 설계속도 기본 = resolve_design_speed) · GET /api/dashboard/plan-routes/assigned
- GET /api/e01/source-candidates · POST /api/e01/project(B06 완료만 · STABILITY · 원본 칸 · 자료 복사는 528-4)
- 종류별 단계 시드(PLAN 3 · STABILITY 4) · storage 허용 D0x · E0x · B01 목록에 project_kind
- 가입 인증 — 메일 *.go.kr = 회사 산림청 · ACTIVE · 화면 회사 칸 「산림청」 잠금 표시
- 지킴 시험 test_528_project_groups.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 19:32:25 +09:00
co-authored by Claude Opus 5.5
parent bc39953fbd
commit ff8a8bad08
14 changed files with 692 additions and 15 deletions
+59
View File
@@ -0,0 +1,59 @@
-- 020_project_groups.sql
-- 신규 프로젝트 그룹 — 종류 · 예정노선 연도 구간 · 지정 업체 · 산림청 회사 (요구 345 · 347-8)
--
-- 추가만 함 — 기존 행 = ROAD(임도설계) · 기존 칸 · 동작 그대로.
-- 예정노선 시작 · 종료 누가거리는 기존 route_start_m · route_end_m 을 씀.
USE aislo_db;
ALTER TABLE projects
ADD COLUMN IF NOT EXISTS project_kind VARCHAR(20) NOT NULL DEFAULT 'ROAD'
COMMENT 'ROAD 임도설계 · PLAN 예정노선 · EROSION 사방설계 · STABILITY 안정성평가' AFTER road_type,
ADD COLUMN IF NOT EXISTS project_year_end INT NULL
COMMENT '사업연도 범위 끝(예정노선) — 시작은 project_year' AFTER project_year,
ADD COLUMN IF NOT EXISTS design_speed_kph INT NULL
COMMENT '설계속도(km/h · 예정노선 등록값)' AFTER project_year_end,
ADD COLUMN IF NOT EXISTS source_project_id CHAR(36) NULL
COMMENT '원본 프로젝트(E = 임도 · 임도 = 예정노선)',
ADD COLUMN IF NOT EXISTS source_copied_at DATETIME NULL COMMENT '원본 복사 시각',
ADD COLUMN IF NOT EXISTS source_fingerprint CHAR(40) NULL COMMENT '복사 때 원본 지문(sha1)';
CREATE TABLE IF NOT EXISTS plan_route_years (
project_id CHAR(36) NOT NULL COMMENT 'projects.id (PLAN)',
year INT NOT NULL,
start_x DOUBLE NULL,
start_y DOUBLE NULL,
end_x DOUBLE NULL,
end_y DOUBLE NULL,
epsg INT NULL,
route_json JSON NULL COMMENT '분석 결과 노선',
fingerprint CHAR(40) NULL COMMENT '노선 내용 지문(sha1)',
confirmed_at DATETIME NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (project_id, year),
CONSTRAINT fk_plan_route_years_project FOREIGN KEY (project_id)
REFERENCES projects (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='예정노선 연도 구간';
CREATE TABLE IF NOT EXISTS plan_route_assignees (
id INT AUTO_INCREMENT PRIMARY KEY,
project_id CHAR(36) NOT NULL COMMENT 'projects.id (PLAN)',
year INT NOT NULL,
company_id INT NULL COMMENT '등록 회사 — NULL 이면 수기 업체',
company_name VARCHAR(255) NULL,
email VARCHAR(255) NULL,
notified_at DATETIME NULL,
linked_project_id CHAR(36) NULL COMMENT '업체가 연결한 임도 프로젝트',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_plan_route_assignees_project (project_id, year),
KEY idx_plan_route_assignees_company (company_id),
CONSTRAINT fk_plan_route_assignees_project FOREIGN KEY (project_id)
REFERENCES projects (id) ON DELETE CASCADE,
CONSTRAINT fk_plan_route_assignees_company FOREIGN KEY (company_id)
REFERENCES companies (id) ON DELETE SET NULL,
CONSTRAINT fk_plan_route_assignees_linked FOREIGN KEY (linked_project_id)
REFERENCES projects (id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='예정노선 연도별 지정 업체';
-- 산림청 회사 — 메일 *.go.kr 가입자가 자동으로 붙음.
INSERT IGNORE INTO companies (name, company_code, status) VALUES ('산림청', 'FOREST', 'ACTIVE');