diff --git a/backend/alembic/versions/dd9d72cfd3c4_003_upgrade_term_table.py b/backend/alembic/versions/dd9d72cfd3c4_003_upgrade_term_table.py new file mode 100644 index 00000000..4eb2da4f --- /dev/null +++ b/backend/alembic/versions/dd9d72cfd3c4_003_upgrade_term_table.py @@ -0,0 +1,35 @@ +"""003_Upgrade_Term_Table + +Revision ID: dd9d72cfd3c4 +Revises: 1c8bcc7e25c8 +Create Date: 2025-04-27 14:56:29.185812 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes + + +# revision identifiers, used by Alembic. +revision = 'dd9d72cfd3c4' +down_revision = '1c8bcc7e25c8' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('terms', 'id', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + existing_nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('terms', 'id', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + existing_nullable=False) + # ### end Alembic commands ### diff --git a/backend/apps/settings/api/terminology.py b/backend/apps/settings/api/terminology.py index c7716d5a..62b6a1a3 100644 --- a/backend/apps/settings/api/terminology.py +++ b/backend/apps/settings/api/terminology.py @@ -1,5 +1,7 @@ -from fastapi import APIRouter +from fastapi import APIRouter +from sqlmodel import select from apps.settings.models.setting_models import term_model +from apps.settings.schemas.setting_schemas import term_schema_creator from common.core.deps import SessionDep from common.core.pagination import Paginator from common.core.schemas import PaginatedResponse, PaginationParams @@ -18,4 +20,49 @@ async def pager( return await paginator.get_paginated_response( model=term_model, pagination=pagination, - **filters) \ No newline at end of file + **filters) + + +@router.get("/{id}", response_model=term_model) +async def get_terminology_by_id( + session: SessionDep, + id: int +): + term = session.get(term_model, id) + return term + +@router.post("", response_model=term_model) +async def add_terminology( + session: SessionDep, + creator: term_schema_creator +): + terminology = term_model(**creator.model_dump()) + session.add(terminology) + session.commit() + return terminology + +@router.put("", response_model=term_model) +async def update_terminology( + session: SessionDep, + terminology: term_model +): + terminology.id = int(terminology.id) + term = session.exec(select(term_model).where(term_model.id == terminology.id)).first() + update_data = terminology.model_dump(exclude_unset=True) + for field, value in update_data.items(): + setattr(term, field, value) + session.add(term) + session.commit() + return terminology + +@router.delete("/{id}", response_model=term_model) +async def delete_terminology( + session: SessionDep, + id: int +): + term = session.exec(select(term_model).where(term_model.id == id)).first() + session.delete(term) + session.commit() + return { + "message": f"Terminology with ID {id} deleted successfully." + } diff --git a/backend/apps/settings/models/setting_models.py b/backend/apps/settings/models/setting_models.py index 83fc5a60..29de7c85 100644 --- a/backend/apps/settings/models/setting_models.py +++ b/backend/apps/settings/models/setting_models.py @@ -1,10 +1,10 @@ from sqlmodel import Field, SQLModel +from common.core.models import SnowflakeBase -class term_model(SQLModel, table=True): + +class term_model(SnowflakeBase, table=True): __tablename__ = "terms" - - id: int = Field(primary_key=True, index=True) term: str = Field(max_length=255) definition: str = Field(max_length=255) domain: str = Field(max_length=255) diff --git a/backend/apps/settings/schemas/setting_schemas.py b/backend/apps/settings/schemas/setting_schemas.py index a4b11401..01378647 100644 --- a/backend/apps/settings/schemas/setting_schemas.py +++ b/backend/apps/settings/schemas/setting_schemas.py @@ -1,7 +1,6 @@ from pydantic import BaseModel -class term_schema(BaseModel): - id: int +class term_schema_creator(BaseModel): term: str definition: str domain: str diff --git a/backend/common/core/models.py b/backend/common/core/models.py new file mode 100644 index 00000000..57718314 --- /dev/null +++ b/backend/common/core/models.py @@ -0,0 +1,33 @@ +from pydantic import field_validator +from sqlmodel import BigInteger, SQLModel, Field +from typing import Optional + +from common.utils.snowflake import snowflake + +class SnowflakeBase(SQLModel): + id: Optional[int] = Field( + default_factory=snowflake.generate_id, + primary_key=True, + sa_type=BigInteger(), + index=True, + nullable=False + ) + + @field_validator("id", mode="before") + def validate_id(cls, v): + if isinstance(v, str): + if not v: + return None + try: + return int(v) + except ValueError: + raise ValueError("Invalid bigint string") + elif isinstance(v, int): + return v + raise TypeError("BigInt must be int or string") + + + class Config: + json_encoders = { + int: lambda v: str(v) if v > 2**53-1 else v + } \ No newline at end of file diff --git a/backend/common/utils/snowflake.py b/backend/common/utils/snowflake.py new file mode 100644 index 00000000..5f75e8fa --- /dev/null +++ b/backend/common/utils/snowflake.py @@ -0,0 +1,57 @@ +import time +import threading +class SnowflakeGenerator: + def __init__(self, worker_id=0, datacenter_id=0): + self.worker_id = worker_id + self.datacenter_id = datacenter_id + self.sequence = 0 + self.last_timestamp = -1 + self.lock = threading.Lock() + + self.worker_id_bits = 5 + self.datacenter_id_bits = 5 + self.max_worker_id = -1 ^ (-1 << self.worker_id_bits) + self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits) + self.sequence_bits = 12 + + self.worker_id_shift = self.sequence_bits + self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits + self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits + self.sequence_mask = -1 ^ (-1 << self.sequence_bits) + + if self.worker_id > self.max_worker_id or self.worker_id < 0: + raise ValueError(f"worker ID can't be greater than {self.max_worker_id} or less than 0") + if self.datacenter_id > self.max_datacenter_id or self.datacenter_id < 0: + raise ValueError(f"datacenter ID can't be greater than {self.max_datacenter_id} or less than 0") + + def _current_time(self): + return int(time.time() * 1000) + + def _wait_next_millis(self, last_timestamp): + timestamp = self._current_time() + while timestamp <= last_timestamp: + timestamp = self._current_time() + return timestamp + + def generate_id(self): + with self.lock: + timestamp = self._current_time() + + if timestamp < self.last_timestamp: + raise ValueError("Clock moved backwards. Refusing to generate ID") + + if timestamp == self.last_timestamp: + self.sequence = (self.sequence + 1) & self.sequence_mask + if self.sequence == 0: + timestamp = self._wait_next_millis(self.last_timestamp) + else: + self.sequence = 0 + + self.last_timestamp = timestamp + + return ((timestamp << self.timestamp_left_shift) | + (self.datacenter_id << self.datacenter_id_shift) | + (self.worker_id << self.worker_id_shift) | + self.sequence) + +snowflake = SnowflakeGenerator(worker_id=1) \ No newline at end of file diff --git a/backend/scripts/alembic/exec.sh b/backend/scripts/alembic/exec.sh new file mode 100755 index 00000000..35381fd5 --- /dev/null +++ b/backend/scripts/alembic/exec.sh @@ -0,0 +1 @@ +alembic upgrade head \ No newline at end of file diff --git a/backend/scripts/prestart.sh b/backend/scripts/prestart.sh old mode 100644 new mode 100755 diff --git a/backend/scripts/tests-start.sh b/backend/scripts/tests-start.sh old mode 100644 new mode 100755 diff --git a/frontend/auto-imports.d.ts b/frontend/auto-imports.d.ts index 76e1ee65..21b8307c 100644 --- a/frontend/auto-imports.d.ts +++ b/frontend/auto-imports.d.ts @@ -7,4 +7,5 @@ export {} declare global { const ElMessage: typeof import('element-plus/es')['ElMessage'] + const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] } diff --git a/frontend/src/api/setting.ts b/frontend/src/api/setting.ts new file mode 100644 index 00000000..45dc7de5 --- /dev/null +++ b/frontend/src/api/setting.ts @@ -0,0 +1,9 @@ +import { request } from '@/utils/request' + +export const termApi = { + pager: (pageNumber: number, pageSize: number) => request.get(`/settings/terminology/pager/${pageNumber}/${pageSize}`), + add: (data: any) => request.post('/settings/terminology', data), + edit: (data: any) => request.put('/settings/terminology', data), + delete: (id: number) => request.delete(`/settings/terminology/${id}`), + query: (id: number) => request.get(`/settings/terminology/${id}`) +} \ No newline at end of file diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index a9a34309..f6df087d 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -91,11 +91,10 @@ class HttpService { } // Handle business logic - if (response.data?.success !== true) { + /* if (response.data?.success !== true) { return Promise.reject(response.data) - } - - return response.data.data + } */ + return response.data }, async (error: AxiosError) => { const config = error.config as FullRequestConfig & { __retryCount?: number } diff --git a/frontend/src/views/setting/Professional.vue b/frontend/src/views/setting/Professional.vue index 0dbc0076..1db27fba 100644 --- a/frontend/src/views/setting/Professional.vue +++ b/frontend/src/views/setting/Professional.vue @@ -23,7 +23,7 @@ Export Terminology -
+
Add Terminology
@@ -49,48 +49,154 @@
+ + + + + + + + + + + + + + + + + + +