【目录结构】
APPS中的表数据
feeder表(model没有数据)
1 url.py 2 3 4 from django.contrib import admin 5 from django.urls import path, include 6 from rest_framework.routers import SimpleRouter 7 from .views import ( 8 FeederLoginView, 9 FeederInfoView, 10 FeederOrdersView, 11 FeederRemunerationView, 12 ) 13 14 15 router = SimpleRouter() 16 router.register("login", FeederLoginView, "login") 17 router.register("feederinfo", FeederInfoView, "feederinfo") 18 router.register("orders", FeederOrdersView, "orders") 19 router.register("remuneration", FeederRemunerationView, "remuneration") 20 21 22 urlpatterns = [path("", include(router.urls))] 23 24 25 ---------------------------------------------------------------------------- 26 27 view.py 28 29 from utils.common_response import APIResponse 30 from rest_framework.viewsets import GenericViewSet 31 from user.serializer import LoginSerializer 32 from django.contrib import auth 33 from rest_framework.decorators import action 34 from rest_framework_simplejwt.authentication import JWTAuthentication 35 from rest_framework.permissions import IsAuthenticated 36 from user.models import User, FeederRemuneration 37 import json 38 from rest_framework.response import Response 39 from order.models import Order 40 from datetime import datetime 41 from decimal import Decimal 42 43 44 # 多方式登录接口 45 class FeederLoginView(GenericViewSet): 46 serializer_class = LoginSerializer 47 48 def _login(self, request, *args, **kwargs): 49 serializer = self.get_serializer(data=request.data) 50 serializer.is_valid(raise_exception=True) 51 token = serializer.context.get("token") 52 user = serializer.context.get("username") 53 icon = serializer.context.get("icon") 54 if user.is_pet_feeder == 1: 55 auth.login(request, user) 56 return APIResponse( 57 token=token, 58 username=user.username, 59 icon=icon, 60 msg="登录成功!", 61 data=serializer.data, 62 ) 63 else: 64 return APIResponse(code=400, msg="用户不是喂养员,无法登录!") 65 66 @action(methods=["POST"], detail=False) 67 def multiple_login(self, request, *args, **kwargs): 68 return self._login(request, *args, **kwargs) 69 70 71 class FeederInfoView(GenericViewSet): 72 authentication_classes = [JWTAuthentication] 73 permission_classes = [IsAuthenticated] 74 75 def list(self, request, *args, **kwargs): 76 user = request.user 77 feeder = user.feeder 78 if feeder: 79 data = { 80 "id": feeder.id, 81 "name": feeder.name, 82 "icon": "http://192.168.1.38:8000/media/" + str(feeder.icon), 83 "gender": feeder.gender, 84 "desc": feeder.desc, 85 "service_duration": feeder.service_duration, 86 "price": feeder.price, 87 "feed_type": feeder.feed_type, 88 } 89 return Response({"code": 100, "msg": "查询成功!", "result": data}) 90 else: 91 return Response({"code": 400, "msg": "用户不是喂养员"}) 92 93 94 class FeederOrdersView(GenericViewSet): 95 authentication_classes = [JWTAuthentication] 96 permission_classes = [IsAuthenticated] 97 98 # 待接单点击变成待上门 99 @action(methods=["PUT"], detail=False) 100 def jiedan(self, request, *args, **kwargs): 101 feeder_id = request.user.feeder_id 102 order_id = request.data.get("order_id") # 0512046476199352916 103 qs = Order.objects.filter(order_id=order_id).first() 104 qs.feeder_id = feeder_id 105 qs.order_status = 2 106 qs.save() 107 return APIResponse(msg="接单成功!") 108 109 # 待上门点击变成进行中 110 @action(methods=["PUT"], detail=False) 111 def daishangmen(self, request, *args, **kwargs): 112 order_id = request.data.get("order_id") 113 qs = Order.objects.filter(order_id=order_id).first() 114 qs.order_status = 3 115 qs.save() 116 return APIResponse(msg="确认成功!") 117 118 # 进行中点击变成已完成 119 @action(methods=["PUT"], detail=False) 120 def jinxingzhong(self, request, *args, **kwargs): 121 order_id = request.data.get("order_id") 122 qs = Order.objects.filter(order_id=order_id).first() 123 qs.order_status = 4 124 qs.complete_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") 125 qs.save() 126 127 remuneration = qs.actual_payment - qs.actual_payment * Decimal("0.2") 128 129 feeder_obj = FeederRemuneration.objects.filter(feeder=qs.feeder).first() 130 feeder_obj.remuneration += remuneration 131 feeder_obj.save() 132 133 return APIResponse(msg="订单完成!") 134 135 136 class FeederRemunerationView(GenericViewSet): 137 authentication_classes = [JWTAuthentication] 138 permission_classes = [IsAuthenticated] 139 140 def list(self, request, *args, **kwargs): 141 user = request.user.feeder_id 142 qs = FeederRemuneration.objects.filter(feeder=user).first() 143 remuneration = qs.remuneration 144 return APIResponse(msg="查询成功!", remuneration=remuneration)
home表数据
1 admin.py 2 3 from django.contrib import admin 4 5 # Register your models here. 6 from .models import Banner 7 8 9 @admin.register(Banner) 10 class BannerAdmin(admin.ModelAdmin): 11 list_display = ("title", "image", "link", "info", "description") 12 13 --------------------------------------------------------------------------- 14 apps.py 15 16 from django.apps import AppConfig 17 18 19 class HomeConfig(AppConfig): 20 default_auto_field = "django.db.models.BigAutoField" 21 name = "home" 22 verbose_name = "首页" 23 24 25 ----------------------------------------------------------------------------- 26 models.py 27 28 from django.db import models 29 30 from utils.common_models import BaseModel 31 32 33 class Banner(BaseModel): 34 title = models.CharField(max_length=64, unique=True, verbose_name="名称") 35 image = models.ImageField(upload_to="banner", verbose_name="图片") 36 link = models.CharField(max_length=64, verbose_name="跳转链接") 37 info = models.TextField(verbose_name="详情") 38 description = models.TextField(null=True, blank=True, verbose_name="介绍") 39 40 class Meta: 41 db_table = "puddingpet_banner" 42 verbose_name = "轮播图" 43 verbose_name_plural = verbose_name 44 45 ---------------------------------------------------------------------------------- 46 serializers.py 47 48 from rest_framework import serializers 49 from .models import Banner 50 51 52 class BannerSerializer(serializers.ModelSerializer): 53 class Meta: 54 model = Banner 55 fields = "__all__" 56 57 58 ------------------------------------------------------------------------------------- 59 url.py 60 61 from django.contrib import admin 62 from django.urls import path, include 63 from rest_framework.routers import SimpleRouter 64 from .views import BannerView 65 66 router = SimpleRouter() 67 router.register("banner", BannerView, "banner") 68 69 70 urlpatterns = [path("", include(router.urls))] 71 72 ------------------------------------------------------------------------------------ 73 viwe.py 74 75 from django.shortcuts import render 76 from rest_framework.viewsets import GenericViewSet 77 from utils.common_mixin import CacheListModelMixin 78 from .models import Banner 79 from django.conf import settings 80 from .serializer import BannerSerializer 81 82 83 class BannerView(GenericViewSet, CacheListModelMixin): 84 cache_key = "banner_list" 85 86 87 queryset = ( 88 Banner.objects.all().filter(is_delete=False, is_show=True).order_by("orders") 89 ) 90 91 serializer_class = BannerSerializer
order表数据
1 admin.py 2 3 from django.contrib import admin 4 from .models import Order 5 6 7 @admin.register(Order) 8 class OrderAdmin(admin.ModelAdmin): 9 list_display = ( 10 "order_id", 11 "order_time", 12 "complete_time", 13 "customer", 14 "feeder", 15 "appointment_time", 16 "address", 17 "pet", 18 "remarks", 19 "actual_payment", 20 "payment_method", 21 "review", 22 "order_status", 23 ) 24 25 ------------------------------------------------------------------------------------ 26 apps.py 27 28 from django.apps import AppConfig 29 30 31 class OrderConfig(AppConfig): 32 default_auto_field = "django.db.models.BigAutoField" 33 name = "order" 34 verbose_name = "订单表" 35 36 -------------------------------------------------------------------------------------- 37 models.py 38 39 from django.db import models 40 from user.models import User, PetFeeder, UserAddress 41 import random 42 from pet.models import Pet 43 44 45 def generate_order_id(): 46 """生成19位纯数字的订单ID""" 47 return "".join(random.choices("0123456789", k=19)) 48 49 50 class Order(models.Model): 51 order_id = models.CharField( 52 max_length=19, 53 default=generate_order_id, 54 unique=True, 55 editable=False, 56 verbose_name="订单号码", 57 ) # 订单号码 随机生成 58 order_time = models.DateTimeField( 59 auto_now_add=True, verbose_name="下单时间" 60 ) # 下单时间 61 complete_time = models.DateTimeField( 62 null=True, blank=True, verbose_name="完成时间" 63 ) # 完成时间 64 customer = models.ForeignKey( 65 User, on_delete=models.CASCADE, verbose_name="下单人" 66 ) # 下单人(外键) 67 feeder = models.ForeignKey( 68 PetFeeder, 69 on_delete=models.CASCADE, 70 verbose_name="预约对象", 71 blank=True, 72 null=True, 73 ) # 预约对象(外键) 74 appointment_time = models.CharField( 75 max_length=128, verbose_name="预约上门时间" 76 ) # 预约上门时间 77 address = models.ForeignKey( 78 UserAddress, on_delete=models.CASCADE, verbose_name="预约上门地址" 79 ) # 预约上门地址(外键) 80 pet = models.ForeignKey( 81 Pet, on_delete=models.CASCADE, verbose_name="预约宠物" 82 ) # 预约宠物(外键) 83 remarks = models.TextField(blank=True, null=True, verbose_name="备注") # 备注 84 actual_payment = models.IntegerField( 85 verbose_name="实付款", blank=True, null=True 86 ) # 实付款 87 88 PAYMENT_METHOD_CHOICES = ( 89 (0, "微信支付"), 90 (1, "支付宝"), 91 (2, "当面支付"), 92 ) 93 payment_method = models.IntegerField( 94 choices=PAYMENT_METHOD_CHOICES, 95 verbose_name="支付方式", 96 blank=True, 97 null=True, 98 ) # 支付方式 99 100 review = models.CharField( 101 max_length=128, blank=True, null=True, verbose_name="订单评价" 102 ) # 订单评价 103 104 ORDER_STATUS_CHOICES = ( 105 (0, "待付款"), 106 (1, "待接单"), 107 (2, "待上门"), 108 (3, "进行中"), 109 (4, "已完成"), 110 ) 111 order_status = models.IntegerField( 112 choices=ORDER_STATUS_CHOICES, default=0, verbose_name="订单状态" 113 ) # 订单状态 114 115 class Meta: 116 db_table = "puddingpet_order" 117 verbose_name = "订单表" 118 verbose_name_plural = verbose_name 119 120 def __str__(self): 121 return f"订单{self.order_id} by {self.customer.username}" 122 123 ------------------------------------------------------------------------------ 124 pagination .py 125 126 from rest_framework.pagination import PageNumberPagination 127 128 129 class CommonPagination(PageNumberPagination): 130 page_size = 3 # 每页显示多少条 131 page_query_param = "page" # http://127.0.0.1:8000/book/bookview/?page=2 分页 132 page_size_query_param = ( 133 "size" # http://127.0.0.1:8000/book/bookview/?page=1&size=3 每页显示条数 134 ) 135 max_page_size = 10 # 不管size输入多少,最多显示10条 136 137 -------------------------------------------------------------------------------- 138 serializers.py 139 140 141 from rest_framework import serializers 142 143 from .models import Order 144 145 146 class OrderSerializer(serializers.ModelSerializer): 147 order_id = serializers.CharField(read_only=True) 148 order_time = serializers.CharField(read_only=True) # 2024-06-03 10:12:42 149 150 class Meta: 151 model = Order 152 fields = [ 153 "order_id", # 订单号码 随机生成 154 "order_time", # 下单时间 155 "complete_time", # 完成时间 156 "customer", # 下单人(外键) 157 "feeder", # 预约对象(外键) 158 "appointment_time", # 预约上门时间 159 "address", # 预约上门地址(外键) 160 "pet", # 预约宠物(外键) 161 "remarks", # 备注 162 "actual_payment", # 实付款 163 "payment_method", # 支付方式 164 "review", # 订单评价 165 "order_status", # 订单状态 166 ] 167 168 # 如果有饲养员id,就是选择下单,有price,待付款 169 # 如果没有饲养员id,就是直接下单,price=0,待接单 170 # 等有饲养员接单了,把价格设置进去,让他付款 171 def create(self, validated_data): 172 print(validated_data) 173 # {'customer': <User: heart>, 'feeder': <PetFeeder: 超级喂养员>, 'appointment_time': '2024-06-09 18:41', 174 # 'address': <UserAddress: hearttest>, 'pet': <Pet: 哈基米>, 'remarks': '321', 'actual_payment': 0} 175 feeder_obj = validated_data.get("feeder") 176 if feeder_obj: 177 order = Order.objects.create(**validated_data) 178 return order 179 else: 180 order = Order.objects.create(**validated_data) 181 order.order_status = 1 182 order.save() 183 return order 184 185 186 class OrderReviewSerializer(serializers.Serializer): 187 review = serializers.CharField() 188 189 190 class UserOrderSerializer(serializers.ModelSerializer): 191 order_id = serializers.CharField(read_only=True) 192 order_time = serializers.CharField(read_only=True) # 2024-06-03 10:12:42 193 payment_method = serializers.CharField(source="get_payment_method_display") 194 order_status = serializers.CharField(source="get_order_status_display") 195 196 class Meta: 197 model = Order 198 fields = [ 199 "order_id", # 订单号码 随机生成 200 "order_time", # 下单时间 201 "complete_time", # 完成时间 202 "customer", # 下单人(外键) 203 "feeder", # 预约对象(外键) 204 "appointment_time", # 预约上门时间 205 "address", # 预约上门地址(外键) 206 "pet", # 预约宠物(外键) 207 "remarks", # 备注 208 "actual_payment", # 实付款 209 "payment_method", # 支付方式 210 "review", # 订单评价 211 "order_status", # 订单状态 212 ] 213 depth = 1 214 215 ------------------------------------------------------------------------------ 216 tasks.py 217 218 from celery import shared_task 219 import time 220 from .models import Order, PetFeeder 221 import redis 222 223 redis_client = redis.StrictRedis(host="127.0.0.1", port=6379, db=0) 224 225 226 @shared_task 227 def qiangdan(order_id, feeder_id, feeder_price): 228 # Redis 分布式锁 229 lock_key = f"lock:order:{order_id}" # 生成一个唯一的锁键(key)。 使用订单 ID 作为锁的标识符,这样每个订单都有一个唯一的锁,可以防止同一个订单被多次处理。 230 lock_timeout = 5 # 锁的超时时间,单位是秒 231 lock = redis_client.lock( 232 lock_key, timeout=lock_timeout 233 ) # 创建一个 Redis 锁对象。 初始化锁对象,以便后续操作(获取和释放锁)。 234 235 if lock.acquire( 236 blocking=False 237 ): # 尝试获取锁 blocking=False 表示非阻塞方式获取锁。如果锁当前被其他任务持有,立即返回 False,否则返回 True 并成功获取锁。 238 # 确保任务能在锁可用时立即开始处理订单。如果锁不可用,则不会等待,而是立即返回 False,表示获取锁失败。 239 try: 240 print(f"获取锁成功,开始处理订单") 241 print(f"查询订单是否还存在,耗时2s") 242 time.sleep(2) 243 244 try: 245 order_obj = Order.objects.get( 246 order_id=order_id, order_status=1, feeder__isnull=True 247 ) 248 # 如果订单存在,直接进行处理 249 print("订单存在,开始处理") 250 time.sleep(1) # 模拟处理时间 251 print("下单,耗时2s") 252 feeder_obj = PetFeeder.objects.filter(id=feeder_id).first() 253 order_obj.feeder = feeder_obj 254 order_obj.order_status = 0 255 order_obj.actual_payment = feeder_price 256 order_obj.save() 257 time.sleep(2) 258 return True 259 except ( 260 Order.DoesNotExist 261 ): # 如果数据库中没有满足这些条件的订单对象,就会抛出 Order.DoesNotExist 异常。 262 print("订单不存在或已被抢") 263 return False 264 finally: 265 if lock.locked(): 266 try: 267 lock.release() 268 print("释放锁") 269 except ( 270 redis.exceptions.LockNotOwnedError # type: ignore 271 ): # Redis-Py 库中的一个异常类。当尝试释放一个不再由你持有的锁时,会抛出这个异常。 272 print("锁已经被自动释放或不再拥有,无法释放") 273 else: 274 print("抢单失败") 275 return False 276 277 ------------------------------------------------------------------------------------ 278 urls.py 279 280 from django.contrib import admin 281 from django.urls import path, include 282 from rest_framework.routers import SimpleRouter 283 from .views import OrderView, OrderReviewView, UserOrderView, QiangdanView 284 285 286 router = SimpleRouter() 287 router.register("order", OrderView, "order") 288 router.register("review", OrderReviewView, "review") 289 router.register("userorder", UserOrderView, "userorder") 290 router.register("qiangdan", QiangdanView, "qiangdan") 291 292 293 urlpatterns = [path("", include(router.urls))] 294 295 ---------------------------------------------------------------------------------- 296 views.py 297 298 from django.db.models import Q 299 from django.shortcuts import render 300 from rest_framework.generics import CreateAPIView 301 from .models import Order 302 from utils.common_logger import logger 303 from utils.common_response import APIResponse 304 from rest_framework.viewsets import GenericViewSet, ViewSet 305 from .serializer import OrderSerializer, OrderReviewSerializer, UserOrderSerializer 306 from rest_framework_simplejwt.authentication import JWTAuthentication 307 from rest_framework.permissions import IsAuthenticated 308 from user.models import User 309 from utils.common_mixin import APIListModelMixin 310 from .pagination import CommonPagination 311 from rest_framework.decorators import action 312 from .tasks import qiangdan 313 from celery.result import AsyncResult 314 from user.models import PetFeeder, UserBalance 315 316 317 class OrderView(GenericViewSet): 318 queryset = Order.objects.all() 319 serializer_class = OrderSerializer 320 authentication_classes = [JWTAuthentication] 321 permission_classes = [IsAuthenticated] 322 323 def create(self, request, *args, **kwargs): 324 print( 325 request.data 326 ) # {'customer': 'heart', 'appointment_time': '2024-06-09 18:41', 327 # 'address': 2, 'pet': '2', 'remarks': '123', 'actual_payment': 0} 328 user = User.objects.filter(pk=request.user.id).first() 329 request.data["customer"] = user.id 330 serializer = self.get_serializer(data=request.data) 331 serializer.is_valid(raise_exception=True) 332 serializer.save() 333 return APIResponse(msg="下单成功!") 334 335 # 付款 336 @action(methods=["POST"], detail=False) 337 def pay(self, request, *args, **kwargs): 338 order_id = request.data.get("order_id") 339 actual_payment = int(request.data.get("actual_payment")) 340 user_balance_obj = UserBalance.objects.filter(user=request.user).first() 341 order_obj = Order.objects.filter(order_id=order_id).first() 342 if int(user_balance_obj.balance) < actual_payment: 343 return APIResponse(code=401, msg="余额不足!请先充值!") 344 else: 345 shifukuan = int(user_balance_obj.balance) - int(actual_payment) 346 user_balance_obj.balance = shifukuan 347 user_balance_obj.save() 348 order_obj.order_status = 1 349 order_obj.payment_method = 0 350 order_obj.save() 351 return APIResponse(msg=f"支付成功!剩余金额{shifukuan}元!") 352 353 354 class OrderReviewView(GenericViewSet, APIListModelMixin): 355 queryset = Order.objects.exclude(review__isnull=True) 356 serializer_class = OrderReviewSerializer 357 358 359 class UserOrderView(GenericViewSet): 360 queryset = Order.objects.all() 361 serializer_class = UserOrderSerializer 362 pagination_class = CommonPagination 363 authentication_classes = [JWTAuthentication] 364 permission_classes = [IsAuthenticated] 365 366 def _get_user(self, request): 367 return request.user.id 368 369 def _check_data(self, qs): 370 if qs: 371 serializer = self.get_serializer(instance=qs, many=True) 372 return APIResponse(msg="查询成功!", results=serializer.data) 373 else: 374 return APIResponse(code=400, msg="没有订单哦!") 375 376 def list(self, request, *args, **kwargs): 377 user = self._get_user(request) # type: ignore 378 qs = self.paginate_queryset( 379 Order.objects.filter(customer=user).order_by("-order_time").all() 380 ) 381 return self._check_data(qs) # type: ignore 382 383 @action(methods=["GET"], detail=False) 384 def daifukuanall(self, request, *args, **kwargs): 385 qs = self.paginate_queryset( 386 Order.objects.filter(order_status=0).order_by("-order_time").all() 387 ) 388 if qs: 389 serializer = self.get_serializer(instance=qs, many=True) 390 return APIResponse(msg="查询成功!", results=serializer.data) 391 else: 392 return APIResponse(code=400, msg="没有订单哦!") 393 394 @action(methods=["GET"], detail=False) 395 def daijiedanall(self, request, *args, **kwargs): 396 qs = self.paginate_queryset( 397 Order.objects.filter( 398 Q(order_status=0) | Q(order_status=1), feeder=request.user.feeder 399 ) 400 .order_by("-order_time") 401 .all() 402 ) 403 if qs: 404 serializer = self.get_serializer(instance=qs, many=True) 405 return APIResponse(msg="查询成功!", results=serializer.data) 406 else: 407 return APIResponse(code=400, msg="没有订单哦!") 408 409 @action(methods=["GET"], detail=False) 410 def daishangmenall(self, request, *args, **kwargs): 411 qs = self.paginate_queryset( 412 Order.objects.filter(order_status=2, feeder=request.user.feeder) 413 .order_by("-order_time") 414 .all() 415 ) 416 if qs: 417 serializer = self.get_serializer(instance=qs, many=True) 418 return APIResponse(msg="查询成功!", results=serializer.data) 419 else: 420 return APIResponse(code=400, msg="没有订单哦!") 421 422 @action(methods=["GET"], detail=False) 423 def jinxingzhongall(self, request, *args, **kwargs): 424 qs = self.paginate_queryset( 425 Order.objects.filter(order_status=3, feeder=request.user.feeder) 426 .order_by("-order_time") 427 .all() 428 ) 429 if qs: 430 serializer = self.get_serializer(instance=qs, many=True) 431 return APIResponse(msg="查询成功!", results=serializer.data) 432 else: 433 return APIResponse(code=400, msg="没有订单哦!") 434 435 @action(methods=["GET"], detail=False) 436 def yiwanchengall(self, request, *args, **kwargs): 437 qs = self.paginate_queryset( 438 Order.objects.filter(order_status=4, feeder=request.user.feeder) 439 .order_by("-order_time") 440 .all() 441 ) 442 if qs: 443 serializer = self.get_serializer(instance=qs, many=True) 444 return APIResponse(msg="查询成功!", results=serializer.data) 445 else: 446 return APIResponse(code=400, msg="没有订单哦!") 447 448 # feeder__isnull=True 449 @action(methods=["GET"], detail=False) 450 def daiqiangdanall(self, request, *args, **kwargs): 451 qs = self.paginate_queryset( 452 Order.objects.filter(order_status=1, feeder__isnull=True) 453 .order_by("-order_time") 454 .all() 455 ) 456 if qs: 457 serializer = self.get_serializer(instance=qs, many=True) 458 return APIResponse(msg="查询成功!", results=serializer.data) 459 else: 460 return APIResponse(code=400, msg="没有订单哦!") 461 462 @action(methods=["GET"], detail=False) 463 def daifukuan(self, request, *args, **kwargs): 464 user = self._get_user(request) # type: ignore 465 qs = self.paginate_queryset( 466 Order.objects.filter(customer=user, order_status=0) 467 .order_by("-order_time") 468 .all() 469 ) 470 return self._check_data(qs) # type: ignore 471 472 @action(methods=["GET"], detail=False) 473 def daijiedan(self, request, *args, **kwargs): 474 user = self._get_user(request) # type: ignore 475 qs = self.paginate_queryset( 476 Order.objects.filter(customer=user, order_status=1) 477 .order_by("-order_time") 478 .all() 479 ) 480 481 return self._check_data(qs) # type: ignore 482 483 @action(methods=["GET"], detail=False) 484 def daishangmen(self, request, *args, **kwargs): 485 user = self._get_user(request) # type: ignore 486 qs = self.paginate_queryset( 487 Order.objects.filter(customer=user, order_status=2) 488 .order_by("-order_time") 489 .all() 490 ) 491 return self._check_data(qs) # type: ignore 492 493 @action(methods=["GET"], detail=False) 494 def jinxingzhong(self, request, *args, **kwargs): 495 user = self._get_user(request) # type: ignore 496 qs = self.paginate_queryset( 497 Order.objects.filter(customer=user, order_status=3) 498 .order_by("-order_time") 499 .all() 500 ) 501 return self._check_data(qs) # type: ignore 502 503 @action(methods=["GET"], detail=False) 504 def yiwancheng(self, request, *args, **kwargs): 505 user = self._get_user(request) # type: ignore 506 qs = self.paginate_queryset( 507 Order.objects.filter(customer=user, order_status=4) 508 .order_by("-order_time") 509 .all() 510 ) 511 return self._check_data(qs) # type: ignore 512 513 514 class QiangdanView(ViewSet): 515 authentication_classes = [JWTAuthentication] 516 permission_classes = [IsAuthenticated] 517 518 @action(methods=["POST"], detail=False) 519 def paidui(self, request, *args, **kwargs): 520 order_id = request.data.get("order_id") 521 feeder_price = request.data.get("feeder_price") 522 feeder_id = request.user.feeder.id 523 task_id = qiangdan.delay(str(order_id), feeder_id, feeder_price) 524 return APIResponse(msg="您正在排队", task_id=str(task_id)) 525 526 @action(methods=["GET"], detail=False) 527 def get_result(self, request, *args, **kwargs): 528 task_id = request.GET.get("task_id") 529 a = AsyncResult( 530 id=task_id 531 ) # 使用 Celery 的 AsyncResult 类,根据任务 ID 获取任务结果对象 a。 532 if a.successful(): # 检查任务是否成功完成。 533 result = a.get() # 获取任务的返回结果(True 或 False)。 534 if result: # 如果任务成功(返回 True),返回抢单成功的响应。 535 return APIResponse(success="1", msg="抢单成功!") 536 else: # 如果任务失败(返回 False),返回抢单失败的响应,并告知订单已被抢。 537 return APIResponse(success="0", msg="抢单失败!") 538 elif ( 539 a.status == "PENDING" 540 ): # 如果任务状态为 PENDING,表示任务在等待执行,返回相应的响应。 541 return APIResponse(success="2", msg="任务等待中被执行!") 542 else: 543 return APIResponse(success="3", msg="抢单任务正在执行!")
pet表
1 admin.py 2 3 from django.contrib import admin 4 from .models import * 5 6 7 # Register your models here. 8 @admin.register(Pet) 9 class PetAdmin(admin.ModelAdmin): 10 list_display = ( 11 "name", 12 "icon", 13 "pet_type", 14 "variety", 15 "gender", 16 "birthday", 17 "weight", 18 "character", 19 ) 20 21 --------------------------------------------------------------------------------------- 22 models.py 23 24 from django.db import models 25 from user.models import User 26 27 28 # Create your models here. 29 # 宠物表,一个用户可以有多只宠物,一只宠物只能属于一个用户 30 class Pet(models.Model): 31 GENDER_CHOICES = [ 32 (0, "妹妹"), 33 (1, "弟弟"), 34 ] 35 PETFEEDER_CHOICES = [(0, "猫猫"), (1, "狗狗")] 36 name = models.CharField(max_length=32, verbose_name="名字") # 名字 37 icon = models.ImageField( 38 upload_to="pet_icon", default="pet_icon/default.png", verbose_name="头像" 39 ) # 头像 40 pet_type = models.IntegerField( 41 choices=PETFEEDER_CHOICES, verbose_name="宠物类别" 42 ) # 宠物类别 43 variety = models.CharField(max_length=32, verbose_name="宠物品种") # 宠物品种 44 gender = models.IntegerField(choices=GENDER_CHOICES, verbose_name="性别") # 性别 45 birthday = models.CharField(max_length=32, verbose_name="出生日期") # 出生日期 46 weight = models.IntegerField(verbose_name="体重") # 体重 47 character = models.CharField(max_length=255, verbose_name="性格") # 性格描述 48 user = models.ForeignKey( 49 User, on_delete=models.CASCADE, verbose_name="用户" 50 ) # 对应用户 51 52 class Meta: 53 db_table = "puddingpet_pet" 54 verbose_name = "宠物表" 55 verbose_name_plural = verbose_name 56 57 def __str__(self): 58 return self.name 59 60 61 ----------------------------------------------------------------------------------- 62 serializers 63 64 from rest_framework import serializers 65 from .models import Pet 66 from user.models import User 67 68 69 class PetSerializer(serializers.ModelSerializer): 70 pet_type = serializers.CharField(source="get_pet_type_display", read_only=True) 71 gender = serializers.CharField(source="get_gender_display", read_only=True) 72 user = serializers.CharField(source="user.username", read_only=True) 73 id = serializers.CharField(read_only=True) 74 75 class Meta: 76 model = Pet 77 fields = [ 78 "id", 79 "name", 80 "icon", 81 "pet_type", 82 "variety", 83 "gender", 84 "birthday", 85 "weight", 86 "character", 87 "user", 88 ] 89 90 91 class BindPetSerializer(serializers.ModelSerializer): 92 user = serializers.CharField() 93 94 class Meta: 95 model = Pet 96 fields = [ 97 "name", 98 "icon", 99 "pet_type", 100 "variety", 101 "gender", 102 "birthday", 103 "weight", 104 "character", 105 "user", 106 ] 107 108 def create(self, validated_data): 109 user = validated_data.pop("user") 110 user_obj = User.objects.filter(pk=int(user)).first() 111 pet_obj = Pet.objects.create(**validated_data, user=user_obj) 112 return pet_obj 113 114 ------------------------------------------------------------------------------------ 115 url.py 116 117 from django.contrib import admin 118 from django.urls import path, include 119 from rest_framework.routers import SimpleRouter 120 from .views import PetView, BindPetView, PetAvatarView 121 122 router = SimpleRouter() 123 router.register("petinfo", PetView, "petinfo") 124 router.register("bindpet", BindPetView, "bindpet") 125 router.register("petavatar", PetAvatarView, "petavatar") 126 127 urlpatterns = [path("", include(router.urls))] 128 129 ----------------------------------------------------------------------------------- 130 view.py 131 132 from django.shortcuts import render 133 from rest_framework.viewsets import GenericViewSet 134 from .models import Pet 135 from .serializer import PetSerializer, BindPetSerializer 136 from utils.common_response import APIResponse 137 from rest_framework_simplejwt.authentication import JWTAuthentication 138 from rest_framework.permissions import IsAuthenticated 139 from django.core.files.storage import default_storage 140 from django.core.files.base import ContentFile 141 from rest_framework.exceptions import APIException 142 from rest_framework.decorators import action 143 144 145 class PetView(GenericViewSet): 146 queryset = Pet.objects.all() 147 serializer_class = PetSerializer 148 authentication_classes = [JWTAuthentication] 149 permission_classes = [IsAuthenticated] 150 151 @action(methods=["GET"], detail=False) 152 def all_pet(self, request, *args, **kwargs): 153 queryset = self.get_queryset() 154 serializer = self.get_serializer(queryset, many=True) 155 return APIResponse(data=serializer.data) 156 157 @action(methods=["GET"], detail=False) 158 def user_pet(self, request, *args, **kwargs): 159 user = request.user.id 160 queryset = Pet.objects.filter(user=user) 161 serializer = self.get_serializer(queryset, many=True) 162 return APIResponse(results=serializer.data) 163 164 165 class BindPetView(GenericViewSet): 166 queryset = Pet.objects.all() 167 serializer_class = BindPetSerializer 168 authentication_classes = [JWTAuthentication] 169 permission_classes = [IsAuthenticated] 170 171 # 添加宠物 172 def create(self, request, *args, **kwargs): 173 user = self.request.user.id 174 request.data["user"] = user 175 icon = request.headers.get("Icon").replace("/media/", "") 176 print(request.headers.get("Icon")) 177 print(icon) 178 serializer = self.get_serializer(data=request.data) 179 serializer.is_valid(raise_exception=True) 180 pet_obj = serializer.save() 181 pet_obj.icon = icon 182 pet_obj.save() 183 return APIResponse(msg="添加成功!") 184 185 186 class PetAvatarView(GenericViewSet): 187 def create(self, request, *args, **kwargs): 188 avatar = request.FILES.get("avatar") 189 if avatar: 190 file_name = default_storage.save( 191 f"pet_icon/" + str(avatar)[-15:], ContentFile(avatar.read()) 192 ) 193 # print(file_name) # pet_icon/716f48749ef.jpg 194 file_url = default_storage.url(file_name) # /media/pet_icon/716f48749ef.jpg 195 path = "http://192.168.1.38:8000" + file_url 196 print(path) 197 else: 198 raise APIException("服务器异常!") 199 return APIResponse(msg="上传头像成功!", file_url=path, file_name=file_name)
user表
1 admin.py 2 3 from django.contrib import admin 4 5 # Register your models here. 6 from .models import * 7 8 9 @admin.register(User) 10 class UserAdmin(admin.ModelAdmin): 11 list_display = ("id", "username", "mobile", "gender", "age", "is_pet_feeder") 12 13 14 @admin.register(UserAddress) 15 class UserAddressAdmin(admin.ModelAdmin): 16 list_display = ( 17 "name", 18 "mobile", 19 "province", 20 "city", 21 "detail", 22 "is_default", 23 ) 24 25 26 @admin.register(PetFeeder) 27 class PetFeederAdmin(admin.ModelAdmin): 28 list_display = ( 29 "name", 30 "icon", 31 "gender", 32 "desc", 33 "service_duration", 34 "price", 35 ) 36 37 ---------------------------------------------------------------------------------- 38 apps.py 39 40 from django.apps import AppConfig 41 42 43 class UserConfig(AppConfig): 44 default_auto_field = "django.db.models.BigAutoField" 45 name = "user" 46 verbose_name = "用户" 47 48 ----------------------------------------------------------------------------------- 49 models.py 50 51 from django.db import models 52 from django.contrib.auth.models import AbstractUser 53 54 55 # 用户表 56 class User(AbstractUser): 57 GENDER_CHOICES = [ 58 (0, "女"), 59 (1, "男"), 60 ] 61 PETFEEDER_CHOICES = [(0, "是"), (1, "否")] 62 mobile = models.CharField( 63 max_length=11, unique=True, verbose_name="手机号" 64 ) # 手机号 65 icon = models.ImageField( 66 upload_to="icon", default="icon/default.png", verbose_name="头像" 67 ) # 头像 68 gender = models.IntegerField( 69 choices=GENDER_CHOICES, null=True, verbose_name="性别", blank=True 70 ) # 性别 71 age = models.CharField(max_length=32, null=True, verbose_name="年龄") # 年龄 72 is_pet_feeder = models.IntegerField( 73 choices=PETFEEDER_CHOICES, default=0, verbose_name="是否喂养员" 74 ) # 是否是喂养员 75 feeder = models.OneToOneField( 76 to="PetFeeder", 77 on_delete=models.CASCADE, 78 null=True, 79 blank=True, 80 verbose_name="喂养员id", 81 ) # type: ignore 82 83 class Meta: 84 db_table = "puddingpet_user" 85 verbose_name = "用户表" 86 verbose_name_plural = verbose_name 87 88 def __str__(self): 89 return self.username 90 91 92 # 用户地址表,一个用户可以有多个地址,一个地址只能属于一个用户 93 class UserAddress(models.Model): 94 name = models.CharField(max_length=32, verbose_name="名字") # 名字 95 mobile = models.CharField(max_length=11, verbose_name="手机号") # 手机号 96 province = models.CharField(max_length=32, verbose_name="省") # 省 97 city = models.CharField(max_length=32, verbose_name="市") # 市 98 detail = models.CharField(max_length=128, verbose_name="详细地址") # 详细地址 99 is_default = models.BooleanField(default=False, verbose_name="是否默认") # 是否默认 100 user = models.ForeignKey( 101 User, on_delete=models.CASCADE, verbose_name="对应用户" 102 ) # 对应用户 103 104 class Meta: 105 db_table = "puddingpet_address" 106 verbose_name = "用户地址表" 107 verbose_name_plural = verbose_name 108 109 def __str__(self): 110 return self.name 111 112 113 # 喂养员表 114 class PetFeeder(models.Model): 115 GENDER_CHOICES = [ 116 (0, "女"), 117 (1, "男"), 118 ] 119 FEED_TYPE_CHOICES = [(0, "猫"), (1, "狗")] 120 name = models.CharField(max_length=32, verbose_name="名字") # 名字 121 icon = models.ImageField( 122 upload_to="feeder_icon", default="feeder_icon/default.png", verbose_name="头像" 123 ) # 头像 124 gender = models.IntegerField( 125 choices=GENDER_CHOICES, null=True, verbose_name="性别" 126 ) # 性别 127 desc = models.TextField(blank=True, null=True, verbose_name="简介") # 简介 128 service_duration = models.CharField( 129 max_length=255, verbose_name="服务时长" 130 ) # 服务时长 131 price = models.IntegerField(verbose_name="价格") # 价格 132 feed_type = models.IntegerField( 133 choices=FEED_TYPE_CHOICES, null=True, verbose_name="喂养类型" 134 ) # 喂养类型(猫,狗) 135 136 class Meta: 137 db_table = "puddingpet_feeder" 138 verbose_name = "喂养员表" 139 verbose_name_plural = verbose_name 140 141 def __str__(self): 142 return self.name 143 144 145 # 消息表 146 class Message(models.Model): 147 sender = models.ForeignKey( 148 User, 149 related_name="sent_messages", 150 on_delete=models.CASCADE, 151 verbose_name="发送者", 152 ) # 发送者 153 receiver = models.ForeignKey( 154 User, 155 related_name="received_messages", 156 on_delete=models.CASCADE, 157 verbose_name="接收者", 158 ) # 接收者 159 content = models.TextField(verbose_name="内容") # 内容 160 send_time = models.DateTimeField( 161 auto_now_add=True, verbose_name="发送时间" 162 ) # 发送时间 163 164 class Meta: 165 db_table = "puddingpet_message" 166 verbose_name = "消息表" 167 verbose_name_plural = verbose_name 168 169 def __str__(self): 170 return str(self.sender) 171 172 173 # 用户反馈表 174 class Feedback(models.Model): 175 image = models.ImageField( 176 upload_to="feedback", verbose_name="反馈图片", null=True, blank=True 177 ) 178 content = models.TextField(verbose_name="反馈内容") 179 180 class Meta: 181 db_table = "puddingpet_feedback" 182 verbose_name = "反馈表" 183 verbose_name_plural = verbose_name 184 185 186 class UserBalance(models.Model): 187 user = models.ForeignKey( 188 User, 189 related_name="user_balance", 190 on_delete=models.CASCADE, 191 verbose_name="用户", 192 ) 193 balance = models.IntegerField(verbose_name="用户余额") 194 195 class Meta: 196 db_table = "puddingpet_user_balance" 197 verbose_name = "用户余额表" 198 verbose_name_plural = verbose_name 199 200 201 class FeederRemuneration(models.Model): 202 feeder = models.ForeignKey( 203 PetFeeder, 204 related_name="feeder_remuneration", 205 on_delete=models.CASCADE, 206 verbose_name="喂养员", 207 ) 208 remuneration = models.DecimalField( 209 verbose_name="喂养员酬金", 210 max_digits=10, # Total number of digits (adjust as needed) 211 decimal_places=2, # Number of decimal places (adjust as needed) 212 ) 213 214 class Meta: 215 db_table = "puddingpet_feeder_remuneration" 216 verbose_name = "喂养员酬金表" 217 verbose_name_plural = verbose_name 218 219 -------------------------------------------------------------------------------- 220 serializers.py 221 222 from rest_framework import serializers 223 from django.core.cache import cache 224 from django.conf import settings 225 from rest_framework.exceptions import ValidationError, APIException 226 from rest_framework_simplejwt.tokens import RefreshToken 227 import re 228 from .models import ( 229 User, 230 Message, 231 PetFeeder, 232 UserAddress, 233 Feedback, 234 UserBalance, 235 FeederRemuneration, 236 ) 237 238 239 class CommonLoginSerializer: 240 def _get_user(self, attrs): 241 raise Exception("这个方法必须被重写") 242 243 def _get_token(self, user): 244 refresh = RefreshToken.for_user(user) 245 return str(refresh.access_token) 246 247 def _pre_data(self, token, user): 248 self.context["token"] = token 249 self.context["username"] = user 250 # self.instance=user # 当前用户,放到instance中了 251 self.context["icon"] = ( 252 settings.BACKEND_URL + "media/" + str(user.icon) 253 ) # 不带 域名前缀的 254 255 def validate(self, attrs): 256 # 1 取出用户名(手机号,邮箱)和密码 257 user = self._get_user(attrs) 258 # 2 如果存在:签发token,返回 259 token = self._get_token(user) 260 # 3 把token,用户名和icon放入context 261 self._pre_data(token, user) 262 return attrs 263 264 265 class LoginSerializer(CommonLoginSerializer, serializers.ModelSerializer): 266 username = serializers.CharField() 267 268 class Meta: 269 model = User 270 fields = ["username", "password", "icon"] 271 extra_kwargs = {"password": {"write_only": True}} # 它不做序列化 272 # depth = 1 273 274 def _get_user(self, attrs): 275 username = attrs.get("username") 276 password = attrs.get("password") 277 if re.match(r"^1[3-9][0-9]{9}$", username): 278 user = User.objects.filter(mobile=username).first() 279 elif re.match("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$", username): # type: ignore 280 user = User.objects.filter(email=username).first() 281 else: 282 user = User.objects.filter(username=username).first() 283 284 if user and user.check_password(password): 285 return user 286 else: 287 raise ValidationError("用户名或密码错误") 288 289 290 class SMSLoginSerializer(CommonLoginSerializer, serializers.Serializer): 291 code = serializers.CharField() 292 mobile = serializers.CharField() 293 294 def _get_user(self, attrs): 295 mobile = attrs.get("mobile") 296 code = attrs.get("code") 297 old_code = cache.get(f"cache_code_{mobile}") 298 assert old_code == code or (settings.DEBUG and code == "8888"), ValidationError( 299 "验证码错误" 300 ) 301 user = User.objects.filter(mobile=mobile).first() 302 assert user, ValidationError("该手机号未注册!") 303 return user 304 305 306 class RegisterSerializer(serializers.ModelSerializer): 307 code = serializers.CharField() 308 309 class Meta: 310 model = User 311 fields = ["mobile", "password", "code"] 312 313 def validate(self, attrs): 314 code = attrs.pop("code") 315 mobile = attrs.get("mobile") 316 old_code = cache.get(f"cache_code_{mobile}") 317 assert old_code == code or (settings.DEBUG and code == "8888"), ValidationError( 318 "验证码错误" 319 ) 320 attrs["username"] = mobile 321 return attrs 322 323 def create(self, validated_data): 324 user = User.objects.create_user(**validated_data) # type: ignore 325 UserBalance.objects.create(balance=0, user=user) 326 return user 327 328 329 class CheckMessageSerializer(serializers.Serializer): 330 content = serializers.CharField(read_only=True) 331 send_time = serializers.CharField(read_only=True) 332 sender = serializers.CharField() 333 receiver = serializers.CharField() 334 335 336 class PetFeederSerializer(serializers.ModelSerializer): 337 gender = serializers.CharField(source="get_gender_display") 338 feed_type = serializers.CharField(source="get_feed_type_display") 339 icon = serializers.SerializerMethodField() 340 341 class Meta: 342 model = PetFeeder 343 fields = [ 344 "id", 345 "name", 346 "icon", 347 "gender", 348 "desc", 349 "service_duration", 350 "price", 351 "feed_type", 352 ] 353 354 def get_icon(self, obj): 355 return settings.ICON_URL + obj.icon.url 356 357 358 class UserSerializer(serializers.ModelSerializer): 359 feeder = PetFeederSerializer(read_only=True) 360 gender = serializers.CharField(source="get_gender_display") 361 362 class Meta: 363 model = User 364 fields = ["id", "username", "mobile", "icon", "gender", "age", "feeder"] 365 366 367 class MessageSerializer(serializers.Serializer): 368 content = serializers.CharField() 369 send_time = serializers.SerializerMethodField(read_only=True) 370 sender = serializers.CharField() 371 receiver = serializers.CharField() 372 373 def create(self, validated_data): 374 # {'content': '123', 'sender': 'heart', 'receiver': '1'} 375 sender_name = validated_data.get("sender") 376 receiver_id = validated_data.get("receiver") 377 content = validated_data.get("content") 378 sender_obj = User.objects.filter(username=sender_name).first() 379 feeder_obj = User.objects.filter(feeder=receiver_id).first() 380 msg = Message.objects.create( 381 content=content, sender=sender_obj, receiver=feeder_obj 382 ) 383 return msg 384 385 def get_send_time(self, obj): 386 return obj.send_time.strftime("%Y-%m-%d %H:%M:%S") 387 388 389 # 地址序列化类 390 class AddressSerializer(serializers.ModelSerializer): 391 class Meta: 392 model = UserAddress 393 fields = ["id", "name", "mobile", "province", "city", "detail", "is_default"] 394 395 def create(self, validated_data): 396 user_id = self.context 397 user_obj = User.objects.filter(pk=user_id).first() 398 address = UserAddress.objects.create(**validated_data, user=user_obj) 399 return address 400 401 def update(self, instance, validated_data): 402 user = self.context 403 useraddr_obj = UserAddress.objects.filter(pk=instance.id).first() 404 for attr, value in validated_data.items(): 405 setattr(useraddr_obj, attr, value) 406 useraddr_obj.user = user 407 useraddr_obj.save() 408 return useraddr_obj 409 410 411 class UserEditNameSerializer(serializers.Serializer): 412 username = serializers.CharField() 413 gender = serializers.CharField() 414 mobile = serializers.CharField() 415 416 def update(self, instance, validated_data): 417 new_username = validated_data.get("username") 418 new_gender = validated_data.get("gender") 419 mobile = validated_data.get("mobile") 420 if new_gender == "男": 421 gender = 1 422 elif new_gender == "女": 423 gender = 0 424 user = User.objects.filter(mobile=mobile).update( 425 username=new_username, gender=gender, mobile=mobile 426 ) 427 return user 428 429 430 class UserMsgListSerializer(serializers.Serializer): 431 id = serializers.CharField(read_only=True) 432 content = serializers.CharField(read_only=True) 433 send_time = serializers.SerializerMethodField(read_only=True) 434 sender = UserSerializer(read_only=True) 435 receiver = UserSerializer(read_only=True) 436 437 def get_send_time(self, obj): 438 return obj.send_time.strftime("%Y-%m-%d %H:%M:%S") 439 440 441 class RegFeederSerializer(serializers.Serializer): 442 name = serializers.CharField() 443 gender = serializers.CharField() 444 desc = serializers.CharField() 445 service_duration = serializers.CharField() 446 price = serializers.CharField() 447 feed_type = serializers.CharField() 448 449 def create(self, validated_data): 450 user = self.context 451 petfeeder = PetFeeder.objects.create(**validated_data) 452 user_obj = User.objects.filter(pk=user).first() 453 user_obj.is_pet_feeder = 1 454 user_obj.feeder_id = petfeeder 455 user_obj.save() 456 FeederRemuneration.objects.create(remuneration=0, feeder=petfeeder) 457 return petfeeder 458 459 460 class FeedbackSerializer(serializers.ModelSerializer): 461 class Meta: 462 model = Feedback 463 fields = "__all__" 464 465 def create(self, validated_data): 466 feed_obj = Feedback.objects.create(**validated_data, image=self.context) 467 return feed_obj 468 469 470 # 查询余额序列化类 471 class UserBalanceSerializer(serializers.ModelSerializer): 472 amount = serializers.DecimalField( 473 max_digits=10, decimal_places=2, min_value=0.01, required=True 474 ) 475 476 class Meta: 477 model = UserBalance 478 fields = ["id", "amount", "balance"] 479 480 --------------------------------------------------------------------------------- 481 urls.py 482 483 from django.contrib import admin 484 from django.urls import path, include 485 from rest_framework.routers import DefaultRouter 486 from .views import ( 487 UserRegisterView, 488 UserMobileView, 489 UserLoginView, 490 MessageView, 491 PetFeederView, 492 PetFeederFilterView, 493 AddressView, 494 UserAvatarView, 495 UserInfoView, 496 UserAllAddressView, 497 UserMsgListView, 498 RegisterFeederView, 499 FeedbackView, 500 UserBalanceView, 501 ) 502 503 router = DefaultRouter() 504 505 router.register("register", UserRegisterView, "register") 506 router.register("mobile", UserMobileView, "mobile") 507 router.register("mul_login", UserLoginView, "mul_login") 508 router.register("pet_feeder", PetFeederView, "pet_feeder") 509 router.register("pet_feeder_filter", PetFeederFilterView, "pet_feeder_filter") 510 router.register("message", MessageView, "message") 511 router.register("addr", AddressView, "addr") 512 router.register("avatar", UserAvatarView, "avatar") 513 router.register("userinfo", UserInfoView, "userinfo") 514 router.register("all_addr", UserAllAddressView, "all_addr") 515 router.register("usermsglist", UserMsgListView, "usermsglist") 516 router.register("regfeeder", RegisterFeederView, "regfeeder") 517 router.register("feedback", FeedbackView, "feedback") 518 router.register("recharge", UserBalanceView, "recharge") 519 520 urlpatterns = [ 521 path("", include(router.urls)), 522 ] 523 524 ---------------------------------------------------------------------------------- 525 views.py 526 527 from django.shortcuts import get_object_or_404, render 528 from rest_framework.mixins import CreateModelMixin 529 from utils.common_logger import logger 530 from utils.common_response import APIResponse 531 from rest_framework.viewsets import GenericViewSet, ViewSet, ModelViewSet 532 from rest_framework.decorators import action 533 from .models import User, PetFeeder, UserAddress, Feedback, UserBalance 534 from django.core.cache import cache 535 from libs.tencent_sms import get_code, send_sms as sms 536 from threading import Thread 537 from utils.common_mixin import ( 538 APIListModelMixin, 539 APIRetrieveModelMixin, 540 ) 541 from .serializer import ( 542 RegisterSerializer, 543 LoginSerializer, 544 SMSLoginSerializer, 545 MessageSerializer, 546 PetFeederSerializer, 547 UserSerializer, 548 AddressSerializer, 549 UserEditNameSerializer, 550 UserMsgListSerializer, 551 RegFeederSerializer, 552 FeedbackSerializer, 553 UserBalanceSerializer, 554 ) 555 from .models import User, Message 556 from rest_framework_simplejwt.authentication import JWTAuthentication 557 from rest_framework.permissions import IsAuthenticated 558 from rest_framework.decorators import action 559 from rest_framework.exceptions import APIException 560 from django.core.files.storage import default_storage 561 from django.core.files.base import ContentFile 562 from django.contrib import auth 563 from django.db.models import Q 564 import re 565 566 567 # 发送短信接口 568 class UserMobileView(GenericViewSet): 569 @action(methods=["GET"], detail=False) 570 def check_mobile(self, request, *args, **kwargs): 571 mobile = request.query_params.get("mobile") 572 if mobile: 573 if re.match(r"^1[3-9][0-9]{9}$", mobile): 574 user = User.objects.filter(mobile=mobile).first() 575 else: 576 return APIResponse(code=400, msg="手机号不符合!") 577 if user: 578 return APIResponse(code=400, msg="手机号已存在!") 579 return APIResponse(msg="手机号不存在!") 580 return APIResponse(code=400, msg="还未填写手机号!") 581 582 @action(methods=["GET"], detail=False) 583 def send_sms(self, request, *args, **kwargs): 584 mobile = request.query_params.get("mobile") 585 code = get_code() 586 cache.set(f"cache_code_{mobile}", code) 587 t = Thread(target=sms, args=[mobile, code]) 588 t.start() 589 return APIResponse(msg="短信已发送") 590 591 592 # 多方式登录接口 593 class UserLoginView(GenericViewSet): 594 serializer_class = LoginSerializer 595 596 def get_serializer_class(self): 597 if self.action == "sms_login": 598 return SMSLoginSerializer 599 else: 600 return LoginSerializer 601 602 def _login(self, request, *args, **kwargs): 603 serializer = self.get_serializer(data=request.data) 604 serializer.is_valid(raise_exception=True) 605 token = serializer.context.get("token") 606 user = serializer.context.get("username") 607 icon = serializer.context.get("icon") 608 auth.login(request, user) 609 return APIResponse( 610 token=token, username=user.username, icon=icon, msg="登录成功!" 611 ) 612 613 @action(methods=["POST"], detail=False) 614 def multiple_login(self, request, *args, **kwargs): 615 return self._login(request, *args, **kwargs) 616 617 @action(methods=["POST"], detail=False) 618 def sms_login(self, request, *args, **kwargs): 619 return self._login(request, *args, **kwargs) 620 621 622 # 用户注册接口 623 class UserRegisterView(GenericViewSet): 624 serializer_class = RegisterSerializer 625 626 def create(self, request, *args, **kwargs): 627 serializer = self.get_serializer(data=request.data) 628 serializer.is_valid(raise_exception=True) 629 serializer.save() 630 return APIResponse(msg="注册成功!") 631 632 633 # 消息接口 634 class MessageView(GenericViewSet): 635 queryset = Message.objects.all() 636 serializer_class = MessageSerializer 637 authentication_classes = [JWTAuthentication] 638 permission_classes = [IsAuthenticated] 639 640 def list(self, request, *args, **kwargs): 641 send_name = request.query_params.get("send_name") 642 recv_id = request.query_params.get("recv_id") 643 send_obj = User.objects.filter(username=send_name).first() 644 feeder_obj = User.objects.filter(feeder=recv_id).first() 645 if send_obj and feeder_obj: 646 qs = Message.objects.filter( 647 Q(sender=send_obj, receiver=feeder_obj) 648 | Q(sender=feeder_obj, receiver=send_obj) 649 ).order_by("send_time") 650 serializer = self.get_serializer(instance=qs, many=True) 651 return APIResponse(results=serializer.data) 652 653 def create(self, request, *args, **kwargs): 654 serializer = self.get_serializer(data=request.data) 655 serializer.is_valid(raise_exception=True) 656 serializer.save() 657 return APIResponse(results=serializer.data) 658 659 660 # 查询所有喂养员 661 class PetFeederView(GenericViewSet, APIListModelMixin, APIRetrieveModelMixin): 662 queryset = PetFeeder.objects.all() 663 serializer_class = UserSerializer 664 665 def get_serializer_class(self): 666 if self.action == "list": 667 return UserSerializer 668 elif self.action == "retrieve": 669 return PetFeederSerializer 670 else: 671 return PetFeederSerializer 672 673 def get_object(self): 674 obj_id = self.kwargs.get("pk") 675 user = get_object_or_404(User, feeder=obj_id) 676 feeder_data = user.feeder 677 return feeder_data 678 679 def get_queryset(self): 680 return User.objects.filter(is_pet_feeder=1) 681 682 683 class PetFeederFilterView(ViewSet): 684 def list(self, request, *args, **kwargs): 685 radio = int(request.GET.get("radio")) 686 if radio == 2: 687 queryset = PetFeeder.objects.all() 688 else: 689 queryset = PetFeeder.objects.filter(feed_type=int(radio)) 690 serializer = PetFeederSerializer(queryset, many=True) 691 return APIResponse(results=serializer.data) 692 693 694 # 添加地址接口 695 from rest_framework.decorators import action 696 from rest_framework import status 697 698 699 class AddressView(GenericViewSet): 700 serializer_class = AddressSerializer 701 authentication_classes = [JWTAuthentication] 702 permission_classes = [IsAuthenticated] 703 704 def create(self, request, *args, **kwargs): 705 user = self.request.user.id 706 serializer = self.get_serializer(data=request.data, context=user) 707 serializer.is_valid(raise_exception=True) 708 serializer.save() 709 return APIResponse(msg="添加地址成功!") 710 711 712 # 查询当前登录用户所有地址 all_addr 713 class UserAllAddressView(GenericViewSet): 714 authentication_classes = [JWTAuthentication] 715 permission_classes = [IsAuthenticated] 716 serializer_class = AddressSerializer 717 queryset = UserAddress.objects.all() 718 719 def get_queryset(self): 720 # 获取当前登录的用户,只返回自己的地址 721 user = self.request.user 722 queryset = UserAddress.objects.filter(user=user) 723 return queryset 724 725 def list(self, request, *args, **kwargs): 726 queryset = self.filter_queryset(self.get_queryset()) 727 # 如果查询集为空,则返回一个带有自定义消息的响应 728 if not queryset.exists(): 729 return APIResponse(detail="该用户没有地址") 730 # 序列化查询集并返回响应 731 else: 732 serializer = self.get_serializer(queryset, many=True) 733 return APIResponse(msg="获取地址列表成功!", results=serializer.data) 734 735 # 修改地址 736 def update(self, request, *args, **kwargs): 737 qs = self.get_object() 738 serializer = self.get_serializer( 739 instance=qs, data=request.data, context=self.request.user 740 ) 741 serializer = self.get_serializer( 742 instance=qs, data=request.data, context=self.request.user 743 ) 744 serializer.is_valid(raise_exception=True) 745 serializer.save() 746 return APIResponse(msg="修改地址成功!") 747 748 # 删除地址 749 @action(methods=["delete"], detail=True) 750 # def delete_address(self, request, pk=None): 751 def destroy_info(self, request, *args, **kwargs): 752 print("svi") 753 address = self.get_object() 754 if address.user != request.user: 755 return APIResponse(code=400, msg="无权删除他人地址") 756 address.delete() 757 return APIResponse(msg="删除地址成功!") 758 759 760 class UserAvatarView(GenericViewSet): 761 queryset = User.objects.all() 762 763 def create(self, request, *args, **kwargs): 764 avatar = request.FILES.get("avatar") 765 username = request.data.get("user") 766 if avatar: 767 file_name = default_storage.save( 768 f"user/{username}/" + str(avatar)[-15:], ContentFile(avatar.read()) 769 ) 770 # logger.info(file_name) # logger.info(file_name) 771 file_url = default_storage.url(file_name).replace("/media/", "") 772 # print(file_url) # /media/user/heart/517b8dc72d4.jpg 773 # logger.info(file_url) # /user/heart/43aa2b8998c.jpg 774 user = User.objects.filter(username=username).first() 775 user.icon = file_url # type: ignore 776 user.save() # type: ignore 777 path = f"http://192.168.1.38:8000/media/{file_url}" 778 print(path) # http://192.168.1.38:8000/media/user/heart/237f61825e8.jpg 779 else: 780 raise APIException("服务器异常!") 781 return APIResponse(msg="上传头像成功!", path=path) 782 783 784 class UserInfoView(GenericViewSet): 785 queryset = User.objects.all() 786 serializer_class = UserSerializer 787 permission_classes = [IsAuthenticated] 788 authentication_classes = [JWTAuthentication] 789 790 def get_queryset(self): 791 return User.objects.filter(pk=self.request.user.id) # type: ignore 792 793 def get_serializer_class(self): 794 if self.action == "list": 795 return UserSerializer 796 elif self.action == "update": 797 return UserEditNameSerializer 798 else: 799 return UserEditNameSerializer 800 801 def list(self, request, *args, **kwargs): 802 qs = self.get_queryset().first() 803 serializer = self.get_serializer(instance=qs) 804 return APIResponse(result=serializer.data) 805 806 def put(self, request, *args, **kwargs): 807 serializer = self.get_serializer(data=request.data) 808 user = self.request.user 809 serializer = self.get_serializer(instance=user, data=request.data) 810 serializer.is_valid(raise_exception=True) 811 serializer.save() 812 return APIResponse(msg="修改成功!") 813 814 815 class UserMsgListView(GenericViewSet, APIListModelMixin): 816 queryset = Message.objects.all() 817 serializer_class = UserMsgListSerializer 818 permission_classes = [IsAuthenticated] 819 authentication_classes = [JWTAuthentication] 820 821 def get_queryset(self): 822 user = self.request.user 823 instance = Message.objects.filter(Q(sender=user) | Q(receiver=user)) 824 return instance 825 826 827 class RegisterFeederView(GenericViewSet): 828 serializer_class = RegFeederSerializer 829 permission_classes = [IsAuthenticated] 830 authentication_classes = [JWTAuthentication] 831 832 def create(self, request, *args, **kwargs): 833 user = self.request.user.id 834 user_obj = User.objects.filter(pk=user).first() 835 if user_obj.is_pet_feeder == 1: 836 return APIResponse(code=400, msg="已是喂养员!请勿重复注册!") 837 else: 838 serializer = self.get_serializer(data=request.data, context=user) 839 serializer.is_valid(raise_exception=True) 840 serializer.save() 841 return APIResponse(msg="注册成功!") 842 843 844 class FeedbackView(GenericViewSet): 845 serializer_class = FeedbackSerializer 846 847 def create(self, request, *args, **kwargs): 848 file = request.FILES.get("file") 849 if file: 850 file_name = default_storage.save( 851 f"feedback/" + str(file)[-15:], ContentFile(file.read()) 852 ) 853 file_url = default_storage.url(file_name) # /media/pet_icon/716f48749ef.jpg 854 path = "http://192.168.1.38:8000" + file_url 855 else: 856 raise APIException("服务器异常!") 857 return APIResponse(msg="上传图片成功!", file_name=file_name, file_url=path) 858 859 @action(methods=["POST"], detail=False) 860 def feedback(self, request, *args, **kwargs): 861 image = request.data.pop("image") 862 serializer = self.get_serializer(data=request.data, context=image) 863 serializer.is_valid(raise_exception=True) 864 serializer.save() 865 return APIResponse(msg="反馈成功!") 866 867 868 # 查询用户余额 869 class UserBalanceView(GenericViewSet): 870 serializer_class = UserBalanceSerializer 871 permission_classes = [IsAuthenticated] 872 authentication_classes = [JWTAuthentication] 873 874 def get_queryset(self): 875 return User.objects.filter(pk=self.request.user.id) 876 877 @action(methods=["GET"], detail=False) 878 def get_balance(self, request): 879 user_balance = self.get_queryset().first() 880 if user_balance: 881 from django.forms.models import model_to_dict 882 883 instance = UserBalance.objects.get(user_id=user_balance.id) 884 print(instance.balance) 885 return APIResponse(data=instance.balance) 886 return APIResponse(msg="暂无余额记录") 887 888 @action(methods=["POST"], detail=False) 889 def recharge_balance(self, request, *args, **kwargs): 890 amount = request.data.get("amount") 891 print(amount) 892 obj = self.get_queryset().first() 893 if obj: 894 user_balance = UserBalance.objects.get(user_id=obj.id) 895 print(user_balance) 896 user_balance.balance += int(amount) 897 user_balance.save() 898 return APIResponse(data=user_balance.balance) 899 else: 900 return APIResponse(msg="充值金额未提供")
1 settings.py 2 3 # -*- coding: utf-8 -*- 4 # author : heart 5 # blog_url : https://www.cnblogs.com/ssrheart/ 6 # time : 2024/5/14 7 8 SECRET_ID = 'AKIDrbEk9QZbR6Xq22vuo2A9wshMnBRjx8B1' 9 SECRET_KEY = 'jDZnuunfR1UtEjFslgVG16Tp8JNJ6kCV' 10 SMS_SDK_APPID = "1400635776" 11 SIGN_NAME = '小猿取经公众号' 12 TEMPLATE_ID = "1049981" 13 14 15 --------------------------------------------------------------------------------------- 16 17 sms.py 18 19 # -*- coding: utf-8 -*- 20 # author : heart 21 # blog_url : https://www.cnblogs.com/ssrheart/ 22 # time : 2024/5/14 23 import json 24 25 from tencentcloud.common import credential 26 from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException 27 from tencentcloud.sms.v20210111 import sms_client, models 28 from tencentcloud.common.profile.client_profile import ClientProfile 29 from tencentcloud.common.profile.http_profile import HttpProfile 30 from .settings import * 31 import random 32 33 34 # 生成n位数字的随机验证码 35 def get_code(num=4): 36 code = '' 37 for i in range(num): 38 r = random.randint(0, 9) 39 code += str(r) 40 41 return code 42 43 44 # 发送短信函数 45 def send_sms(mobile, code): 46 try: 47 cred = credential.Credential(SECRET_ID, SECRET_KEY) 48 httpProfile = HttpProfile() 49 httpProfile.reqMethod = "POST" # post请求(默认为post请求) 50 httpProfile.reqTimeout = 30 # 请求超时时间,单位为秒(默认60秒) 51 httpProfile.endpoint = "sms.tencentcloudapi.com" # 指定接入地域域名(默认就近接入) 52 53 # 非必要步骤: 54 # 实例化一个客户端配置对象,可以指定超时时间等配置 55 clientProfile = ClientProfile() 56 clientProfile.signMethod = "TC3-HMAC-SHA256" # 指定签名算法 57 clientProfile.language = "en-US" 58 clientProfile.httpProfile = httpProfile 59 60 # 实例化要请求产品(以sms为例)的client对象 61 # 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 62 client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile) 63 req = models.SendSmsRequest() 64 # 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看 65 req.SmsSdkAppId = SMS_SDK_APPID 66 # 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 67 # 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看 68 req.SignName = SIGN_NAME 69 # 模板 ID: 必须填写已审核通过的模板 ID 70 # 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看 71 req.TemplateId = TEMPLATE_ID 72 # 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,,若无模板参数,则设置为空 73 req.TemplateParamSet = [code, '1'] 74 # 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号] 75 req.PhoneNumberSet = [f"+86{mobile}"] 76 # 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 77 req.SessionContext = "" 78 # 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] 79 req.ExtendCode = "" 80 # 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId,无需填写该字段。注:月度使用量达到指定量级可申请独立 SenderId 使用,详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 81 req.SenderId = "" 82 resp = client.SendSms(req) 83 # 输出json格式的字符串回包 84 res = json.loads(resp.to_json_string(indent=2)) 85 if res.get('SendStatusSet')[0].get('Code') == 'Ok': 86 return True 87 else: 88 return False 89 90 except TencentCloudSDKException as err: 91 print(err) 92 return False 93 94 95 if __name__ == '__main__': 96 print(get_code(3))
1 common_settings.py 2 3 BANNER_COUNT = 3 4 5 # ============== celery配置 ============== # 6 CELERY_BROKER_URL = "redis://127.0.0.1:6379/3" 7 # BACKEND配置,使用redis 8 CELERY_RESULT_BACKEND = "redis://127.0.0.1:6379/4" 9 CELERY_ACCEPT_CONTENT = ["json"] 10 CELERY_TASK_SERIALIZER = "json" 11 # 结果序列化方案 12 CELERY_RESULT_SERIALIZER = "json" 13 # 任务结果过期时间,秒 14 CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 15 # 时区配置 16 CELERY_TIMEZONE = "Asia/Shanghai" 17 18 ---------------------------------------------------------------------- 19 dev.py 20 21 # 22 # ┌─┐ ┌─┐ + + 23 # ┌──┘ ┴───────┘ ┴──┐++ 24 # │ │ 25 # │ ─── │++ + + + 26 # ███████───███████ │+ 27 # │ │+ 28 # │ ─┴─ │ 29 # │ │ 30 # └───┐ ┌───┘ 31 # │ │ 32 # │ │ + + 33 # │ │ 34 # │ └──────────────┐ 35 # │ │ 36 # │ ├─┐ 37 # │ ┌─┘ 38 # │ │ 39 # └─┐ ┐ ┌───────┬──┐ ┌──┘ + + + + 40 # │ ─┤ ─┤ │ ─┤ ─┤ 41 # └──┴──┘ └──┴──┘ + + + + 42 # 神兽保佑 43 # 代码无BUG! 44 45 from datetime import timedelta 46 from pathlib import Path 47 import sys, os 48 49 BASE_DIR = Path(__file__).resolve().parent.parent 50 apps = os.path.join(BASE_DIR, "apps") 51 sys.path.insert(0, apps) 52 sys.path.insert(0, str(BASE_DIR)) 53 54 SECRET_KEY = "django-insecure-w^5($((r_4ug)s!k(0xymkhns%mu+4gc%lp_bi(x@yd+8mj1ym" 55 56 DEBUG = True 57 58 ALLOWED_HOSTS = ["*"] 59 60 # ============== APP配置 ============== # 61 INSTALLED_APPS = [ 62 "simpleui", 63 "django.contrib.admin", 64 "django.contrib.auth", 65 "django.contrib.contenttypes", 66 "django.contrib.sessions", 67 "django.contrib.messages", 68 "django.contrib.staticfiles", 69 "corsheaders", # 跨域 70 "rest_framework", # drf 71 "home", 72 "user", 73 "pet", 74 "order", 75 ] 76 # ============== 中间件配置 ============== # 77 MIDDLEWARE = [ 78 "django.middleware.security.SecurityMiddleware", 79 "django.contrib.sessions.middleware.SessionMiddleware", 80 "django.middleware.common.CommonMiddleware", 81 "django.middleware.csrf.CsrfViewMiddleware", 82 "django.contrib.auth.middleware.AuthenticationMiddleware", 83 "django.contrib.messages.middleware.MessageMiddleware", 84 "django.middleware.clickjacking.XFrameOptionsMiddleware", 85 "corsheaders.middleware.CorsMiddleware", # 跨域 86 ] 87 88 ROOT_URLCONF = "puddingpet_api.urls" 89 90 TEMPLATES = [ 91 { 92 "BACKEND": "django.template.backends.django.DjangoTemplates", 93 "DIRS": [], 94 "APP_DIRS": True, 95 "OPTIONS": { 96 "context_processors": [ 97 "django.template.context_processors.debug", 98 "django.template.context_processors.request", 99 "django.contrib.auth.context_processors.auth", 100 "django.contrib.messages.context_processors.messages", 101 ], 102 }, 103 }, 104 ] 105 106 WSGI_APPLICATION = "puddingpet_api.wsgi.application" 107 ASGI_APPLICATION = "puddingpet_api.asgi.application" 108 109 # ============== 数据库配置 ============== # 110 USER_NAME = os.environ.get("MYSQL_USER", "puddingpet") 111 USER_PASSWORD = os.environ.get("MYSQL_PASSWORD", "Pudding123?") 112 113 114 DATABASES = { 115 "default": { 116 "ENGINE": "django.db.backends.mysql", 117 "NAME": "puddingpet", 118 "USER": USER_NAME, 119 "PASSWORD": USER_PASSWORD, 120 "HOST": "1.94.109.143", 121 "PORT": 3306, 122 "CHARSET": "utf8mb4", 123 } 124 } 125 126 AUTH_PASSWORD_VALIDATORS = [ 127 { 128 "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 129 }, 130 { 131 "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 132 }, 133 { 134 "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 135 }, 136 { 137 "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 138 }, 139 ] 140 141 # ============== 时区配置 ============== # 142 LANGUAGE_CODE = "zh-hans" 143 TIME_ZONE = "Asia/Shanghai" 144 USE_I18N = True 145 USE_TZ = False 146 147 # ============== 静态文件配置 ============== # 148 STATIC_URL = "static/" 149 150 # ============== models配置 ============== # 151 # 用于指定模型中自动生成主键字段的默认类型 152 # 如果没有在模型中明确指定主键字段类型,Django 将使用 BigAutoField 作为默认类型。 153 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 154 155 # ============== 跨域配置 ============== # 156 CORS_ORIGIN_ALLOW_ALL = True 157 158 CORS_ALLOW_HEADERS = ( 159 "accept", 160 "accept-encoding", 161 "authorization", 162 "content-type", 163 "dnt", 164 "origin", 165 "user-agent", 166 "x-csrftoken", 167 "x-requested-with", 168 # 额外允许的请求头 169 "token", 170 ) 171 172 # ============== auth表 ============== # 173 AUTH_USER_MODEL = "user.User" 174 175 # ============== media设置 ============== # 176 MEDIA_URL = "/media/" 177 MEDIA_ROOT = os.path.join(BASE_DIR, "media") 178 179 # ============== 日志 ============== # 180 from utils import common_logger 181 182 common_logger.configure_logger() 183 184 185 # ============== 异常捕获 ============== # 186 REST_FRAMEWORK = { 187 "EXCEPTION_HANDLER": "utils.common_exception.exception_handler", 188 "DEFAULT_AUTHENTICATION_CLASSES": ( 189 "rest_framework.authentication.TokenAuthentication", 190 ), 191 } 192 from .common_settings import * 193 194 # import socket 195 196 # ============== 头像拼接路径 ============== # 197 # HOST = socket.gethostbyname(socket.gethostname()) 198 BACKEND_URL = "http://192.168.1.38:8000/" 199 ICON_URL = "http://192.168.1.38:8000" 200 201 # ============== JWT配置 ============== # 202 SIMPLE_JWT = { 203 "ACCESS_TOKEN_LIFETIME": timedelta(days=7), # Access Token的有效期 204 } 205 # ============== django-redis/cache配置 ============== # 206 CACHES = { 207 "default": { 208 "BACKEND": "django_redis.cache.RedisCache", 209 "LOCATION": "redis://127.0.0.1:6379", 210 "OPTIONS": { 211 "CLIENT_CLASS": "django_redis.client.DefaultClient", 212 "CONNECTION_POOL_KWARGS": {"max_connections": 100}, 213 }, 214 } 215 } 216 217 218 ---------------------------------------------------------------------------------- 219 pro.py 220 221 # 222 # ┌─┐ ┌─┐ + + 223 # ┌──┘ ┴───────┘ ┴──┐++ 224 # │ │ 225 # │ ─── │++ + + + 226 # ███████───███████ │+ 227 # │ │+ 228 # │ ─┴─ │ 229 # │ │ 230 # └───┐ ┌───┘ 231 # │ │ 232 # │ │ + + 233 # │ │ 234 # │ └──────────────┐ 235 # │ │ 236 # │ ├─┐ 237 # │ ┌─┘ 238 # │ │ 239 # └─┐ ┐ ┌───────┬──┐ ┌──┘ + + + + 240 # │ ─┤ ─┤ │ ─┤ ─┤ 241 # └──┴──┘ └──┴──┘ + + + + 242 # 神兽保佑 243 # 代码无BUG! 244 245 from datetime import timedelta 246 from pathlib import Path 247 import sys, os 248 249 BASE_DIR = Path(__file__).resolve().parent.parent 250 apps = os.path.join(BASE_DIR, "apps") 251 sys.path.insert(0, apps) 252 sys.path.insert(0, str(BASE_DIR)) 253 254 SECRET_KEY = "django-insecure-w^5($((r_4ug)s!k(0xymkhns%mu+4gc%lp_bi(x@yd+8mj1ym" 255 256 DEBUG = True 257 258 ALLOWED_HOSTS = ["*"] 259 260 # ============== APP配置 ============== # 261 INSTALLED_APPS = [ 262 "simpleui", 263 "django.contrib.admin", 264 "django.contrib.auth", 265 "django.contrib.contenttypes", 266 "django.contrib.sessions", 267 "django.contrib.messages", 268 "django.contrib.staticfiles", 269 "corsheaders", # 跨域 270 "rest_framework", # drf 271 "home", 272 ] 273 # ============== 中间件配置 ============== # 274 MIDDLEWARE = [ 275 "django.middleware.security.SecurityMiddleware", 276 "django.contrib.sessions.middleware.SessionMiddleware", 277 "django.middleware.common.CommonMiddleware", 278 "django.middleware.csrf.CsrfViewMiddleware", 279 "django.contrib.auth.middleware.AuthenticationMiddleware", 280 "django.contrib.messages.middleware.MessageMiddleware", 281 "django.middleware.clickjacking.XFrameOptionsMiddleware", 282 "corsheaders.middleware.CorsMiddleware", # 跨域 283 ] 284 285 ROOT_URLCONF = "puddingpet_api.urls" 286 287 TEMPLATES = [ 288 { 289 "BACKEND": "django.template.backends.django.DjangoTemplates", 290 "DIRS": [], 291 "APP_DIRS": True, 292 "OPTIONS": { 293 "context_processors": [ 294 "django.template.context_processors.debug", 295 "django.template.context_processors.request", 296 "django.contrib.auth.context_processors.auth", 297 "django.contrib.messages.context_processors.messages", 298 ], 299 }, 300 }, 301 ] 302 303 WSGI_APPLICATION = "puddingpet_api.wsgi.application" 304 305 306 # ============== 数据库配置 ============== # 307 USER_NAME = os.environ.get("MYSQL_USER", "puddingpet") 308 USER_PASSWORD = os.environ.get("MYSQL_PASSWORD", "Pudding123?") 309 310 311 DATABASES = { 312 "default": { 313 "ENGINE": "django.db.backends.mysql", 314 "NAME": "puddingpet", 315 "USER": USER_NAME, 316 "PASSWORD": USER_PASSWORD, 317 "HOST": "1.94.109.143", 318 "PORT": 3306, 319 "CHARSET": "utf8mb4", 320 } 321 } 322 323 AUTH_PASSWORD_VALIDATORS = [ 324 { 325 "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 326 }, 327 { 328 "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 329 }, 330 { 331 "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 332 }, 333 { 334 "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 335 }, 336 ] 337 338 # ============== 时区配置 ============== # 339 LANGUAGE_CODE = "zh-hans" 340 TIME_ZONE = "Asia/Shanghai" 341 USE_I18N = True 342 USE_TZ = False 343 344 # ============== 静态文件配置 ============== # 345 STATIC_URL = "static/" 346 347 # ============== models配置 ============== # 348 # 用于指定模型中自动生成主键字段的默认类型 349 # 如果没有在模型中明确指定主键字段类型,Django 将使用 BigAutoField 作为默认类型。 350 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 351 352 # ============== 跨域配置 ============== # 353 CORS_ORIGIN_ALLOW_ALL = True 354 355 CORS_ALLOW_HEADERS = ( 356 "accept", 357 "accept-encoding", 358 "authorization", 359 "content-type", 360 "dnt", 361 "origin", 362 "user-agent", 363 "x-csrftoken", 364 "x-requested-with", 365 # 额外允许的请求头 366 "token", 367 ) 368 369 # 设置auth表 370 # AUTH_USER_MODEL = "user.User" 371 372 # 开启media 373 MEDIA_URL = "/media/" 374 MEDIA_ROOT = os.path.join(BASE_DIR, "media") 375 376 # 日志 377 # from utils import common_logger 378 379 # common_logger.configure_logger() 380 381 382 # ============== 异常捕获 ============== # 383 REST_FRAMEWORK = { 384 "EXCEPTION_HANDLER": "utils.common_exception.exception_handler", 385 } 386 from .common_settings import * 387 388 # ============== 头像拼接路径 ============== # 389 BACKEND_URL = "http://127.0.0.1:8000/" 390 391 # ============== JWT配置 ============== # 392 SIMPLE_JWT = { 393 "ACCESS_TOKEN_LIFETIME": timedelta(days=7), # Access Token的有效期 394 }
1 common_exception.py 2 3 from rest_framework.views import exception_handler as drf_exception_handler 4 from rest_framework.response import Response 5 from utils.common_response import APIResponse 6 from utils.common_logger import logger 7 8 9 # 自定义异常类 10 class PasswordException(Exception): 11 pass 12 13 14 def exception_handler(exc, context): 15 res = drf_exception_handler(exc, context) 16 print(res) 17 request = context.get("request") 18 view = context.get("view") 19 ip = request.META.get("REMOTE_ADDR") 20 path = request.get_full_path() 21 method = request.method 22 user_id = request.user.id or "匿名用户" 23 logger.error( 24 f"操作出错!{str(exc)},视图类:{str(view)},ip:{ip},请求地址:{path},请求方式:{method},用户id:{user_id}" 25 ) 26 27 if res: 28 # drf异常 29 if isinstance(res.data, dict): 30 err = ( 31 res.data.get("detail") 32 or res.data.get("non_field_errors") 33 or "请正确输入!" 34 ) 35 elif isinstance(res.data, list): 36 err = res.data[0] 37 else: 38 err = "服务异常,请稍后再尝试,[drf]" 39 # print(res.data) 40 response = APIResponse(code=4000, msg=err) 41 else: 42 # 非drf异常 43 if isinstance(exc, ZeroDivisionError): 44 err = "数据操作出错,除以0了" 45 code = 4001 46 elif isinstance(exc, PasswordException): 47 err = "密码错误!" 48 code = 4002 49 else: 50 err = f"{str(exc)}" 51 code = 4004 52 response = APIResponse(code=code, msg=err) 53 54 return response 55 56 --------------------------------------------------------------------------------- 57 common_logger.py 58 59 from loguru import logger 60 import os 61 62 LOG_PATH = os.path.join("logs", "puddingpet.log") 63 64 65 def configure_logger(): 66 # logger.remove() # 清除默认的日志处理器 67 # logger.level("CRITICAL") 68 logger.add(f"{LOG_PATH}", rotation="300 MB", level="ERROR") 69 70 ---------------------------------------------------------------------------------- 71 common_mixin.py 72 73 from rest_framework.mixins import ( 74 CreateModelMixin, 75 ListModelMixin, 76 UpdateModelMixin, 77 DestroyModelMixin, 78 RetrieveModelMixin, 79 ) 80 from utils.common_response import APIResponse 81 from django.core.cache import cache 82 from utils.common_logger import logger 83 from rest_framework.exceptions import APIException 84 85 86 class APIListModelMixin(ListModelMixin): 87 def list(self, request, *args, **kwargs): 88 res = super().list(request, *args, **kwargs) 89 return APIResponse(results=res.data) 90 91 92 class CacheListModelMixin(ListModelMixin): 93 cache_key = None 94 95 def list(self, request, *args, **kwargs): 96 assert self.cache_key, APIException( 97 "如果继承CacheListModelMixin,必须要加cache_key!" 98 ) 99 results = cache.get(self.cache_key) 100 if not results: 101 logger.info("走了数据库") 102 res = super().list(request, *args, **kwargs) 103 results = res.data 104 cache.set(self.cache_key, results) 105 return APIResponse(results=results) 106 107 108 class APIRetrieveModelMixin(RetrieveModelMixin): 109 def retrieve(self, request, *args, **kwargs): 110 res = super().retrieve(request, *args, **kwargs) 111 return APIResponse(result=res.data) 112 113 -------------------------------------------------------------------------------------- 114 conmmon_models.py 115 116 from django.db import models 117 118 119 class BaseModel(models.Model): 120 created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") 121 updated_time = models.DateTimeField(auto_now=True, verbose_name="最后更新时间") 122 is_delete = models.BooleanField(default=False, verbose_name="是否删除") 123 is_show = models.BooleanField(default=True, verbose_name="是否上架") 124 orders = models.IntegerField(verbose_name="优先级") 125 126 class Meta: 127 abstract = True # 只用来继承,不在数据库中生成表 128 129 --------------------------------------------------------------------------------- 130 common_response.py 131 132 from rest_framework.response import Response 133 134 135 class APIResponse(Response): 136 137 def __init__(self, code=100, msg="成功!", headers=None, status=None, **kwargs): 138 data = {"code": code, "msg": msg} 139 if kwargs: 140 data.update(kwargs) 141 super().__init__(data=data, status=status, headers=headers)