Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
66bd9e0
feat(product_change): add schema for product change requests with val…
MisakaVan May 22, 2025
cda2c2f
feat(product_change): declare CRUD operations for product change requ…
MisakaVan May 22, 2025
6d10483
feat(product_change): implement CRUD operations
MisakaVan May 22, 2025
446f16c
refactor(product_change): replace string literals with enum reference…
MisakaVan May 22, 2025
2bab2b6
refactor(product_change): remove unused PendingStatus from update sta…
MisakaVan May 22, 2025
0232646
test(product_change): add unit tests for ProductChangeRequestCRUD2 me…
MisakaVan May 22, 2025
2ddb8d3
feat(product_change): declare new endpoints and services for product …
MisakaVan May 23, 2025
f1a7a20
refactor(product_change): move ProductStatusApiEnum to product_schema…
MisakaVan May 23, 2025
e159c03
feat(exceptions): add custom exception classes for invalid operations…
MisakaVan May 23, 2025
c820a95
feat(product_change): add method to update change request status to '…
MisakaVan May 23, 2025
0f56603
feat(product_change_request_service): implementation
MisakaVan May 23, 2025
e07e07f
fix(schema): ProductChangeRequestCreate uses correct base
MisakaVan May 23, 2025
594f5bc
test(product_change_request_service_v2): add unit tests for ProductCh…
MisakaVan May 23, 2025
d1c9df2
feat(dependencies): add ProductChangeRequestCRUD2 and ProductChangeRe…
MisakaVan May 23, 2025
75a5153
feat(endpoint/product.c.r): implement v2 endpoints for product_change…
MisakaVan May 23, 2025
1985d57
feat(product_change_request_crud_v2): add Decimal handling and custom…
MisakaVan May 23, 2025
a2a62e2
feat(product_change_request_crud_v2): add optional new_product_id to …
MisakaVan May 24, 2025
2be8ee8
feat(product_change_request_service_v2): enhance logging and add inte…
MisakaVan May 24, 2025
7f07d51
refactor(product_change_request_v2): remove tags from endpoints and u…
MisakaVan May 24, 2025
2edd26a
feat(store_change_request_v2): implement new endpoints and schemas fo…
MisakaVan May 24, 2025
b0398fd
feat(store_change_request_crud_v2): add CRUD operations for store cha…
MisakaVan May 24, 2025
aaaf91c
feat(store_change_request_service_v2): implement store change request…
MisakaVan May 24, 2025
72c4b29
feat(endpoints): implement store change request v2 endpoints
MisakaVan May 24, 2025
59cd07d
test(store_change_request_endpoints_v2): add integration tests for st…
MisakaVan May 24, 2025
5817404
feat(user_crud): add update_user_role method to modify user roles
MisakaVan May 24, 2025
5ae9446
feat(store_change_request_service_v2): update user role to merchant u…
MisakaVan May 24, 2025
a399658
Merge pull request #32 from MisakaVan/refactor-change-request
MisakaVan May 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
382 changes: 382 additions & 0 deletions src/backend/app/api/v1/endpoints/product_change_request_v2.py

Large diffs are not rendered by default.

272 changes: 272 additions & 0 deletions src/backend/app/api/v1/endpoints/store_change_request_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# src/backend/app/api/v1/endpoints/store_change_request_v2.py
import datetime

from fastapi import APIRouter, Depends, HTTPException, status, Path as FastApiPath, Query
from typing import List, Optional

# 依赖项导入
from backend.app.dependencies.auth_deps import get_current_active_user, get_db_connection
from backend.app.dependencies.service_deps import get_store_change_request_service_v2
from backend.app.services.store_change_request_service_v2 import (
StoreChangeRequestService2 as SCRServiceV2,
)

# Schema 导入 - 使用您指定的 v2 文件名和更新后的类名
from backend.app.schemas.user_schema import UserResponse as CurrentUserSchema
from backend.app.schemas.store_change_request_schema_v2 import (
StoreChangeRequestCreate,
StoreChangeRequestResponse,
StoreChangeRequestListResponse,
StoreChangeRequestUpdateRequestByAdmin,
StoreChangeRequestQueryParams,
StoreStatusEnum,
StoreChangeRequestStatusEnum as RequestStatusEnum,
StoreChangeRequestTypeEnum as RequestTypeEnum,
ProposedStoreData,
)

