@@ -265,8 +265,12 @@ async def create_ticket(
|
||||
]
|
||||
request_payload = TicketCreateRequest(
|
||||
workspace_code=workspace_code,
|
||||
requester_id=str(form.get("requester_id") or ""),
|
||||
requester_tenant_id=str(form.get("requester_tenant_id") or ""),
|
||||
# Multipart writers do not send requester identity fields. Use
|
||||
# the verified SSO principal before Pydantic validates the
|
||||
# required fields; applying model_copy afterwards is too late
|
||||
# because empty values fail min_length validation first.
|
||||
requester_id=principal.user_id,
|
||||
requester_tenant_id=principal.tenant_id,
|
||||
requester_contact=str(form.get("requester_contact") or ""),
|
||||
title=str(form.get("title") or ""),
|
||||
description=str(form.get("description") or ""),
|
||||
|
||||
@@ -41,6 +41,9 @@ class Settings(BaseSettings):
|
||||
jwt_secret: str = ""
|
||||
admin_candidate_tenant_id: str = ""
|
||||
initial_super_admin_phone_number: str = ""
|
||||
# Users without an explicit workspace role receive END_USER access to
|
||||
# this public feedback workspace.
|
||||
default_support_workspace_code: str = "EGBIM_DEMO"
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ class WorkspaceFormField(BaseModel):
|
||||
label: str
|
||||
field_type: str
|
||||
required: bool = False
|
||||
options: list[dict[str, str | int]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class WorkspaceFormTemplateResponse(BaseModel):
|
||||
|
||||
@@ -9,6 +9,7 @@ from app.db.models import (
|
||||
UserWorkspaceAccess,
|
||||
Workspace,
|
||||
)
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
class AccessService:
|
||||
@@ -36,19 +37,25 @@ class AccessService:
|
||||
|
||||
@staticmethod
|
||||
def default_user_workspace_code(db: Session) -> str | None:
|
||||
"""Return the first active ABC project workspace.
|
||||
"""Return the configured public feedback workspace.
|
||||
|
||||
The old Q&A_Platform seed workspace must not determine the first SSO
|
||||
redirect after ABC projects are synchronized.
|
||||
Users without an explicit role assignment receive END_USER access to
|
||||
this workspace. A configured code prevents old seed data or a newly
|
||||
created project from changing the public writer target.
|
||||
"""
|
||||
return db.execute(
|
||||
configured_code = settings.default_support_workspace_code.strip()
|
||||
query = (
|
||||
select(Workspace.workspace_code)
|
||||
.where(
|
||||
Workspace.is_active.is_(True),
|
||||
Workspace.workspace_type == "SOFTWARE_APP",
|
||||
)
|
||||
.order_by(Workspace.id.asc()),
|
||||
).scalar()
|
||||
)
|
||||
if configured_code:
|
||||
query = query.where(Workspace.workspace_code == configured_code)
|
||||
else:
|
||||
query = query.order_by(Workspace.id.asc())
|
||||
return db.execute(query).scalar()
|
||||
|
||||
@staticmethod
|
||||
def _upsert_support_user(
|
||||
|
||||
@@ -39,6 +39,11 @@ from app.schemas.ticket import (
|
||||
|
||||
SEOUL_TIMEZONE = ZoneInfo("Asia/Seoul")
|
||||
QNA_CATEGORY_CODES = {"ERROR_QNA", "IMPROVEMENT_QNA", "GENERAL_QNA"}
|
||||
QNA_CATEGORY_OPTIONS = [
|
||||
{"id": 1, "key": "ERROR_QNA", "name": "오류 문의"},
|
||||
{"id": 2, "key": "IMPROVEMENT_QNA", "name": "개선 문의"},
|
||||
{"id": 3, "key": "GENERAL_QNA", "name": "일반 문의"},
|
||||
]
|
||||
access_service = AccessService()
|
||||
|
||||
|
||||
@@ -84,6 +89,20 @@ class TicketService:
|
||||
def get_workspace_form_template(self, db: Session, workspace_code: str) -> WorkspaceFormTemplateResponse:
|
||||
workspace = self._get_workspace_by_code(db, workspace_code, raise_not_found=False)
|
||||
|
||||
qna_fields = [
|
||||
WorkspaceFormField(
|
||||
field_code="category",
|
||||
label="구분",
|
||||
field_type="select",
|
||||
required=True,
|
||||
options=QNA_CATEGORY_OPTIONS,
|
||||
),
|
||||
WorkspaceFormField(field_code="title", label="제목", field_type="text", required=True),
|
||||
WorkspaceFormField(field_code="description", label="내용", field_type="textarea", required=True),
|
||||
WorkspaceFormField(field_code="ip_address", label="사용자 IP 주소", field_type="text"),
|
||||
WorkspaceFormField(field_code="mac_address", label="MAC 주소", field_type="text"),
|
||||
]
|
||||
|
||||
templates = {
|
||||
"INTRA_BOOK_REQUEST": WorkspaceFormTemplateResponse(
|
||||
workspace_code="INTRA_BOOK_REQUEST",
|
||||
@@ -113,6 +132,12 @@ class TicketService:
|
||||
WorkspaceFormField(field_code="expected_result", label="기대 결과", field_type="textarea"),
|
||||
],
|
||||
),
|
||||
"EGBIM_DEMO": WorkspaceFormTemplateResponse(
|
||||
workspace_code="EGBIM_DEMO",
|
||||
workspace_name=workspace.workspace_name if workspace else "EGBIM_DEMO",
|
||||
requires_approval=False,
|
||||
fields=qna_fields,
|
||||
),
|
||||
}
|
||||
|
||||
return templates.get(
|
||||
@@ -1910,7 +1935,7 @@ class TicketService:
|
||||
if not normalized or normalized == "GENERAL":
|
||||
return self._default_category_code(workspace_code)
|
||||
|
||||
if workspace_code == "Q&A_Platform" and normalized not in QNA_CATEGORY_CODES:
|
||||
if workspace_code in {"Q&A_Platform", "EGBIM_DEMO"} and normalized not in QNA_CATEGORY_CODES:
|
||||
return "GENERAL_QNA"
|
||||
|
||||
return normalized
|
||||
|
||||
Reference in New Issue
Block a user