123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- 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
|