from sqlalchemy.engine.base import Connection
from backend.app.utils import logger
from backend.app.utils.exceptions import (
StoreNotFoundException,
BadRequestException,
PermissionDeniedException,
)


router = APIRouter()


@router.post(
"/",
response_model=StoreChangeRequestResponse,
status_code=status.HTTP_201_CREATED,
summary="创建店铺变更请求",
)
async def submit_store_change_request(
request_in: StoreChangeRequestCreate,
current_user: CurrentUserSchema = Depends(get_current_active_user),
db: Connection = Depends(get_db_connection),
service: SCRServiceV2 = Depends(get_store_change_request_service_v2),
):
"""
创建一个新的店铺变更请求。
需要提供店铺 ID 和变更类型(创建、更新或删除)。
"""
logger.info(f"User {current_user.UserID} is submitting a store change request: {request_in}")
try:
with db.begin_nested() if db.in_transaction() else db.begin():
return await service.submit_new_request(
db, requesting_user=current_user, request_in=request_in,
)
except StoreNotFoundException as e:
logger.error(f"Store not found: {e}")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except BadRequestException as e:
logger.error(f"Bad request: {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except PermissionDeniedException as e:
logger.error(f"Permission denied: {e}")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error"
)


@router.get(
"/{request_id}",
response_model=StoreChangeRequestResponse,
summary="获取单个店铺变更请求",
)
async def get_store_change_request(
request_id: int = FastApiPath(..., description="变更请求的唯一ID"),
current_user: CurrentUserSchema = Depends(get_current_active_user),
db: Connection = Depends(get_db_connection),
service: SCRServiceV2 = Depends(get_store_change_request_service_v2),
):
"""
获取单个店铺变更请求的详细信息。
"""
logger.info(
f"User {current_user.UserID} is retrieving store change request with ID {request_id}"
)
try:
with db.begin_nested() if db.in_transaction() else db.begin():
return await service.get_request_details(
db, change_request_id=request_id, actor_user=current_user,
)
except StoreNotFoundException as e:
logger.error(f"Store not found: {e}")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except BadRequestException as e:
logger.error(f"Bad request: {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except PermissionDeniedException as e:
logger.error(f"Permission denied: {e}")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error"
)


@router.get(
"/list/",
response_model=StoreChangeRequestListResponse,
summary="商家获取店铺变更请求列表",
)
async def list_store_change_requests(
query_params: StoreChangeRequestQueryParams = Depends(),
current_user: CurrentUserSchema = Depends(get_current_active_user),
db: Connection = Depends(get_db_connection),
service: SCRServiceV2 = Depends(get_store_change_request_service_v2),
):
"""
获取店铺变更请求列表,可以按状态、类型等筛选。
"""
logger.info(
f"User {current_user.UserID} is listing store change requests with filters: {query_params}"
)
try:
with db.begin_nested() if db.in_transaction() else db.begin():
return await service.list_requests_for_requesting_user(
db,
requesting_user=current_user,
query_params=query_params,
)
except StoreNotFoundException as e:
logger.error(f"Store not found: {e}")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except BadRequestException as e:
logger.error(f"Bad request: {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except PermissionDeniedException as e:
logger.error(f"Permission denied: {e}")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error"
)


@router.get(
"/list-admin/",
response_model=StoreChangeRequestListResponse,
summary="管理员获取店铺变更请求列表",
)
async def list_store_change_requests_admin(
query_params: StoreChangeRequestQueryParams = Depends(),
current_user: CurrentUserSchema = Depends(get_current_active_user),
db: Connection = Depends(get_db_connection),
service: SCRServiceV2 = Depends(get_store_change_request_service_v2),
):
"""
管理员获取店铺变更请求列表,可以按状态、类型等筛选。
"""
logger.info(
f"Admin {current_user.UserID} is listing store change requests with filters: {query_params}"
)
try:
with db.begin_nested() if db.in_transaction() else db.begin():
return await service.list_requests_for_admin(
db,
admin_user=current_user,
query_params=query_params,
)
except StoreNotFoundException as e:
logger.error(f"Store not found: {e}")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except BadRequestException as e:
logger.error(f"Bad request: {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except PermissionDeniedException as e:
logger.error(f"Permission denied: {e}")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error"
)


