from django.db import transaction from django.http import HttpResponse,JsonResponse from . import aidog_tools,aidog_request_tools,puppyStateUpdate #引入请求声纹API方法 from datetime import datetime, timedelta from django.utils import timezone import random import json import time import os import jwt from .models import InventoryInfo os.environ.setdefault("DJANGO_SETTINGS_MODULE", "aiDogProject.settings") import django django.setup() from aiDogApp.models import * from django.views.decorators.csrf import csrf_exempt import hashlib from django.core import serializers from django.core.cache import cache # 使用道具接口:客户端上传access_token,用户id、item_id,dog_list,date_time # 2025/03/12 # author Feng # 使用道具后, 所有狗的数据全部更新 @csrf_exempt def use_props(request): try: # 获取请求参数 params = aidog_request_tools.handle_post_request_parameters(request, ["access_token", "user_id", "item_id", "dog_list", "date_time"]) access_token, user_id, item_id, dog_list, interaction_type = params["access_token"], params["user_id"], params[ "item_id"], params["dog_list"], params["date_time"] time_now = timezone.make_aware(datetime.now()) # 处理dog_list参数 - 它可能是字符串形式的列表或JSON字符串 # 验证旧的access_token是否有效 try: decoded_token = jwt.decode(access_token, aidog_tools.SECRET_KEY, algorithms=['HS256']) user_id = decoded_token['user_id'] # 取出数据库的uid: try: user = User.objects.get(uid=user_id) except User.DoesNotExist: return {"status": "error", "message": f"No user found with uid={user_id}"} except jwt.ExpiredSignatureError: # 旧access_token已过期,返回失败信息 return JsonResponse({ "status": "error", "code": 400, "message": "access_token expired, please refresh again" }) # 确保dog_list是列表类型 # if not isinstance(dog_list, list): # dog_list = [dog_list] # 处理dog_list参数 - 它可能是字符串形式的列表或JSON字符串 if isinstance(dog_list, str): try: # 尝试解析为JSON dog_list = json.loads(dog_list) except json.JSONDecodeError: try: # 如果不是有效的JSON,尝试使用字面量评估 # 这种方法可以处理Python列表字符串表示 import ast dog_list = ast.literal_eval(dog_list) except (SyntaxError, ValueError): # 如果仍然失败,保持原样 pass # 在Doginfo表中过滤出dog_list中存在的d_id对应的数据 dog_info_list = Doginfo.objects.filter(owner_id=user_id, d_id__in=dog_list) # 检查是否有找到狗 if not dog_info_list.exists(): # 尝试不使用owner_id过滤,仅检查是否存在这些dog_id all_dogs = Doginfo.objects.filter(d_id__in=dog_list) if all_dogs.exists(): # 狗存在,但不属于这个用户 wrong_owner_dogs = [dog.d_id for dog in all_dogs] return JsonResponse({ "status": "error", "message": f"Dogs found but don't belong to user {user_id}: {wrong_owner_dogs}" }) else: # 狗完全不存在 return JsonResponse({ "status": "error", "message": f"No dogs found with the provided IDs: {dog_list}" }) # 获取用户道具库存 try: user_inventory = UserInventory.objects.get(owner_id=user_id, item_id=item_id) # 检查用户是否有足够的道具数量 # if user_inventory.quantity < len(dog_info_list): if user_inventory.quantity < 1: return JsonResponse({ "status": "error", "message": f"Insufficient item quantity. Required: {1}, Available: {user_inventory.quantity}" }) except UserInventory.DoesNotExist: return JsonResponse({ "status": "error", "message": f"User doesn't have this item in inventory: {item_id}" }) # 更新每只狗的状态 # 获取用户所有的狗 dog_all_list = Doginfo.objects.filter(owner_id=user_id) dogs = [] successful_updates = 0 # 提取需要更新的狗的ID列表 dog_info_d_ids = [dog.d_id for dog in dog_info_list] # 遍历所有狗数据 for dog in dog_all_list: dog_id = dog.d_id # 获取狗的信息并转换为字典 dog_serialized = serializers.serialize('json', [dog]) dog_data = json.loads(dog_serialized)[0]['fields'] # 如果当前狗在需要更新的列表中,调用更新接口 if dog_id in dog_info_d_ids: update_response = puppyStateUpdate.props_gain_state(user_id, dog_id, item_id, time_now) if update_response["status"] == "error": # 如果更新某只狗时出错,记录错误但继续处理其他狗 pass else: successful_updates += 1 # 获取狗的相关信息(无论是否更新,都获取最新状态) new_dog_status = aidog_tools.model_to_dict_without_id(DogStatus.objects.filter(d_id=dog_id).first()) new_dog_training = aidog_tools.model_to_dict_without_id(DogTrainingNew.objects.filter(d_id=dog_id).first()) new_dog_body_attributes = aidog_tools.model_to_dict_without_id( DogBodyAttributes.objects.filter(d_id=dog_id).first()) new_dog_personality_relationship = aidog_tools.model_to_dict_without_id( DogPersonalityRelationship.objects.filter(d_id=dog_id).first()) # 合并所有相关信息 dog_info_combined = { **dog_data, **new_dog_status, **new_dog_training, **new_dog_body_attributes, **new_dog_personality_relationship, } # 只保留字段而不是数据库内部的元数据(如 '_state') dog_info_clean = {key: value for key, value in dog_info_combined.items() if not key.startswith('_')} dogs.append(dog_info_clean) # 更新道具数量 (只扣除成功更新的狗的数量) if successful_updates > 0 : # user_inventory.quantity -= successful_updates #---todo==========按狗的数量扣除多个道具 if not item_id.startswith('toy_'): user_inventory.quantity -= 1 #---todo==========无论几只狗每次只扣除一个道具 # if user_inventory.quantity <= 0: # 如果道具数量为0或负数,删除库存记录 # user_inventory.delete() # else: user_inventory.save() # 获取用户所有道具库存 user_inventory_all = UserInventory.objects.filter(owner_id=user_id) props_dict = { "food": {}, "toy": {}, "other": {} } for prop in user_inventory_all: prop_serialized = serializers.serialize('json', [prop]) prop_data = json.loads(prop_serialized)[0]['fields'] if prop_data['quantity'] > 0: # Determine the category based on item_id prefix if prop_data['item_id'].startswith('food_') or prop_data['item_id'].startswith('water_'): category = 'food' elif prop_data['item_id'].startswith('toy_'): category = 'toy' else: category = 'other' # Add to the corresponding category dictionary props_dict[category][prop_data['item_id']] = prop_data['quantity'] # 处理isRegUser的布尔值转换 isRegUser = True if getattr(user, 'isRegUser', 0) == 1 else False # 成功返回结果 res = { "status": "success", "message": f"item:{str(item_id)} is used", "user_info": { "user_name": user.user_name, "coin": user.coin, "isRegUser": isRegUser, "mobile": user.mobile, "email": user.email, "level": user.level, "uuid": user.uuid, "registration_time": user.registration_time }, "status": "success", "dogs":dogs, "props": props_dict, } return JsonResponse(res) except Exception as e: return aidog_tools.handle_error(str(e)) # 购买道具接口:客户端上传access_token,用户id、item_id,qty(默认为1),date_time # 2025/05/23 # author Feng # 返回购物成功信息,用户道具表,用户信息表 @csrf_exempt def purchase_props(request): # 记录请求时间 current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') try: # 获取请求参数 params = aidog_request_tools.handle_post_request_parameters( request, ["access_token", "user_id", "item_id", "qty"] ) date_time = request.POST.get("date_time", '') access_token = params["access_token"] item_id = params["item_id"] qty = int(params.get("qty", 1)) # 默认数量为1,确保是整数 # 验证数量参数 if qty <= 0: return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": "Quantity must be greater than 0" }) # 验证数量上限(防止恶意购买) if qty > 999: return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": "Quantity cannot exceed 999" }) # 验证访问令牌 token_validation = aidog_tools.validate_access_token(access_token) if isinstance(token_validation, JsonResponse): return token_validation user_id = token_validation['user_id'] # 验证道具信息 try: # 先从缓存获取 # cache_key = f"item_info_{item_id}" # item_info = cache.get(cache_key) # # if item_info is None: # # 缓存未命中,从数据库查询 # item_info = InventoryInfo.objects.get(item_id=item_id) # # 缓存5分钟 # cache.set(cache_key, item_info, 300) item_info = InventoryInfo.objects.get(item_id=item_id) # 检查道具是否可购买 if not item_info.is_active: return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": f"Item {item_id} is not available for purchase" }) except InventoryInfo.DoesNotExist: return JsonResponse({ "status": "error", "code": 404, "date": current_time, "message": f"Item not found: {item_id}" }) # 使用数据库事务确保数据一致性 with transaction.atomic(): # 获取用户信息 try: user = User.objects.select_for_update().get(uid=user_id) except User.DoesNotExist: return JsonResponse({ "status": "error", "code": 404, "date": current_time, "message": f"User not found with uid={user_id}" }) # 计算总价格 total_cost = item_info.price * qty # 检查用户余额 if user.coin < total_cost: return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": f"Insufficient coins. Need {total_cost} coins, but only have {user.coin} coins" }) # 获取或创建用户道具库存 user_inventory, created = UserInventory.objects.select_for_update().get_or_create( owner_id=user.uid, item_id=item_id, defaults={'quantity': 0} ) # 根据道具类型检查库存上限 current_quantity = user_inventory.quantity # 定义需要检查库存上限的道具 limit_999_items = ['food_00001', 'food_00002', 'food_00003', 'water_00001', 'water_00002', 'other_00001'] limit_1_items = ['toy_00001'] if item_id in limit_999_items: # 检查是否超过999的上限 if current_quantity >= 999: return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": f"Cannot purchase {item_id}. Inventory limit of 999 has been reached (current: {current_quantity})" }) # 检查购买后是否会超过上限 if current_quantity + qty > 999: max_can_buy = 999 - current_quantity return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": f"Cannot purchase {qty} x {item_id}. Would exceed inventory limit of 999. You can only purchase {max_can_buy} more items" }) elif item_id in limit_1_items: # 检查是否超过1的上限 if current_quantity >= 1: return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": f"Cannot purchase {item_id}. Inventory limit of 1 has been reached (current: {current_quantity})" }) # 检查购买数量是否会超过上限 if qty > 1: return JsonResponse({ "status": "error", "code": 400, "date": current_time, "message": f"Cannot purchase {qty} x {item_id}. Maximum allowed quantity is 1" }) # 扣除金币并增加道具数量 user.coin -= total_cost user_inventory.quantity += qty # 保存更改 user.save(update_fields=['coin']) user_inventory.save(update_fields=['quantity']) # 获取用户所有道具库存 props_dict = _get_user_props_dict(user_id) # 构建用户信息 user_info = aidog_tools._build_user_info(user) # 成功返回结果 res = { "status": "success", "message": f"Successfully purchased {qty} x {item_id}", "user_info": user_info, "props": props_dict, } return JsonResponse(res) except Exception as e: return aidog_tools.handle_error(str(e)) # 获取用户道具字典 def _get_user_props_dict(user_id): # 先尝试从缓存获取 # cache_key = f"user_props_{user_id}" # props_dict = cache.get(cache_key) # # if props_dict is not None: # return props_dict # # # 缓存未命中,查询数据库 # 使用JOIN查询获取道具信息和库存信息 user_inventory_data = UserInventory.objects.filter( owner_id=user_id, quantity__gt=0 ).select_related().values( 'item_id', 'quantity' ) # 批量获取道具信息 item_ids = [item['item_id'] for item in user_inventory_data] item_infos = { item.item_id: item.item_type for item in InventoryInfo.objects.filter(item_id__in=item_ids) } props_dict = { "food": {}, "toy": {}, "other": {} } for inventory_item in user_inventory_data: item_id = inventory_item['item_id'] quantity = inventory_item['quantity'] # 从数据库获取道具类型 item_type = item_infos.get(item_id, 'other') props_dict[item_type][item_id] = quantity # 缓存5分钟 # cache.set(cache_key, props_dict, 300) return props_dict