|
|
@@ -15,6 +15,7 @@ from app.core import security
|
|
|
from app.models.application import Application
|
|
|
from app.models.user import User
|
|
|
from app.models.mapping import AppUserMapping
|
|
|
+from app.core.utils import generate_english_name
|
|
|
from app.schemas.application import (
|
|
|
ApplicationCreate,
|
|
|
ApplicationUpdate,
|
|
|
@@ -1014,32 +1015,132 @@ def sync_mapping(
|
|
|
):
|
|
|
"""
|
|
|
从外部平台同步用户映射关系(机器对机器)。
|
|
|
- 只同步映射关系,不创建或更新用户本身。
|
|
|
+ 支持增删改查:
|
|
|
+ - UPSERT (默认): 创建或更新映射及用户。
|
|
|
+ - DELETE: 仅删除应用与用户的映射关系,不删除用户。
|
|
|
需要应用访问令牌 (Authorization Bearer JWT 或 X-App-Access-Token)。
|
|
|
"""
|
|
|
# Normalize input: treat empty strings as None
|
|
|
mapped_key = sync_in.mapped_key if sync_in.mapped_key else None
|
|
|
mapped_email = sync_in.mapped_email if sync_in.mapped_email else None
|
|
|
+ in_name = sync_in.name if sync_in.name else None
|
|
|
+ in_english_name = sync_in.english_name if sync_in.english_name else None
|
|
|
+
|
|
|
+ # ==========================================
|
|
|
+ # 1. Handle DELETE Action
|
|
|
+ # ==========================================
|
|
|
+ if sync_in.sync_action == "DELETE":
|
|
|
+ # 查找用户
|
|
|
+ user = db.query(User).filter(User.mobile == sync_in.mobile).first()
|
|
|
+ if not user:
|
|
|
+ # 用户不存在,无法删除映射,直接抛出404或视作成功
|
|
|
+ raise HTTPException(status_code=404, detail="用户不存在")
|
|
|
+
|
|
|
+ # 查找映射
|
|
|
+ mapping = db.query(AppUserMapping).filter(
|
|
|
+ AppUserMapping.app_id == current_app.id,
|
|
|
+ AppUserMapping.user_id == user.id
|
|
|
+ ).first()
|
|
|
+
|
|
|
+ if not mapping:
|
|
|
+ raise HTTPException(status_code=404, detail="映射关系不存在")
|
|
|
+
|
|
|
+ # 构造返回数据(删除前快照,将状态置为 False)
|
|
|
+ resp_data = MappingResponse(
|
|
|
+ id=mapping.id,
|
|
|
+ app_id=mapping.app_id,
|
|
|
+ user_id=mapping.user_id,
|
|
|
+ mapped_key=mapping.mapped_key,
|
|
|
+ mapped_email=mapping.mapped_email,
|
|
|
+ user_mobile=user.mobile,
|
|
|
+ user_status=user.status,
|
|
|
+ is_active=False # 标记为非活跃/已删除
|
|
|
+ )
|
|
|
+
|
|
|
+ # 执行物理删除 (只删映射,不删用户)
|
|
|
+ db.delete(mapping)
|
|
|
+ db.commit()
|
|
|
+
|
|
|
+ # 记录日志
|
|
|
+ LogService.create_log(
|
|
|
+ db=db,
|
|
|
+ app_id=current_app.id,
|
|
|
+ operator_id=current_app.owner_id,
|
|
|
+ action_type=ActionType.DELETE,
|
|
|
+ target_user_id=user.id,
|
|
|
+ target_mobile=user.mobile,
|
|
|
+ details={"mapped_key": mapping.mapped_key, "action": "M2M_DELETE"}
|
|
|
+ )
|
|
|
+
|
|
|
+ return resp_data
|
|
|
+
|
|
|
+ # ==========================================
|
|
|
+ # 2. Handle UPSERT Action (Existing Logic)
|
|
|
+ # ==========================================
|
|
|
+
|
|
|
+ # 0. Check Uniqueness for Name and English Name (Global Check)
|
|
|
+ # We exclude the current user (by mobile) to allow updates to self without conflict
|
|
|
+ if in_name:
|
|
|
+ name_conflict = db.query(User).filter(
|
|
|
+ User.name == in_name,
|
|
|
+ User.mobile != sync_in.mobile
|
|
|
+ ).first()
|
|
|
+ if name_conflict:
|
|
|
+ raise HTTPException(status_code=400, detail=f"姓名 '{in_name}' 已存在")
|
|
|
+
|
|
|
+ if in_english_name:
|
|
|
+ en_name_conflict = db.query(User).filter(
|
|
|
+ User.english_name == in_english_name,
|
|
|
+ User.mobile != sync_in.mobile
|
|
|
+ ).first()
|
|
|
+ if en_name_conflict:
|
|
|
+ raise HTTPException(status_code=400, detail=f"英文名 '{in_english_name}' 已存在")
|
|
|
|
|
|
# 1. Find User or Create
|
|
|
user = db.query(User).filter(User.mobile == sync_in.mobile).first()
|
|
|
new_user_created = False
|
|
|
+
|
|
|
if not user:
|
|
|
+ # Create New User
|
|
|
+
|
|
|
+ # Auto-generate English name if missing but Chinese name is present
|
|
|
+ if in_name and not in_english_name:
|
|
|
+ in_english_name = generate_english_name(in_name)
|
|
|
+
|
|
|
+ # Validation: Name and English Name are required for new users
|
|
|
+ if not in_name or not in_english_name:
|
|
|
+ raise HTTPException(status_code=400, detail="新建用户必须提供姓名和英文名称")
|
|
|
+
|
|
|
# Auto create user
|
|
|
password = security.generate_alphanumeric_password(8) # Random password letters+digits
|
|
|
- random_suffix = security.generate_alphanumeric_password(6)
|
|
|
+
|
|
|
user = User(
|
|
|
mobile=sync_in.mobile,
|
|
|
password_hash=security.get_password_hash(password),
|
|
|
status="ACTIVE",
|
|
|
role="ORDINARY_USER",
|
|
|
- name=f"用户{random_suffix}",
|
|
|
- english_name=mapped_key
|
|
|
+ name=in_name,
|
|
|
+ english_name=in_english_name
|
|
|
)
|
|
|
db.add(user)
|
|
|
db.commit()
|
|
|
db.refresh(user)
|
|
|
new_user_created = True
|
|
|
+ else:
|
|
|
+ # Update Existing User (if fields provided)
|
|
|
+ updated = False
|
|
|
+ if in_name is not None and user.name != in_name:
|
|
|
+ user.name = in_name
|
|
|
+ updated = True
|
|
|
+
|
|
|
+ if in_english_name is not None and user.english_name != in_english_name:
|
|
|
+ user.english_name = in_english_name
|
|
|
+ updated = True
|
|
|
+
|
|
|
+ if updated:
|
|
|
+ db.add(user)
|
|
|
+ db.commit()
|
|
|
+ db.refresh(user)
|
|
|
|
|
|
# 2. Handle Mapping
|
|
|
mapping = db.query(AppUserMapping).filter(
|