938 lines
32 KiB
Python
938 lines
32 KiB
Python
from fastapi import APIRouter, Depends, Request, Form, Body, UploadFile,File
|
||
from fastapi.responses import PlainTextResponse
|
||
from typing import Optional, Dict, Any
|
||
from pydantic import BaseModel
|
||
from sqlalchemy.orm import Session
|
||
from mooc.db.database import get_db
|
||
from mooc.crud.crud_goouc_fullexam_user import (
|
||
CRUDUserDoexam,
|
||
CRUDUserExamAnswer,
|
||
CRUDUserWrongPraction,
|
||
CRUDUserCollectionPraction,
|
||
full_exam_user
|
||
)
|
||
from mooc.models.goouc_fullexam_user import (
|
||
UserDoexam,
|
||
UserExamAnswer,
|
||
UserWrongPraction,
|
||
UserCollectionPraction,
|
||
FullExamUser
|
||
)
|
||
from mooc.core.config import settings
|
||
from mooc.utils.wechat_client import wx_api
|
||
from datetime import datetime
|
||
import os
|
||
from mooc.core.logger import get_logger
|
||
import httpx
|
||
from mooc.core.response import ResponseFactory
|
||
import re
|
||
from mooc.crud.crud_goouc_fullexam import setting, xueshen
|
||
from mooc.crud.crud_goouc_fullexam_user import full_exam_user, user_member
|
||
|
||
wxapp_router = APIRouter()
|
||
|
||
# 创建模块级别的日志记录器
|
||
logger = get_logger(__name__)
|
||
|
||
class WxappRequest(BaseModel):
|
||
uid: Optional[str] = None
|
||
op: Optional[str] = None
|
||
m: Optional[str] = None
|
||
data: Dict[str, Any] = {}
|
||
code: Optional[str] = None
|
||
|
||
# 启用额外字段支持
|
||
model_config = {
|
||
"extra": "allow" # 允许存储模型中未定义的字段
|
||
}
|
||
|
||
# 添加一个方法便于获取任意字段,带默认值
|
||
def get_field(self, field_name: str, default=None):
|
||
"""获取字段值,优先从直接属性获取,然后从data字典获取"""
|
||
if hasattr(self, field_name):
|
||
return getattr(self, field_name)
|
||
return self.data.get(field_name, default)
|
||
|
||
@wxapp_router.post("/index")
|
||
async def handle_wxapp_request(
|
||
request: Request,
|
||
i: str,
|
||
t: Optional[str] = None,
|
||
v: Optional[str] = None,
|
||
c: Optional[str] = "entry",
|
||
a: Optional[str] = "wxapp",
|
||
do: Optional[str] = None,
|
||
db: Session = Depends(get_db),
|
||
file: UploadFile = None
|
||
):
|
||
# 获取表单数据
|
||
try:
|
||
form_data = await request.form()
|
||
logger.debug(f"Form data: {form_data}")
|
||
# 将表单数据转换为字典
|
||
data = dict(form_data)
|
||
|
||
# 检查是否包含上传文件
|
||
if "upfile" in form_data:
|
||
file = form_data["upfile"]
|
||
except Exception as e:
|
||
logger.error(f"Error reading form data: {e}")
|
||
# 如果没有表单数据,尝试读取JSON
|
||
try:
|
||
data = await request.json()
|
||
logger.debug(f"JSON data: {data}")
|
||
except Exception as e:
|
||
logger.error(f"Error reading JSON: {e}")
|
||
data = {}
|
||
|
||
logger.debug(f"Final data: {data}")
|
||
logger.debug(f"Query params: {request.query_params}")
|
||
|
||
# 根据do参数处理不同的业务逻辑
|
||
if do == "login2":
|
||
# 针对login2,直接传递表单数据
|
||
if "code" in data:
|
||
return await handle_login2(WxappRequest(code=data.get("code"),
|
||
data=data), db)
|
||
else:
|
||
# 如果没有code,仍然尝试正常处理
|
||
return await handle_login2(WxappRequest(**data), db)
|
||
elif do == "Setuserinfo":
|
||
return await handle_set_user_info(WxappRequest(**data), db)
|
||
elif do == "ExamOperation":
|
||
return await handle_exam_operation(WxappRequest(**data), db, user_doexam, user_exam_answer)
|
||
elif do == "Collection":
|
||
return await handle_collection(WxappRequest(**data), db, user_collection)
|
||
elif do == "WrongQuestion":
|
||
return await handle_wrong_question(WxappRequest(**data), db, user_wrong_praction)
|
||
elif do == "TotalqNum":
|
||
# 添加新的处理逻辑
|
||
return {"code": 0, "data": {"total": 100}, "message": "success"}
|
||
elif do == "Index":
|
||
# 添加首页处理逻辑
|
||
return {"code": 0, "data": {}, "message": "success"}
|
||
elif do == "Advert":
|
||
# 添加广告处理逻辑
|
||
return {"code": 0, "data": [], "message": "success"}
|
||
elif do == "uploadImage":
|
||
if not file:
|
||
return ResponseFactory.error(code=400, message="缺少上传文件")
|
||
return await handle_upload_image(WxappRequest(**data), db, file)
|
||
elif do == "UpdateHeadimg":
|
||
return await handle_update_headimg(WxappRequest(**data), db)
|
||
|
||
|
||
|
||
return {"code": 404, "message": "接口未找到"}
|
||
|
||
async def handle_update_headimg(data: WxappRequest, db: Session):
|
||
"""处理更新用户头像的请求"""
|
||
logger.info(f"处理更新头像请求: uid={data.uid},data={data}")
|
||
|
||
try:
|
||
# 验证必要参数
|
||
uid = data.uid
|
||
# headimg = data.data.get("headimg")
|
||
|
||
# if not uid or not headimg:
|
||
# logger.error("参数不足: uid或headimg缺失")
|
||
# return ResponseFactory.error(code=1, message="传递的参数不存在", data="1001")
|
||
|
||
# 获取用户对象,包含weid条件
|
||
user = full_exam_user.get_by_id_and_weid(db, int(uid), settings.WECHAT_UNIACID)
|
||
if not user:
|
||
logger.error(f"用户不存在: uid={uid}")
|
||
return ResponseFactory.error(code=1, message="用户不存在", data="error")
|
||
logger.debug(f"用户信息: {user.headimg}")
|
||
# 更新用户头像
|
||
|
||
try:
|
||
update_data = {"headimg": data.headimg}
|
||
full_exam_user.update(db, db_obj=user, obj_in=update_data)
|
||
logger.info(f"头像更新成功: uid={uid}, headimg={data.headimg}")
|
||
return ResponseFactory.success(data="ok", message="ok")
|
||
except Exception as e:
|
||
logger.error(f"头像更新失败: {str(e)}")
|
||
return ResponseFactory.error(code=1, message="error", data="error")
|
||
|
||
except Exception as e:
|
||
logger.exception(f"处理更新头像请求过程中发生异常: {str(e)}")
|
||
return ResponseFactory.error(code=1, message=str(e), data="error")
|
||
|
||
async def handle_upload_image(data: WxappRequest, db: Session, file: UploadFile = File(...)):
|
||
"""处理图片上传功能"""
|
||
import os
|
||
import time
|
||
import random
|
||
import string
|
||
import shutil
|
||
from pathlib import Path
|
||
|
||
logger.info(f"处理图片上传请求: uid={data.uid}, module={data.m}")
|
||
|
||
try:
|
||
# 定义允许的图片类型
|
||
allowed_types = [
|
||
"image/jpg", "image/jpeg", "image/png",
|
||
"image/pjpeg", "image/gif", "image/bmp", "image/x-png"
|
||
]
|
||
|
||
# 最大文件大小(2MB)
|
||
max_file_size = 2000000
|
||
|
||
# 检查文件是否存在
|
||
if not file:
|
||
logger.error("图片不存在!")
|
||
return ResponseFactory.error(code=400, message="图片不存在!")
|
||
|
||
# 检查文件内容类型
|
||
content_type = file.content_type
|
||
if content_type not in allowed_types:
|
||
logger.error(f"文件类型不符: {content_type}")
|
||
return ResponseFactory.error(code=400, message=f"文件类型不符: {content_type}")
|
||
|
||
# 读取文件内容以检查大小
|
||
contents = await file.read()
|
||
if len(contents) > max_file_size:
|
||
logger.error(f"文件太大: {len(contents)} bytes")
|
||
return ResponseFactory.error(code=400, message="文件太大!")
|
||
|
||
# 重置文件位置指针
|
||
await file.seek(0)
|
||
|
||
# 获取上传目标文件夹路径
|
||
module_name = data.m or "default"
|
||
|
||
# 使用绝对路径并确保目录存在
|
||
destination_folder = os.path.join(settings.UPLOAD_IMG_DIR, module_name)
|
||
logger.debug(f"上传目标文件夹: {destination_folder}")
|
||
|
||
# 确保目标目录存在
|
||
Path(destination_folder).mkdir(parents=True, exist_ok=True)
|
||
|
||
# 创建更安全的随机文件名 (时间戳 + 随机数)
|
||
current_time = int(time.time())
|
||
random_string = ''.join(random.choices(string.digits + string.ascii_lowercase, k=8))
|
||
file_extension = os.path.splitext(file.filename)[1].lower() # 转为小写以增加一致性
|
||
|
||
# 验证扩展名安全性
|
||
safe_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
|
||
if file_extension not in safe_extensions:
|
||
file_extension = '.jpg' # 默认使用安全扩展名
|
||
|
||
filename = f"{current_time}_{random_string}{file_extension}"
|
||
|
||
# 完整的保存路径
|
||
file_path = os.path.join(destination_folder, filename)
|
||
logger.debug(f"文件将保存至: {file_path}")
|
||
|
||
# 保存文件
|
||
with open(file_path, "wb") as buffer:
|
||
shutil.copyfileobj(file.file, buffer)
|
||
|
||
# 检查文件是否成功保存
|
||
if not os.path.exists(file_path):
|
||
logger.error(f"文件保存失败: {file_path}")
|
||
return ResponseFactory.error(code=500, message="文件保存失败")
|
||
|
||
# 构建文件URL路径
|
||
url_module_path = module_name.replace('\\', '/')
|
||
|
||
# 使用相对路径作为存储路径
|
||
relative_path = f"{url_module_path}/{filename}"
|
||
|
||
# 构建完整的外网访问URL
|
||
# 方法1: 使用BASE_URL配置构建绝对URL
|
||
absolute_url = f"{settings.BASE_URL}{settings.ATTACH_URL}{relative_path}"
|
||
|
||
logger.info(f"文件上传成功: {absolute_url}")
|
||
|
||
# 直接返回完整URL字符串
|
||
return PlainTextResponse(content=absolute_url)
|
||
|
||
except Exception as e:
|
||
logger.exception(f"文件上传过程中发生异常: {str(e)}")
|
||
# 直接返回错误消息字符串
|
||
return str(e)
|
||
def validate_phone(phone: str) -> bool:
|
||
# 基础格式验证
|
||
if not re.match(r"^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$", phone):
|
||
return False
|
||
|
||
# 特殊号段白名单
|
||
special_prefix = ["191", "192", "197"] # 根据实际情况更新
|
||
if any(phone.startswith(p) for p in special_prefix):
|
||
return True
|
||
|
||
# 虚拟运营商号段二次验证
|
||
if phone.startswith(("170", "171", "172")):
|
||
return validate_virtual_operator(phone) # 调用专用验证接口
|
||
|
||
return True
|
||
|
||
async def handle_set_user_info(data: WxappRequest, db: Session):
|
||
"""处理用户信息相关操作"""
|
||
logger.info(f"处理用户信息请求: op={data.op}, uid={data.uid},data={data}")
|
||
|
||
operations = {
|
||
"getinfo": get_user_info,
|
||
"checkStudent": check_student,
|
||
"update": update_user_info,
|
||
"getcode": get_verification_code,
|
||
"checkcode": check_verification_code,
|
||
}
|
||
|
||
# 如果op为空,则默认执行更新用户信息操作
|
||
if not data.op:
|
||
logger.info(f"未指定操作类型,默认执行更新用户信息: uid={data.uid}")
|
||
try:
|
||
# 直接从请求对象中获取数据
|
||
update_data = {}
|
||
if hasattr(data, "name"):
|
||
update_data["name"] = data.name
|
||
if hasattr(data, "phone") and data.phone != "null":
|
||
update_data["phone"] = data.phone
|
||
|
||
# 添加请求中的其他字段
|
||
for field in ["student_id", "id_card", "school", "level"]:
|
||
if hasattr(data, field):
|
||
update_data["field"] = getattr(data, field)
|
||
|
||
logger.debug(f"更新用户数据: {update_data}")
|
||
result = await update_user_info(data.uid, update_data, db)
|
||
return ResponseFactory.success(
|
||
data=result,
|
||
message="更新成功"
|
||
)
|
||
except Exception as e:
|
||
logger.exception(f"更新用户信息时发生异常: {str(e)}")
|
||
return ResponseFactory.error(
|
||
code=1,
|
||
message=str(e)
|
||
)
|
||
|
||
# 处理指定操作类型
|
||
operation = operations.get(data.op)
|
||
logger.debug(f"operation: {operation}")
|
||
if not operation:
|
||
logger.warning(f"不支持的操作: {data.op}")
|
||
return ResponseFactory.error(
|
||
code=1,
|
||
message=f"不支持的操作: {data.op}"
|
||
)
|
||
|
||
try:
|
||
# 处理特殊情况:getcode操作需要直接获取phone属性
|
||
if data.op == "getcode":
|
||
# 确保将phone从对象属性传递到data字典
|
||
data_dict = dict(data.data) if data.data else {} # 复制现有数据
|
||
|
||
# 从对象中获取phone属性
|
||
if hasattr(data, "phone"):
|
||
data_dict["phone"] = data.phone
|
||
|
||
result = await operation(data.uid, data_dict, db)
|
||
else:
|
||
# 其他操作保持原样
|
||
result = await operation(data.uid, data.data, db)
|
||
|
||
logger.debug(f"result: {result}")
|
||
return ResponseFactory.success(
|
||
data=result,
|
||
message="success"
|
||
)
|
||
except Exception as e:
|
||
logger.exception(f"处理用户信息时发生异常: {str(e)}")
|
||
return ResponseFactory.error(
|
||
code=1,
|
||
message=str(e)
|
||
)
|
||
|
||
# 获取用户信息
|
||
async def get_user_info(uid: str, data: Dict[str, Any], db: Session) -> Dict[str, Any]:
|
||
"""获取用户详细信息"""
|
||
logger.debug(f"获取用户信息: uid={uid},data={data}")
|
||
if not uid:
|
||
raise ValueError("用户ID不存在")
|
||
|
||
# 从请求获取协议和域名
|
||
protocol = "https://" # 在实际部署中可能需要根据请求头判断
|
||
domain_name = "yourdomain.com" # 这里需要从请求中获取
|
||
http = f"{protocol}{domain_name}"
|
||
|
||
# 获取用户基本信息 - 已经使用CRUD方法
|
||
user = full_exam_user.get_by_fields(db, {"id": int(uid), "weid": settings.WECHAT_UNIACID, "istatus": 1})
|
||
if not user or user.istatus != 1:
|
||
raise ValueError("用户不存在或已被删除")
|
||
|
||
# 构建用户信息字典
|
||
info = {
|
||
"id": user.id,
|
||
"nickname": user.nickname,
|
||
"headimg": user.headimg,
|
||
"name": user.name,
|
||
"phone": user.phone,
|
||
"grade": user.gradeid,
|
||
"ismember": user.ismember,
|
||
"member_endtime": None,
|
||
"nativeplace": user.nativeplace,
|
||
"integral": user.integral,
|
||
"student_id": user.student_id,
|
||
"id_card": user.id_card,
|
||
"school": user.school,
|
||
"level": user.level,
|
||
"openid": user.openid
|
||
}
|
||
logger.debug(f"用户信息: {info}")
|
||
|
||
# 检查会员是否过期
|
||
current_time = int(datetime.now().timestamp())
|
||
if user.ismember == 1 and user.member_endtime and int(user.member_endtime) < current_time:
|
||
# 更新会员状态为非会员 - 使用CRUD方法
|
||
full_exam_user.update(db, db_obj=user, obj_in={"ismember": 2})
|
||
info["ismember"] = 2
|
||
|
||
# 格式化会员到期时间
|
||
if user.member_endtime:
|
||
info["member_endtime"] = datetime.fromtimestamp(int(user.member_endtime)).strftime("%Y-%m-%d")
|
||
|
||
# 获取系统设置 - 使用CRUD替代原始SQL
|
||
system_setting = setting.get_by_fields(db, {"weid": user.weid})
|
||
|
||
# 默认用户信息状态为完整
|
||
is_ok = 1
|
||
|
||
# 检查用户信息是否完整
|
||
if system_setting and system_setting.info_status == 1:
|
||
if not user.phone or not user.name:
|
||
is_ok = 2
|
||
|
||
# 添加系统设置信息
|
||
if system_setting:
|
||
info["IOS"] = system_setting.IOS
|
||
info["customer_service"] = system_setting.customer_service
|
||
|
||
# 检查会员系统是否开启 - 使用CRUD替代原始SQL
|
||
member_setting = user_member.get_by_fields(db, {
|
||
"weid": user.weid,
|
||
"istatus": 1
|
||
})
|
||
info["is_open"] = member_setting.status if member_setting else 0
|
||
|
||
# 用户信息完整性状态
|
||
info["is_ok"] = is_ok
|
||
|
||
# 默认学生信息
|
||
info["stu_have"] = 2
|
||
info["student_id"] = 0
|
||
info["xuesheng_id"] = 0
|
||
|
||
# 如果用户有姓名和手机号,检查是否是学生 - 使用CRUD替代原始SQL
|
||
if user.phone and user.name:
|
||
student = xueshen.get_by_fields(db, {
|
||
"weid": user.weid,
|
||
"name": user.name,
|
||
"phone": user.phone
|
||
})
|
||
|
||
if student:
|
||
info["student_id"] = student.student_name
|
||
info["xuesheng_id"] = student.xuesheng_id
|
||
info["stu_have"] = 1
|
||
|
||
# 检查是否绑定微信
|
||
info["is_bind"] = 1 if user.openid else 0
|
||
|
||
return {"info": info, "http": http}
|
||
|
||
# 检查学生信息
|
||
async def check_student(uid: str, data: Dict[str, Any], db: Session) -> Dict[str, Any]:
|
||
"""检查学生信息是否存在"""
|
||
phone = data.get("phone")
|
||
name = data.get("name")
|
||
|
||
if not phone or not name:
|
||
raise ValueError("传递的参数不存在")
|
||
|
||
|
||
|
||
user = full_exam_user.get(db, int(uid))
|
||
if not user:
|
||
raise ValueError("用户不存在")
|
||
|
||
|
||
student = xueshen.get_by_fields(db, {
|
||
"weid": user.weid,
|
||
"name": name,
|
||
"phone": phone
|
||
})
|
||
|
||
if not student:
|
||
raise ValueError("学生信息不存在")
|
||
|
||
# 将结果转换为字典
|
||
student_info = {
|
||
"xuesheng_id": student.xuesheng_id,
|
||
"name": student.name,
|
||
"phone": student.phone,
|
||
"student_name": student.student_name,
|
||
"sex": student.sex
|
||
}
|
||
|
||
return {"list": student_info}
|
||
|
||
# 获取验证码
|
||
async def get_verification_code(uid: str, data: Dict[str, Any], db: Session) -> Dict[str, Any]:
|
||
"""发送手机验证码"""
|
||
phone = data.get("phone")
|
||
|
||
# 验证手机号
|
||
if not validate_phone(phone):
|
||
raise ValueError("手机号有误")
|
||
|
||
|
||
user = full_exam_user.get(db, int(uid))
|
||
if not user:
|
||
raise ValueError("用户不存在")
|
||
|
||
# 使用CRUD替代SQL查询系统配置
|
||
system_config = setting.get_by_fields(db, {"weid": user.weid})
|
||
|
||
# 生成4位随机验证码
|
||
import random
|
||
code = ''.join(random.choices('0123456789', k=4))
|
||
|
||
# 发送验证码 - 这里需要替换为实际的短信发送逻辑
|
||
try:
|
||
# 假设这是成功发送
|
||
send_result = {"Code": "OK"}
|
||
|
||
if send_result["Code"] == "OK":
|
||
# 保存验证码记录
|
||
|
||
from mooc.schemas.goouc_fullexam import PhoneCodeCreate
|
||
|
||
phone_code_data = PhoneCodeCreate(
|
||
phone=phone,
|
||
code=int(code), # 确保代码和模型匹配
|
||
createtime=int(datetime.now().timestamp()),
|
||
weid=user.weid
|
||
)
|
||
|
||
phone_code.create(db, obj_in=phone_code_data)
|
||
|
||
return {"status": 1}
|
||
else:
|
||
raise ValueError(f"发送失败: {send_result.get('Message', '未知错误')}")
|
||
except Exception as e:
|
||
logger.error(f"发送验证码失败: {str(e)}")
|
||
raise ValueError(f"发送验证码失败: {str(e)}")
|
||
|
||
# 检查验证码
|
||
async def check_verification_code(uid: str, data: Dict[str, Any], db: Session) -> Dict[str, Any]:
|
||
"""验证手机验证码"""
|
||
input_code = data.get("code", "").strip()
|
||
phone = data.get("phone")
|
||
|
||
if not input_code or not phone:
|
||
raise ValueError("传递的参数不存在")
|
||
|
||
# 获取最新的验证码记录
|
||
from sqlalchemy import text
|
||
code_query = text("""
|
||
SELECT * FROM ims_goouc_fullexam_phonecode
|
||
WHERE weid = :weid AND phone = :phone
|
||
ORDER BY createtime DESC LIMIT 1
|
||
""")
|
||
|
||
user = full_exam_user.get(db, int(uid))
|
||
if not user:
|
||
raise ValueError("用户不存在")
|
||
|
||
code_record = db.execute(code_query, {
|
||
"weid": user.weid,
|
||
"phone": phone
|
||
}).fetchone()
|
||
|
||
if not code_record:
|
||
raise ValueError("验证码不存在")
|
||
|
||
# 检查验证码是否过期(5分钟有效期)
|
||
current_time = int(datetime.now().timestamp())
|
||
if code_record.createtime + 300 < current_time:
|
||
raise ValueError("验证码已过期")
|
||
|
||
# 验证码是否正确
|
||
if str(code_record.code) != input_code:
|
||
raise ValueError("验证码错误")
|
||
|
||
# 使用CRUD检查手机号是否已绑定其他账号
|
||
other_users = full_exam_user.get_multi_by_fields(db, {
|
||
"weid": user.weid,
|
||
"phone": phone
|
||
})
|
||
|
||
# 过滤掉当前用户
|
||
other_users = [u for u in other_users if u.id != int(uid)]
|
||
|
||
if other_users:
|
||
raise ValueError("该手机号已绑定其他账号!")
|
||
|
||
# 更新用户手机号
|
||
try:
|
||
# 使用CRUD更新用户手机号
|
||
full_exam_user.update(db, db_obj=user, obj_in={"phone": phone})
|
||
|
||
# 如果用户有姓名,尝试更新学生信息
|
||
if user.name:
|
||
# 查找学生记录
|
||
student = xueshen.get_by_fields(db, {
|
||
"weid": user.weid,
|
||
"name": user.name
|
||
})
|
||
|
||
# 如果找到学生记录,更新其手机号
|
||
if student:
|
||
xueshen.update(db, db_obj=student, obj_in={"phone": phone})
|
||
|
||
return {"status": "success"}
|
||
except Exception as e:
|
||
logger.error(f"更新手机号失败: {str(e)}")
|
||
db.rollback()
|
||
raise ValueError(f"更新手机号失败: {str(e)}")
|
||
|
||
# 更新用户信息
|
||
async def update_user_info(uid: str, data: Dict[str, Any], db: Session) -> Dict[str, Any]:
|
||
"""更新用户基本信息"""
|
||
if not uid:
|
||
raise ValueError("传递的参数不存在")
|
||
|
||
# 获取用户
|
||
user = full_exam_user.get_by_id_and_weid(db, int(uid), settings.WECHAT_UNIACID)
|
||
if not user or user.istatus != 1:
|
||
raise ValueError("非法用户ID")
|
||
|
||
# 需要更新的字段
|
||
update_data = {}
|
||
|
||
# 姓名字段
|
||
if "name" in data:
|
||
update_data["name"] = data.get("name", "").strip()
|
||
|
||
# 处理其他可能需要更新的字段
|
||
fields = ["student_id", "id_card", "school", "level", "phone"]
|
||
for field in fields:
|
||
if field in data and data.get(field) != "null":
|
||
update_data[field] = data.get(field)
|
||
|
||
# 如果没有要更新的数据
|
||
if not update_data:
|
||
return {"status": 0, "message": "没有提供要更新的数据"}
|
||
|
||
try:
|
||
# 更新用户信息
|
||
full_exam_user.update(db, db_obj=user, obj_in=update_data)
|
||
|
||
# 如果更新了姓名,并且用户有手机号,则尝试更新学生信息
|
||
if "name" in update_data and user.phone:
|
||
from sqlalchemy import text
|
||
update_student_query = text("""
|
||
UPDATE ims_goouc_fullexam_xuesheng
|
||
SET name = :name
|
||
WHERE weid = :weid AND phone = :phone
|
||
""")
|
||
|
||
db.execute(update_student_query, {
|
||
"name": update_data["name"],
|
||
"weid": user.weid,
|
||
"phone": user.phone
|
||
})
|
||
|
||
return {"status": 1, "message": "信息保存成功"}
|
||
except Exception as e:
|
||
logger.error(f"更新用户信息失败: {str(e)}")
|
||
db.rollback()
|
||
raise ValueError(f"信息保存失败: {str(e)}")
|
||
|
||
async def handle_exam_operation(
|
||
data: WxappRequest,
|
||
db: Session,
|
||
user_doexam: CRUDUserDoexam,
|
||
user_exam_answer: CRUDUserExamAnswer
|
||
):
|
||
"""处理考试相关操作"""
|
||
operations = {
|
||
"submit": submit_exam,
|
||
"get_history": get_exam_history,
|
||
"get_detail": get_exam_detail,
|
||
}
|
||
|
||
operation = operations.get(data.op)
|
||
if not operation:
|
||
return {
|
||
"code": 1,
|
||
"message": f"Unsupported operation: {data.op}"
|
||
}
|
||
|
||
try:
|
||
result = await operation(data.uid, data.data, db, user_doexam, user_exam_answer)
|
||
return {
|
||
"code": 0,
|
||
"data": result,
|
||
"message": "success"
|
||
}
|
||
except Exception as e:
|
||
return {
|
||
"code": 1,
|
||
"message": str(e)
|
||
}
|
||
|
||
async def handle_collection(
|
||
data: WxappRequest,
|
||
db: Session,
|
||
user_collection: CRUDUserCollectionPraction
|
||
):
|
||
"""处理收藏相关操作"""
|
||
operations = {
|
||
"add": add_collection,
|
||
"remove": remove_collection,
|
||
"list": list_collections,
|
||
}
|
||
|
||
operation = operations.get(data.op)
|
||
if not operation:
|
||
return {
|
||
"code": 1,
|
||
"message": f"Unsupported operation: {data.op}"
|
||
}
|
||
|
||
try:
|
||
result = await operation(data.uid, data.data, db, user_collection)
|
||
return {
|
||
"code": 0,
|
||
"data": result,
|
||
"message": "success"
|
||
}
|
||
except Exception as e:
|
||
return {
|
||
"code": 1,
|
||
"message": str(e)
|
||
}
|
||
|
||
async def handle_wrong_question(
|
||
data: WxappRequest,
|
||
db: Session,
|
||
user_wrong_praction: CRUDUserWrongPraction
|
||
):
|
||
"""处理错题相关操作"""
|
||
operations = {
|
||
"add": add_wrong_question,
|
||
"remove": remove_wrong_question,
|
||
"list": list_wrong_questions,
|
||
}
|
||
|
||
operation = operations.get(data.op)
|
||
if not operation:
|
||
return {
|
||
"code": 1,
|
||
"message": f"Unsupported operation: {data.op}"
|
||
}
|
||
|
||
try:
|
||
result = await operation(data.uid, data.data, db, user_wrong_praction)
|
||
return {
|
||
"code": 0,
|
||
"data": result,
|
||
"message": "success"
|
||
}
|
||
except Exception as e:
|
||
return {
|
||
"code": 1,
|
||
"message": str(e)
|
||
}
|
||
|
||
|
||
async def handle_login2(data: WxappRequest, db: Session):
|
||
"""处理微信小程序登录2"""
|
||
logger.info(f"处理登录请求: {data.uid}")
|
||
try:
|
||
# 修改这里,查找code的位置
|
||
# 首先检查data.data中是否有code
|
||
code = data.data.get("code") if isinstance(data.data, dict) else None
|
||
|
||
# 如果在data.data中没有找到code,尝试直接从data对象获取
|
||
if not code and hasattr(data, "code"):
|
||
code = data.code
|
||
|
||
logger.debug(f"登录请求数据: {data}")
|
||
|
||
if not code:
|
||
logger.warning(f"登录失败: code为空 - UID: {data.uid}")
|
||
return {
|
||
"code": 1,
|
||
"message": "code值获取失败",
|
||
"data": {"message": "code值获取失败"}
|
||
}
|
||
|
||
try:
|
||
# 调用微信API获取openid和session_key
|
||
logger.debug(f"开始调用微信API: code={code}")
|
||
wx_info = await wx_api.code2session(code)
|
||
logger.debug(f"微信API返回结果: {wx_info}")
|
||
except httpx.ConnectTimeout:
|
||
# 特别处理连接超时错误
|
||
logger.error("连接微信服务器超时,请检查网络连接")
|
||
return {
|
||
"code": 1,
|
||
"message": "连接微信服务器超时,请稍后再试",
|
||
"data": {"message": "网络连接问题,无法连接到微信服务器"}
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"微信API调用失败: {str(e)}", exc_info=True)
|
||
return {
|
||
"code": 1,
|
||
"message": "获取用户信息失败",
|
||
"data": {"message": str(e)}
|
||
}
|
||
|
||
# 检查是否有错误
|
||
if wx_info.get("errcode") == 4001:
|
||
return {
|
||
"code": 1,
|
||
"message": "获取用户信息失败",
|
||
"data": {"message": wx_info.get("errmessage")}
|
||
}
|
||
# 确保weid是整数
|
||
try:
|
||
weid = int(settings.WECHAT_UNIACID)
|
||
except ValueError:
|
||
# 如果无法转换为整数,使用默认值
|
||
logger.warning(f"WECHAT_UNIACID '{settings.WECHAT_UNIACID}' 无法转换为整数,使用默认值1")
|
||
weid = 1
|
||
|
||
# 查询用户 - 注意这里是用openid和weid查询
|
||
users = full_exam_user.get_by_openid_and_weid(
|
||
db,
|
||
wx_info["openid"],
|
||
weid # 使用整数weid
|
||
)
|
||
|
||
info_status = 2 # 默认状态
|
||
|
||
# 构建用户数据
|
||
current_time = int(datetime.now().timestamp())
|
||
user_data = {
|
||
"openid": wx_info["openid"],
|
||
"nickname": data.data.get("nickName"),
|
||
"nativeplace": f"{data.data.get('province')},{data.data.get('city')}",
|
||
"headimg": data.data.get("avatarUrl"),
|
||
"unionid": wx_info.get("unionid"),
|
||
"createtime": current_time,
|
||
"weid": weid, # 现在weid已定义
|
||
"last_login_time": current_time,
|
||
"h5_openid": wx_info["openid"]
|
||
}
|
||
|
||
if not users:
|
||
logger.debug(f"用户不存在,创建新用户: {user_data}")
|
||
# 创建新用户 - 使用CRUD实例
|
||
try:
|
||
logger.debug(f"创建新用户: {user_data}")
|
||
# 使用UserMemberCreate schema需要导入
|
||
from mooc.schemas.goouc_fullexam_user import FullExamUserCreate
|
||
|
||
# 转换为schema对象
|
||
user_create_schema = FullExamUserCreate(**user_data)
|
||
|
||
# 使用CRUD实例创建用户
|
||
new_user = full_exam_user.create(db, obj_in=user_create_schema)
|
||
uid = new_user.id
|
||
except Exception as e:
|
||
logger.error(f"创建新用户失败: {e}")
|
||
return {
|
||
"code": 1,
|
||
"message": "用户数据插入失败",
|
||
"data": str(e)
|
||
}
|
||
|
||
# 处理二维码生成
|
||
try:
|
||
logger.debug(f"开始生成二维码")
|
||
access_token = await wx_api.get_access_token()
|
||
|
||
# 创建二维码目录
|
||
qrcode_path = "../addons/goouc_fullexam/Qrcode/"
|
||
if not os.path.exists(qrcode_path):
|
||
os.makedirs(qrcode_path, mode=0o777, exist_ok=True)
|
||
|
||
filename = f"qrcode_{uid}.jpg"
|
||
|
||
# 生成二维码并保存
|
||
res = await wx_api.save_unlimited_qrcode(
|
||
uid,
|
||
qrcode_path,
|
||
filename,
|
||
access_token
|
||
)
|
||
|
||
if res:
|
||
# 更新用户二维码路径 - 使用CRUD实例
|
||
from mooc.schemas.goouc_fullexam_user import FullExamUserUpdate
|
||
|
||
update_data = {"qrcode": os.path.join(qrcode_path, filename)}
|
||
update_schema = FullExamUserUpdate(**update_data)
|
||
|
||
# 使用CRUD实例更新用户
|
||
full_exam_user.update(db, db_obj=new_user, obj_in=update_schema)
|
||
|
||
except Exception as e:
|
||
logger.error(f"生成二维码错误: {e}")
|
||
return {
|
||
"code": 1,
|
||
"message": "生成二维码错误了",
|
||
"data": str(e)
|
||
}
|
||
|
||
else:
|
||
logger.debug(f"用户存在,更新用户信息: {users}")
|
||
# 检查用户状态
|
||
if users.status != 1:
|
||
return {
|
||
"code": 1,
|
||
"message": "您已被禁用,请联系管理员处理~",
|
||
"data": users
|
||
}
|
||
|
||
# 更新用户信息 - 使用CRUD实例
|
||
try:
|
||
logger.debug(f"开始更新用户信息: {user_data}")
|
||
from mooc.schemas.goouc_fullexam_user import FullExamUserUpdate
|
||
|
||
update_schema = FullExamUserUpdate(**user_data)
|
||
full_exam_user.update(db, db_obj=users, obj_in=update_schema)
|
||
|
||
uid = users.id
|
||
if users.phone:
|
||
info_status = 1
|
||
except Exception as e:
|
||
logger.error(f"更新用户信息失败: {e}")
|
||
return {
|
||
"code": 1,
|
||
"message": "更新用户信息失败",
|
||
"data": str(e)
|
||
}
|
||
|
||
logger.info(f"登录成功,用户ID: {uid}, 状态: {info_status}")
|
||
|
||
return ResponseFactory.success(
|
||
data={"uid": uid, "info_status": info_status, "info": {"uid": uid, "info_status": info_status}},
|
||
message="登录成功"
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.exception(f"登录过程发生未处理异常: {str(e)}")
|
||
return ResponseFactory.error(
|
||
code=1,
|
||
message=f"登录失败: {str(e)}",
|
||
data=None
|
||
) |