@router.delete(
"/{request_id}",
status_code=status.HTTP_204_NO_CONTENT,
summary="删除/取消店铺变更请求",
)
async def cancel_store_change_request(
request_id: int = FastApiPath(..., description="变更请求的唯一ID"),
current_user: CurrentUserSchema = Depends(get_current_active_user),
db: Connection = Depends(get_db_connection),
service: SCRServiceV2 = Depends(get_store_change_request_service_v2),
):
"""
删除或取消一个店铺变更请求。
只有提交请求的用户或管理员可以执行此操作。
"""
logger.info(
f"User {current_user.UserID} is cancelling store change request with ID {request_id}"
)
try:
with db.begin_nested() if db.in_transaction() else db.begin():
return await service.user_cancel_request(
db, change_request_id=request_id, requesting_user=current_user,
)
except StoreNotFoundException as e:
logger.error(f"Store not found: {e}")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except BadRequestException as e:
logger.error(f"Bad request: {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except PermissionDeniedException as e:
logger.error(f"Permission denied: {e}")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error"
)


@router.put(
"/{request_id}/review/",
response_model=StoreChangeRequestResponse,
summary="管理员审核店铺变更请求",
)
async def review_store_change_request(
review_data: StoreChangeRequestUpdateRequestByAdmin,
request_id: int = FastApiPath(..., description="变更请求的唯一ID"),
admin_user: CurrentUserSchema = Depends(get_current_active_user),
db: Connection = Depends(get_db_connection),
service: SCRServiceV2 = Depends(get_store_change_request_service_v2),
):
"""
管理员审核店铺变更请求,更新状态和备注。
"""
logger.info(f"Admin {admin_user.UserID} is reviewing store change request with ID {request_id}")
try:
with db.begin_nested() if db.in_transaction() else db.begin():
return await service.admin_review_request(
db,
change_request_id=request_id,
admin_user=admin_user,
review_data=review_data,
)
except StoreNotFoundException as e:
logger.error(f"Store not found: {e}")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except BadRequestException as e:
logger.error(f"Bad request: {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except PermissionDeniedException as e:
logger.error(f"Permission denied: {e}")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error"
)
8 changes: 6 additions & 2 deletions src/backend/app/api/v1/router.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from fastapi import APIRouter
from .endpoints import user, product, category, auth, cart, address, order, payment, store,\
store_change_request, product_change_request
store_change_request, product_change_request, product_change_request_v2, store_change_request_v2

api_router_v1 = APIRouter()

Expand All @@ -15,4 +15,8 @@
api_router_v1.include_router(payment.router, prefix="/payment", tags=["Payments"])
api_router_v1.include_router(store.router, prefix="/store", tags=["Store"])
api_router_v1.include_router(store_change_request.router, prefix="/store-change", tags=["Store Change Requests"])
api_router_v1.include_router(product_change_request.router, prefix="/product-change", tags=["Product Change Requests"])
api_router_v1.include_router(store_change_request_v2.router, prefix="/store-change-new", tags=["Store Change Requests V2"])
api_router_v1.include_router(product_change_request.router, prefix="/product-change", tags=["Product Change Requests V1"])


api_router_v1.include_router(product_change_request_v2.router, prefix="/product-change-new", tags=["Product Change Requests V2"])
2 changes: 2 additions & 0 deletions src/backend/app/crud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@
from .order_crud import OrderCRUD
from .payment_transaction_crud import PaymentTransactionCRUD
from .store_crud import StoreCRUD
from .product_change_request_crud_v2 import ProductChangeRequestCRUD2
from .store_change_request_crud_v2 import StoreChangeRequestCRUD2
Loading