File size: 10,699 Bytes
551658a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List, Optional
import uuid
import re

from database import get_db
from models.category import Category
from models.product import Product
from models.user import User
from schemas.category import CategoryResponse, CategoryCreate, CategoryUpdate, CategoryTreeResponse
from routers.auth import get_current_user, is_admin_user

router = APIRouter()

def generate_level_code() -> str:
    """生成6位字母数字组合编码"""
    # 使用UUID生成并截取前6位,确保包含字母和数字
    while True:
        code = uuid.uuid4().hex[:6].lower()
        # 检查是否同时包含字母和数字
        if re.search(r'[a-z]', code) and re.search(r'[0-9]', code):
            return code

def get_category_level_by_code(db: Session, category_code: str, company_code: str) -> int:
    """通过编码获取分类的层级,0为顶级"""
    if not category_code:
        return 0
    # 通过下划线数量判断层级
    return category_code.count('_')

def build_category_code(db: Session, parent_code: str, company_code: str) -> str:
    """构建级联编码"""
    if not parent_code:
        # 顶级分类,直接生成编码
        return generate_level_code()
    
    parent = db.query(Category).filter(
        Category.code == parent_code,
        Category.company_code == company_code
    ).first()
    if not parent:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Parent category not found"
        )
    
    # 生成当前层级编码,拼接父级编码
    level_code = generate_level_code()
    return f"{parent_code}_{level_code}"

def build_category_tree(categories: List[Category], parent_code: str = "") -> List[CategoryTreeResponse]:
    """构建分类树形结构"""
    tree = []
    for cat in categories:
        if cat.parent_code == parent_code:
            children = build_category_tree(categories, cat.code)
            tree.append(CategoryTreeResponse(
                **cat.__dict__,
                children=children
            ))
    return tree

@router.get("/", response_model=List[CategoryResponse])
async def get_categories(
    parent_code: Optional[str] = None,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """获取分类列表"""
    query = db.query(Category)
    if not is_admin_user(current_user):
        query = query.filter(Category.company_code == current_user.company_code)
    if parent_code is not None:
        query = query.filter(Category.parent_code == parent_code)
    categories = query.order_by(Category.sort_order, Category.id).all()
    
    # 计算每个分类的产品数量
    result = []
    for category in categories:
        product_count = db.query(Product).filter(
            Product.category_id == category.id
        )
        if not is_admin_user(current_user):
            product_count = product_count.filter(Product.company_code == current_user.company_code)
        result.append(CategoryResponse(
            **category.__dict__,
            product_count=product_count.count()
        ))
    return result

@router.get("/tree", response_model=List[CategoryTreeResponse])
async def get_category_tree(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """获取分类树形结构"""
    query = db.query(Category)
    if not is_admin_user(current_user):
        query = query.filter(Category.company_code == current_user.company_code)
    
    categories = query.order_by(Category.sort_order, Category.id).all()
    
    return build_category_tree(categories, "")

@router.get("/{category_id}", response_model=CategoryResponse)
async def get_category(
    category_id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """获取单个分类详情"""
    query = db.query(Category).filter(Category.id == category_id)
    if not is_admin_user(current_user):
        query = query.filter(Category.company_code == current_user.company_code)
    
    category = query.first()
    if not category:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Category not found"
        )
    
    # 计算产品数量
    product_count = db.query(Product).filter(Product.category_id == category.id)
    if not is_admin_user(current_user):
        product_count = product_count.filter(Product.company_code == current_user.company_code)
    
    return CategoryResponse(
        **category.__dict__,
        product_count=product_count.count()
    )

@router.post("/", response_model=CategoryResponse, status_code=status.HTTP_201_CREATED)
async def create_category(
    category: CategoryCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """创建新分类"""
    # 检查层级限制(最多三级)
    level = get_category_level_by_code(db, category.parent_code, current_user.company_code)
    if level >= 3:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Maximum category level is 3"
        )
    
    # 检查同级分类名称是否已存在
    existing = db.query(Category).filter(
        Category.name == category.name,
        Category.parent_code == category.parent_code,
        Category.company_code == current_user.company_code
    ).first()
    if existing:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Category name already exists in the same level"
        )
    
    # 构建编码
    code = build_category_code(db, category.parent_code, current_user.company_code)
    
    # 创建分类
    category_data = category.model_dump()
    if not is_admin_user(current_user):
        category_data['company_code'] = current_user.company_code
    category_data['code'] = code
    db_category = Category(**category_data)
    db.add(db_category)
    db.commit()
    db.refresh(db_category)
    
    return CategoryResponse(
        **db_category.__dict__,
        product_count=0
    )

@router.put("/{category_id}", response_model=CategoryResponse)
async def update_category(
    category_id: int,
    category_update: CategoryUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """更新分类"""
    query = db.query(Category).filter(Category.id == category_id)
    if not is_admin_user(current_user):
        query = query.filter(Category.company_code == current_user.company_code)
    
    db_category = query.first()
    if not db_category:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Category not found"
        )
    
    update_data = category_update.model_dump(exclude_unset=True)
    
    # 检查是否需要更新父分类
    new_parent_code = update_data.get('parent_code', db_category.parent_code)
    if new_parent_code != db_category.parent_code:
        # 检查层级限制(最多三级)
        level = get_category_level_by_code(db, new_parent_code, current_user.company_code)
        if level >= 3:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Maximum category level is 3"
            )
        
        # 检查是否有子分类,如果有则不能改变父分类
        child_count = db.query(Category).filter(
            Category.parent_code == db_category.code,
            Category.company_code == current_user.company_code
        ).count()
        if child_count > 0:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Cannot change parent category of category with children"
            )
        
        # 重新构建编码
        new_code = build_category_code(db, new_parent_code, current_user.company_code)
        update_data['code'] = new_code
    
    # 检查新名称是否与同级分类冲突
    new_name = update_data.get('name', db_category.name)
    if new_name != db_category.name or new_parent_code != db_category.parent_code:
        existing = db.query(Category).filter(
            Category.name == new_name,
            Category.parent_code == new_parent_code,
            Category.company_code == current_user.company_code,
            Category.id != category_id
        ).first()
        if existing:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Category name already exists in the same level"
            )
    
    # 更新分类
    for field, value in update_data.items():
        setattr(db_category, field, value)
    db.commit()
    db.refresh(db_category)
    
    # 计算产品数量
    product_count = db.query(Product).filter(Product.category_id == db_category.id)
    if not is_admin_user(current_user):
        product_count = product_count.filter(Product.company_code == current_user.company_code)
    
    return CategoryResponse(
        **db_category.__dict__,
        product_count=product_count.count()
    )

@router.delete("/{category_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_category(
    category_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """删除分类"""
    query = db.query(Category).filter(Category.id == category_id)
    if not is_admin_user(current_user):
        query = query.filter(Category.company_code == current_user.company_code)
    
    db_category = query.first()
    if not db_category:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Category not found"
        )
    
    # 检查是否有子分类
    child_count = db.query(Category).filter(
        Category.parent_code == db_category.code,
        Category.company_code == current_user.company_code
    ).count()
    if child_count > 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Cannot delete category with {child_count} child categories"
        )
    
    # 检查是否有产品属于该分类
    product_count = db.query(Product).filter(
        Product.category_id == category_id,
        Product.company_code == current_user.company_code
    ).count()
    if product_count > 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Cannot delete category with {product_count} products"
        )
    
    db.delete(db_category)
    db.commit()
    return None