feat(base): 产品管理接口(类别校验/组合唯一/启停)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-13 10:35:03 +08:00
1 parent a694570dd1
commit 7eeed85c96
4 files changed
+642

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
"""产品管理接口(需求 2.1.2:产品主数据维护)。
产品是折算阈值的行级锚点(product_category 决定 30%/20% 折算阈值),
类别值域由后端校验 —— 前端下拉只是展示约束,后端校验是唯一防线。
**数据提取(POST /products/extract)暂不提供**:它依赖业务明细表
(V5 迁移),待业务管理任务完成后补充。
基础数据属业务操作,登录用户即可访问(区别于系统管理模块的 AdminUser);
写操作经 RequestMetaDep 带入请求元信息,由 service 层写入操作日志。
"""
from typing import Annotated
from fastapi import APIRouter, Depends, Path, Query
from app.api.deps import DbSession, RequestMetaDep, get_current_user
from app.core.response import ApiResponse, PageResult, ok
from app.models import User
from app.schemas.product import (
ProductCreateIn,
ProductOut,
ProductQuery,
ProductStatusIn,
ProductUpdateIn,
)
from app.services import product_service
CurrentUser = Annotated[User, Depends(get_current_user)]
router = APIRouter(prefix="/products", tags=["产品管理"])
@router.get("", response_model=ApiResponse[PageResult[ProductOut]], summary="产品列表")
def list_products(
session: DbSession,
_: CurrentUser,
query: Annotated[ProductQuery, Query()],
) -> ApiResponse[PageResult[ProductOut]]:
"""分页查询产品(含停用)。keyword 对名称或规格模糊匹配。"""
return ok(product_service.list_products(session, query))
@router.post("", response_model=ApiResponse[ProductOut], summary="新增产品")
def create_product(
session: DbSession,
current_user: CurrentUser,
payload: ProductCreateIn,
meta: RequestMetaDep,
) -> ApiResponse[ProductOut]:
"""新增产品。类别须在合法值域内;新产品默认启用。"""
return ok(
product_service.create_product(session, payload, operator=current_user, meta=meta),
message="产品已创建",
)
@router.get("/{product_id}", response_model=ApiResponse[ProductOut], summary="产品详情")
def get_product(
session: DbSession,
_: CurrentUser,
product_id: Annotated[int, Path(ge=1)],
) -> ApiResponse[ProductOut]:
"""按主键查询产品详情。"""
return ok(product_service.get_product(session, product_id))
@router.put("/{product_id}", response_model=ApiResponse[ProductOut], summary="编辑产品")
def update_product(
session: DbSession,
current_user: CurrentUser,
product_id: Annotated[int, Path(ge=1)],
payload: ProductUpdateIn,
meta: RequestMetaDep,
) -> ApiResponse[ProductOut]:
"""编辑产品基本信息(全量替换,不含状态;启用/停用走独立端点)。"""
return ok(
product_service.update_product(
session, product_id, payload, operator=current_user, meta=meta
),
message="已保存",
)
@router.delete("/{product_id}", response_model=ApiResponse[None], summary="删除产品")
def delete_product(
session: DbSession,
current_user: CurrentUser,
product_id: Annotated[int, Path(ge=1)],
meta: RequestMetaDep,
) -> ApiResponse[None]:
"""物理删除产品(仅限误录数据,常规下架用停用)。
被业务明细引用的保护检查待业务数据表(V5)就位后补充。
"""
product_service.delete_product(session, product_id, operator=current_user, meta=meta)
return ok(message="产品已删除")
@router.put(
"/{product_id}/status", response_model=ApiResponse[ProductOut], summary="启用/停用产品"
)
def set_status(
session: DbSession,
current_user: CurrentUser,
product_id: Annotated[int, Path(ge=1)],
payload: ProductStatusIn,
meta: RequestMetaDep,
) -> ApiResponse[ProductOut]:
"""启用或停用产品(1启用 0停用)。"""
result = product_service.set_status(
session, product_id, payload.status, operator=current_user, meta=meta
)
return ok(result, message="已启用" if payload.status == 1 else "已停用")