diff --git a/backend/app/core/logging_config.py b/backend/app/core/logging_config.py index d5b2960..76e0069 100644 --- a/backend/app/core/logging_config.py +++ b/backend/app/core/logging_config.py @@ -12,7 +12,7 @@ class JSONFormatter(logging.Formatter): def format(self, record): log_data = { - 'timestamp': datetime.utcnow().isoformat() + 'Z', + 'timestamp': datetime.now().strftime('%Y-%m-%dT%H:%M:%S'), 'level': record.levelname, 'logger': record.name, 'message': record.getMessage(), diff --git a/backend/app/routers/auth.py b/backend/app/routers/auth.py index c962983..f073f34 100644 --- a/backend/app/routers/auth.py +++ b/backend/app/routers/auth.py @@ -13,6 +13,43 @@ router = APIRouter(prefix="/api/auth", tags=["认证"]) @router.post("/register", response_model=UserResponse) def register(user_data: UserCreate, db: Session = Depends(get_db)): """用户注册""" + # 验证短信验证码 + if user_data.phone and user_data.verifyCode: + from app.services.sms import verify_code + if not verify_code(user_data.phone, user_data.verifyCode): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="验证码错误或已过期" + ) + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="手机号和验证码为必填项" + ) + + # 验证密码复杂度(至少8位,包含大写、小写、数字) + if len(user_data.password) < 8: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="密码至少8位" + ) + import re + if not re.search(r'[A-Z]', user_data.password): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="密码必须包含大写字母" + ) + if not re.search(r'[a-z]', user_data.password): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="密码必须包含小写字母" + ) + if not re.search(r'[0-9]', user_data.password): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="密码必须包含数字" + ) + # 检查用户名是否已存在 existing_user = db.query(User).filter(User.f01_01_name == user_data.f01_01_name).first() if existing_user: diff --git a/backend/app/routers/collections.py b/backend/app/routers/collections.py index 7e1b4e2..4df0b5c 100644 --- a/backend/app/routers/collections.py +++ b/backend/app/routers/collections.py @@ -4,7 +4,7 @@ import uuid import re from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, status, Query, UploadFile, File -from sqlalchemy import func, text +from sqlalchemy import or_, func, text from sqlalchemy.orm import Session from app.core.database import get_db from app.core.auth import get_current_user @@ -117,7 +117,7 @@ def get_collections( User, Collection.f99_91_user_id == User.f99_90_id, isouter=True ) else: - query = db.query(Collection).filter(Collection.f99_91_user_id == current_user.f99_90_id) + query = db.query(Collection).filter(or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin")) if category: query = query.filter(Collection.f01_03_category == category) @@ -224,7 +224,7 @@ def get_stats( all_collections = db.query(Collection).all() else: all_collections = db.query(Collection).filter( - Collection.f99_91_user_id == current_user.f99_90_id + or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin") ).all() # 总数 @@ -396,7 +396,7 @@ def create_collection( if not force and final_code: existing_code = db.query(Collection).filter( Collection.f01_02_code == final_code, - Collection.f99_91_user_id == current_user.f99_90_id + or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin") ).first() if existing_code: @@ -413,7 +413,7 @@ def create_collection( # 查询当前用户是否有相同冠字号的藏品 existing = db.query(Collection).filter( Collection.f02_10_prefix_serial == collection_data.f02_10_prefix_serial, - Collection.f99_91_user_id == current_user.f99_90_id + or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin") ).first() if existing: @@ -492,7 +492,7 @@ def update_collection( """更新藏品""" collection = db.query(Collection).filter( Collection.f99_90_id == collection_id, - Collection.f99_91_user_id == current_user.f99_90_id + or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin") ).first() if not collection: @@ -523,7 +523,7 @@ def delete_collection( # 验证权限并检查是否存在 collection = db.query(Collection).filter( Collection.f99_90_id == collection_id, - Collection.f99_91_user_id == current_user.f99_90_id + or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin") ).first() if not collection: diff --git a/backend/app/routers/ocr.py b/backend/app/routers/ocr.py index 961914e..c49155d 100644 --- a/backend/app/routers/ocr.py +++ b/backend/app/routers/ocr.py @@ -296,7 +296,7 @@ async def claim_temp_image( oss_key = get_oss_path("collection", user_id=current_user.f99_90_id, collection_id=collection_id, filename=final_filename) # 尝试从OSS获取临时图片 - 尝试多种扩展名和日期路径 - temp_extensions = ['jpg', 'jpeg', 'png', 'gif'] + temp_extensions = ['jpg', 'jpeg', 'png', 'gif', 'JPG', 'JPEG', 'PNG', 'GIF'] temp_content = None found_key = None diff --git a/backend/app/routers/users.py b/backend/app/routers/users.py index 2215970..156e84a 100644 --- a/backend/app/routers/users.py +++ b/backend/app/routers/users.py @@ -48,6 +48,27 @@ def update_current_user( if not user: raise HTTPException(status_code=404, detail="用户不存在") + # 处理手机号更换验证码 + new_phone = user_update.phone + verify_code = user_update.verifyCode + + if new_phone and new_phone != user.phone: + # 需要验证验证码 + if not verify_code: + raise HTTPException(status_code=400, detail="更换手机号需要验证码") + + # 验证验证码 + from app.services.sms import verify_code as sms_verify + import os + if os.getenv("SMS_TEST_MODE") == "true": + # 测试模式:任何6位数字有效 + is_valid = len(verify_code) == 6 and verify_code.isdigit() + else: + is_valid = sms_verify(new_phone, verify_code) + + if not is_valid: + raise HTTPException(status_code=400, detail="验证码错误或已过期") + # 更新字段 update_data = user_update.model_dump(exclude_unset=True) for field, value in update_data.items(): @@ -55,6 +76,8 @@ def update_current_user( user.f01_01_name = value elif field == 'username': pass # skip, already handled as f01_01_name + elif field == 'verifyCode': + pass # skip,验证码不存储 elif hasattr(user, field): setattr(user, field, value) @@ -62,7 +85,7 @@ def update_current_user( db.flush() db.commit() db.refresh(user) - logger.info(f"After commit, user email={user.email}") + logger.info(f"After commit, user email={user.email}, phone={user.phone}") return user diff --git a/backend/app/schemas/schemas.py b/backend/app/schemas/schemas.py index b843b8e..7525919 100644 --- a/backend/app/schemas/schemas.py +++ b/backend/app/schemas/schemas.py @@ -7,7 +7,7 @@ from datetime import datetime # ============ 用户相关 ============ class UserBase(BaseModel): - f01_01_name: str = Field(..., min_length=3, max_length=255, alias="username") + f01_01_name: str = Field(..., min_length=2, max_length=255, alias="username") email: Optional[EmailStr] = None phone: Optional[str] = None avatar: Optional[str] = None @@ -18,13 +18,15 @@ class UserBase(BaseModel): class UserCreate(UserBase): - password: str = Field(..., min_length=6) + password: str = Field(..., min_length=8) + verifyCode: Optional[str] = Field(None, alias="verifyCode") class UserUpdate(BaseModel): f01_01_name: Optional[str] = Field(None, alias="username") - email: Optional[EmailStr] = None + email: Optional[str] = None # 改为字符串,不强制验证EmailStr phone: Optional[str] = None + verifyCode: Optional[str] = Field(None, alias="verifyCode") # 更换手机号验证码 avatar: Optional[str] = None address: Optional[str] = None bio: Optional[str] = None diff --git a/backend/app/services/sms.py b/backend/app/services/sms.py index e8a0a7b..1e53a8b 100644 --- a/backend/app/services/sms.py +++ b/backend/app/services/sms.py @@ -8,10 +8,10 @@ from typing import Optional # 阿里云短信配置 SMS_CONFIG = { - "access_key_id": os.getenv("SMS_ACCESS_KEY_ID", "LTAI5t6HUnpFBLEK9194kPVG"), - "access_key_secret": os.getenv("SMS_ACCESS_KEY_SECRET", "LEr4Q8yRxb8D5b24cKfCwlt4MMoke1"), - "sign_name": "阿里云", - "template_code": "100001", + "access_key_id": os.getenv("SMS_ACCESS_KEY_ID", "LTAI5tQAx5niD7JQVqGE5acE"), + "access_key_secret": os.getenv("SMS_ACCESS_KEY_SECRET", "QsQFAEKBkaNynIoKyvdIi3BUyWVZu1"), + "sign_name": os.getenv("SMS_SIGN_NAME", "苏州算力"), + "template_code": os.getenv("SMS_TEMPLATE_CODE", "SMS_501590956"), } # 验证码缓存(生产环境建议用Redis) @@ -82,6 +82,11 @@ def send_verification_code(phone: str) -> dict: def verify_code(phone: str, code: str) -> bool: """验证验证码""" + # 测试模式:任何6位数字验证码都有效 + import os + if os.getenv("SMS_TEST_MODE") == "true": + return len(code) == 6 and code.isdigit() + if phone not in VERIFICATION_CODES: return False diff --git a/config/VERSION b/config/VERSION index 22fd10f..52cda95 100644 --- a/config/VERSION +++ b/config/VERSION @@ -1,14 +1 @@ -# 甲辰藏品管理系统 - 版本配置 -# Version Configuration for Zodiac Collection Management System - -# 当前版本号 (语义化版本:主版本。次版本.修订版) -VERSION=1.1.21 - -# 版本代号 (可选) -VERSION_CODENAME="新生" - -# 发布日期 -RELEASE_DATE=2026-03-18 - -# 版本说明 -VERSION_NOTES="性能优化 - Nginx反向代理、图片OSS直连、自动压缩、上传限制20MB" +v1.1.22 diff --git a/frontend/index.html b/frontend/index.html index 3030ca7..177fa10 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ -