From ab32f76deb3c71b1be863bd7e7c0762811a79ccf Mon Sep 17 00:00:00 2001 From: guoshijiang <guoshijiang@192.168.0.106> Date: Sun, 3 Mar 2024 01:08:02 +0800 Subject: [PATCH 1/3] airdrop grpc server --- airdrop/__init__.py | 0 airdrop/admin.py | 3 ++ airdrop/apps.py | 6 +++ airdrop/migrations/__init__.py | 0 airdrop/models.py | 57 +++++++++++++++++++++ airdrop/tests.py | 3 ++ airdrop/views.py | 3 ++ api/airdrop/__init__.py | 0 api/airdrop/api_v1.py | 0 hailstone/__init__.py | 4 -- hailstone/settings.py | 1 + services/savour_rpc/airdrop_pb2.py | 32 ++++++++++++ services/savour_rpc/airdrop_pb2_grpc.py | 66 +++++++++++++++++++++++++ 13 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 airdrop/__init__.py create mode 100644 airdrop/admin.py create mode 100644 airdrop/apps.py create mode 100644 airdrop/migrations/__init__.py create mode 100644 airdrop/models.py create mode 100644 airdrop/tests.py create mode 100644 airdrop/views.py create mode 100644 api/airdrop/__init__.py create mode 100644 api/airdrop/api_v1.py create mode 100644 services/savour_rpc/airdrop_pb2.py create mode 100644 services/savour_rpc/airdrop_pb2_grpc.py diff --git a/airdrop/__init__.py b/airdrop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/airdrop/admin.py b/airdrop/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/airdrop/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/airdrop/apps.py b/airdrop/apps.py new file mode 100644 index 0000000..06e8113 --- /dev/null +++ b/airdrop/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AirdropConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'airdrop' diff --git a/airdrop/migrations/__init__.py b/airdrop/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/airdrop/models.py b/airdrop/models.py new file mode 100644 index 0000000..178ecc9 --- /dev/null +++ b/airdrop/models.py @@ -0,0 +1,57 @@ +from django.db import models +from common.models import BaseModel, Asset + + +TypeChoice = [(x, x) for x in ['BridgeTransfer', 'BridgeStaking']] + + +class User(BaseModel): + name = models.CharField( + max_length=100, + unique=True, + verbose_name='用户名' + ) + photo = models.ImageField( + upload_to='symbol/%Y/%m/%d/', + blank=True, + null=True + ) + type = models.CharField( + max_length=100, + choices=TypeChoice, + default="BridgeTransfer", + verbose_name='交易类别' + ) + address = models.CharField( + max_length=100, + unique=True, + verbose_name='用户地址' + ) + + class Meta: + verbose_name = 'User' + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + + def as_dict(self): + return { + 'id': self.id, + 'name': self.name, + } + + +class PointsRecord(BaseModel): + address = models.CharField( + max_length=100, + unique=True, + verbose_name='用户地址' + ) + + class Meta: + verbose_name = 'PointsRecord' + verbose_name_plural = verbose_name + + def __str__(self): + return self.address diff --git a/airdrop/tests.py b/airdrop/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/airdrop/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/airdrop/views.py b/airdrop/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/airdrop/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/api/airdrop/__init__.py b/api/airdrop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/airdrop/api_v1.py b/api/airdrop/api_v1.py new file mode 100644 index 0000000..e69de29 diff --git a/hailstone/__init__.py b/hailstone/__init__.py index 650b67a..e69de29 100644 --- a/hailstone/__init__.py +++ b/hailstone/__init__.py @@ -1,4 +0,0 @@ -import pymysql - -pymysql.version_info = (1, 4, 2, "final", 0) -pymysql.install_as_MySQLdb() \ No newline at end of file diff --git a/hailstone/settings.py b/hailstone/settings.py index 0000f81..9f47c6c 100644 --- a/hailstone/settings.py +++ b/hailstone/settings.py @@ -22,6 +22,7 @@ "wallet", "services", "channels", + 'airdrop', ] MIDDLEWARE = [ diff --git a/services/savour_rpc/airdrop_pb2.py b/services/savour_rpc/airdrop_pb2.py new file mode 100644 index 0000000..16ccfa4 --- /dev/null +++ b/services/savour_rpc/airdrop_pb2.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: savourrpc/airdrop.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from services.savour_rpc import common_pb2 as savourrpc_dot_common__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17savourrpc/airdrop.proto\x12\x11savourrpc.airdrop\x1a\x16savourrpc/common.proto\"Y\n\x10\x44ppLinkPointsReq\x12\x16\n\x0e\x63onsumer_token\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\x05\x12\x0f\n\x07\x61\x64\x64ress\x18\x03 \x01(\t\x12\x0e\n\x06points\x18\x04 \x01(\r\"D\n\x10\x44ppLinkPointsRep\x12#\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x15.savourrpc.ReturnCode\x12\x0b\n\x03msg\x18\x02 \x01(\t2s\n\x0e\x41irdropService\x12\x61\n\x13submitDppLinkPoints\x12#.savourrpc.airdrop.DppLinkPointsReq\x1a#.savourrpc.airdrop.DppLinkPointsRep\"\x00\x42\'\n\x14group.savour.airdropZ\x0f./proto/airdropb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'savourrpc.airdrop_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\024group.savour.airdropZ\017./proto/airdrop' + _globals['_DPPLINKPOINTSREQ']._serialized_start=70 + _globals['_DPPLINKPOINTSREQ']._serialized_end=159 + _globals['_DPPLINKPOINTSREP']._serialized_start=161 + _globals['_DPPLINKPOINTSREP']._serialized_end=229 + _globals['_AIRDROPSERVICE']._serialized_start=231 + _globals['_AIRDROPSERVICE']._serialized_end=346 +# @@protoc_insertion_point(module_scope) diff --git a/services/savour_rpc/airdrop_pb2_grpc.py b/services/savour_rpc/airdrop_pb2_grpc.py new file mode 100644 index 0000000..c5e5a9b --- /dev/null +++ b/services/savour_rpc/airdrop_pb2_grpc.py @@ -0,0 +1,66 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from services.savour_rpc import airdrop_pb2 as savourrpc_dot_airdrop__pb2 + + +class AirdropServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.submitDppLinkPoints = channel.unary_unary( + '/savourrpc.airdrop.AirdropService/submitDppLinkPoints', + request_serializer=savourrpc_dot_airdrop__pb2.DppLinkPointsReq.SerializeToString, + response_deserializer=savourrpc_dot_airdrop__pb2.DppLinkPointsRep.FromString, + ) + + +class AirdropServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def submitDppLinkPoints(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_AirdropServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'submitDppLinkPoints': grpc.unary_unary_rpc_method_handler( + servicer.submitDppLinkPoints, + request_deserializer=savourrpc_dot_airdrop__pb2.DppLinkPointsReq.FromString, + response_serializer=savourrpc_dot_airdrop__pb2.DppLinkPointsRep.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'savourrpc.airdrop.AirdropService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class AirdropService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def submitDppLinkPoints(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/savourrpc.airdrop.AirdropService/submitDppLinkPoints', + savourrpc_dot_airdrop__pb2.DppLinkPointsReq.SerializeToString, + savourrpc_dot_airdrop__pb2.DppLinkPointsRep.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) From a2964b3452e5b87fc80777fae1aac132353e87ce Mon Sep 17 00:00:00 2001 From: guoshijiang <guoshijiang@guoshijiangdeMacBook-Pro.local> Date: Mon, 4 Mar 2024 11:27:06 +0800 Subject: [PATCH 2/3] airdrop grpc server development --- airdrop/admin.py | 14 +++- airdrop/airdrop_server.py | 36 +++++++++ airdrop/management/__init__.py | 0 airdrop/management/commands/__init__.py | 0 airdrop/management/commands/airdrop_server.py | 20 +++++ airdrop/migrations/0001_initial.py | 54 +++++++++++++ airdrop/models.py | 79 +++++++++++++++++-- api/airdrop/api.sh | 4 + api/airdrop/api_v1.py | 46 +++++++++++ api/urls.py | 7 ++ 10 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 airdrop/airdrop_server.py create mode 100644 airdrop/management/__init__.py create mode 100644 airdrop/management/commands/__init__.py create mode 100644 airdrop/management/commands/airdrop_server.py create mode 100644 airdrop/migrations/0001_initial.py create mode 100644 api/airdrop/api.sh diff --git a/airdrop/admin.py b/airdrop/admin.py index 8c38f3f..0a019ff 100644 --- a/airdrop/admin.py +++ b/airdrop/admin.py @@ -1,3 +1,15 @@ +#encoding=utf-8 + from django.contrib import admin +from airdrop.models import ( + AirdropUser, + PointsRecord +) + +@admin.register(AirdropUser) +class AddressAmountStatAdmin(admin.ModelAdmin): + list_display = ('id', 'name', 'address', 'points') -# Register your models here. +@admin.register(PointsRecord) +class ChainAdmin(admin.ModelAdmin): + list_display = ('id', 'address', 'type', 'points') diff --git a/airdrop/airdrop_server.py b/airdrop/airdrop_server.py new file mode 100644 index 0000000..7c6684f --- /dev/null +++ b/airdrop/airdrop_server.py @@ -0,0 +1,36 @@ +#encoding=utf-8 + +import pytz +from airdrop.models import AirdropUser, PointsRecord +from services.savour_rpc import airdrop_pb2_grpc, common_pb2, airdrop_pb2 +from django.conf import settings + +tz = pytz.timezone(settings.TIME_ZONE) + +class AirdropServer(airdrop_pb2_grpc.AirdropServiceServicer): + def submitDppLinkPoints(self, request, context) -> airdrop_pb2.DppLinkPointsResponse: + airdrop_user: AirdropUser + type = str(request.type) + address = str(request.address) + points = int(request.points) + airdrop_tmp_user = AirdropUser.objects.filter(address=address).first() + if airdrop_tmp_user is not None: + airdrop_tmp_user.points += points + airdrop_tmp_user.save() + airdrop_user = airdrop_tmp_user + else: + airdrop_user = AirdropUser.objects.create( + address=address, + points=points + ) + PointsRecord.objects.create( + user=airdrop_user, + address=address, + type=type, + points=points + ) + return airdrop_pb2.DppLinkPointsResponse( + code=common_pb2.SUCCESS, + msg="submit dapplink points success", + ) + diff --git a/airdrop/management/__init__.py b/airdrop/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/airdrop/management/commands/__init__.py b/airdrop/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/airdrop/management/commands/airdrop_server.py b/airdrop/management/commands/airdrop_server.py new file mode 100644 index 0000000..db1eb43 --- /dev/null +++ b/airdrop/management/commands/airdrop_server.py @@ -0,0 +1,20 @@ +#encoding=utf-8 + +import grpc +from django.core.management.base import BaseCommand +from concurrent import futures +from services.savour_rpc import airdrop_pb2_grpc +from airdrop.airdrop_server import AirdropServer + + +class Command(BaseCommand): + def handle(self, *args, **options): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + airdrop_pb2_grpc.add_AirdropServiceServicer_to_server( + AirdropServer(), + server + ) + server.add_insecure_port('[::]:50251') + server.start() + print("airdrop rpc server start") + server.wait_for_termination() \ No newline at end of file diff --git a/airdrop/migrations/0001_initial.py b/airdrop/migrations/0001_initial.py new file mode 100644 index 0000000..f3f8315 --- /dev/null +++ b/airdrop/migrations/0001_initial.py @@ -0,0 +1,54 @@ +# Generated by Django 4.1.1 on 2024-03-03 03:11 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='AirdropUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.CharField(blank=True, max_length=100, null=True, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), + ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), + ('name', models.CharField(default='unknown', max_length=100, unique=True, verbose_name='用户名')), + ('photo', models.ImageField(blank=True, null=True, upload_to='symbol/%Y/%m/%d/')), + ('address', models.CharField(max_length=100, unique=True, verbose_name='用户地址')), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('points', models.PositiveIntegerField(default=0, verbose_name='积分数量')), + ('x_twitter', models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='x')), + ('discord', models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='discord')), + ('telegram', models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='discord')), + ('info', models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='个人介绍')), + ], + options={ + 'verbose_name': 'User', + 'verbose_name_plural': 'User', + }, + ), + migrations.CreateModel( + name='PointsRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.CharField(blank=True, max_length=100, null=True, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), + ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), + ('address', models.CharField(max_length=100, unique=True, verbose_name='用户地址')), + ('type', models.CharField(choices=[('BridgeTransfer', 'BridgeTransfer'), ('BridgeStaking', 'BridgeStaking')], default='BridgeTransfer', max_length=100, verbose_name='交易类别')), + ('points', models.PositiveIntegerField(default=0, verbose_name='积分数量')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='airdrop_user', to='airdrop.airdropuser', verbose_name='收藏的商家')), + ], + options={ + 'verbose_name': 'PointsRecord', + 'verbose_name_plural': 'PointsRecord', + }, + ), + ] diff --git a/airdrop/models.py b/airdrop/models.py index 178ecc9..5f86ca7 100644 --- a/airdrop/models.py +++ b/airdrop/models.py @@ -5,8 +5,9 @@ TypeChoice = [(x, x) for x in ['BridgeTransfer', 'BridgeStaking']] -class User(BaseModel): +class AirdropUser(BaseModel): name = models.CharField( + default="unknown", max_length=100, unique=True, verbose_name='用户名' @@ -16,17 +17,47 @@ class User(BaseModel): blank=True, null=True ) - type = models.CharField( - max_length=100, - choices=TypeChoice, - default="BridgeTransfer", - verbose_name='交易类别' - ) address = models.CharField( max_length=100, unique=True, verbose_name='用户地址' ) + email = models.EmailField( + blank=True, + null=True + ) + points = models.PositiveIntegerField( + default=0, + verbose_name="积分数量" + ) + x_twitter = models.CharField( + max_length=100, + default="", + blank=True, + null=True, + verbose_name="x", + ) + discord = models.CharField( + max_length=100, + default="", + blank=True, + null=True, + verbose_name="discord", + ) + telegram = models.CharField( + max_length=100, + default="", + blank=True, + null=True, + verbose_name="discord", + ) + info = models.CharField( + max_length=100, + default="", + blank=True, + null=True, + verbose_name="个人介绍", + ) class Meta: verbose_name = 'User' @@ -39,15 +70,41 @@ def as_dict(self): return { 'id': self.id, 'name': self.name, + 'photo': str(self.photo), + 'address': self.address, + 'email': self.email, + 'points': self.points, + 'x_twitter': self.x_twitter, + 'discord': self.discord, + 'telegram': self.telegram, + 'info': self.info } class PointsRecord(BaseModel): + user = models.ForeignKey( + AirdropUser, + related_name="airdrop_user", + on_delete=models.CASCADE, + blank=True, + null=True, + verbose_name="收藏的商家", + ) address = models.CharField( max_length=100, unique=True, verbose_name='用户地址' ) + type = models.CharField( + max_length=100, + choices=TypeChoice, + default="BridgeTransfer", + verbose_name='交易类别' + ) + points = models.PositiveIntegerField( + default=0, + verbose_name="积分数量" + ) class Meta: verbose_name = 'PointsRecord' @@ -55,3 +112,11 @@ class Meta: def __str__(self): return self.address + + def as_dict(self): + return { + 'id': self.id, + 'name': self.address, + 'type': self.type, + 'points': self.points + } diff --git a/api/airdrop/api.sh b/api/airdrop/api.sh new file mode 100644 index 0000000..72d49bc --- /dev/null +++ b/api/airdrop/api.sh @@ -0,0 +1,4 @@ +curl --location --request POST 'http://127.0.0.1:8000/api/get_points_by_address' --header 'Content-Type: application/json' --data-raw '{"address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F0" }' + + +curl --location --request POST 'http://127.0.0.1:8000/api/get_points_record_by_address' --header 'Content-Type: application/json' --data-raw '{"address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F0" }' \ No newline at end of file diff --git a/api/airdrop/api_v1.py b/api/airdrop/api_v1.py index e69de29..b88d372 100644 --- a/api/airdrop/api_v1.py +++ b/api/airdrop/api_v1.py @@ -0,0 +1,46 @@ +#encoding=utf-8 + +import json +from common.helpers import ( + ok_json, + error_json +) +from airdrop.models import ( + AirdropUser, + PointsRecord +) + +# @check_api_token +def get_points_by_address(request): + params = json.loads(request.body.decode()) + address = params.get("address", None) + if address is None: + return error_json("address is empty", 4000) + airdrop_user = AirdropUser.objects.filter(address=address).first() + if airdrop_user is not None: + return ok_json(airdrop_user.as_dict()) + else: + return error_json("No this user address points", 4000) + +# @check_api_token +def get_points_record_by_address(request): + params = json.loads(request.body.decode()) + address = params.get("address", None) + if address is None: + return error_json("address is empty", 4000) + page = params.get('page', 1) + page_size = params.get('page_size', 20) + start = (page - 1) * page_size + end = start + page_size + points = PointsRecord.objects.filter(address=address).order_by("-id")[start:end] + total = PointsRecord.objects.filter(address=address).order_by("-id").count() + point_list = [] + for point in points: + point_list.append(point.as_dict()) + data = { + "total": total, + "points": point_list, + } + return ok_json(data) + + diff --git a/api/urls.py b/api/urls.py index 5bfbdf7..8d73114 100644 --- a/api/urls.py +++ b/api/urls.py @@ -52,6 +52,10 @@ get_arcticle_detail, like_article ) +from api.airdrop.api_v1 import ( + get_points_by_address, + get_points_record_by_address, +) urlpatterns = [ # Hd wallet module @@ -100,4 +104,7 @@ path(r'set_recovery_key', set_recovery_key, name='set_recovery_key'), path(r'get_tokens', get_tokens, name='get_tokens'), + # airdrop + path(r'get_points_by_address', get_points_by_address, name='get_points_by_address'), + path(r'get_points_record_by_address', get_points_record_by_address, name='get_points_record_by_address'), ] \ No newline at end of file From 41e7ae17dec7be7f592fc08409888a23578407c9 Mon Sep 17 00:00:00 2001 From: guoshijiang <guoshijiang@192.168.0.103> Date: Wed, 13 Mar 2024 13:30:05 +0800 Subject: [PATCH 3/3] finish airdrop api development --- airdrop/migrations/0001_initial.py | 10 +-- airdrop/models.py | 18 ++++- airdrop_api.md | 111 +++++++++++++++++++++++++++++ api/airdrop/api.sh | 4 -- api/airdrop/api_v1.py | 55 ++++++++++++-- api/urls.py | 4 ++ 6 files changed, 186 insertions(+), 16 deletions(-) create mode 100644 airdrop_api.md delete mode 100644 api/airdrop/api.sh diff --git a/airdrop/migrations/0001_initial.py b/airdrop/migrations/0001_initial.py index f3f8315..4217d5f 100644 --- a/airdrop/migrations/0001_initial.py +++ b/airdrop/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.1.1 on 2024-03-03 03:11 +# Generated by Django 4.1.1 on 2024-03-13 05:29 from django.db import migrations, models import django.db.models.deletion @@ -19,7 +19,9 @@ class Migration(migrations.Migration): ('uuid', models.CharField(blank=True, max_length=100, null=True, unique=True)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), - ('name', models.CharField(default='unknown', max_length=100, unique=True, verbose_name='用户名')), + ('name', models.CharField(default='unknown', max_length=100, verbose_name='用户名')), + ('invite_code', models.CharField(default='0000-0000-0000', max_length=100, unique=True, verbose_name='邀请码')), + ('invite_me_uuid', models.CharField(default='0000-0000-0000', max_length=100, verbose_name='邀请人')), ('photo', models.ImageField(blank=True, null=True, upload_to='symbol/%Y/%m/%d/')), ('address', models.CharField(max_length=100, unique=True, verbose_name='用户地址')), ('email', models.EmailField(blank=True, max_length=254, null=True)), @@ -41,8 +43,8 @@ class Migration(migrations.Migration): ('uuid', models.CharField(blank=True, max_length=100, null=True, unique=True)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), - ('address', models.CharField(max_length=100, unique=True, verbose_name='用户地址')), - ('type', models.CharField(choices=[('BridgeTransfer', 'BridgeTransfer'), ('BridgeStaking', 'BridgeStaking')], default='BridgeTransfer', max_length=100, verbose_name='交易类别')), + ('address', models.CharField(max_length=100, verbose_name='用户地址')), + ('type', models.CharField(choices=[('Invite', 'Invite'), ('BridgeTransfer', 'BridgeTransfer'), ('BridgeStaking', 'BridgeStaking')], default='BridgeTransfer', max_length=100, verbose_name='交易类别')), ('points', models.PositiveIntegerField(default=0, verbose_name='积分数量')), ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='airdrop_user', to='airdrop.airdropuser', verbose_name='收藏的商家')), ], diff --git a/airdrop/models.py b/airdrop/models.py index 5f86ca7..b26a372 100644 --- a/airdrop/models.py +++ b/airdrop/models.py @@ -2,16 +2,28 @@ from common.models import BaseModel, Asset -TypeChoice = [(x, x) for x in ['BridgeTransfer', 'BridgeStaking']] +TypeChoice = [(x, x) for x in ['Invite', 'BridgeTransfer', 'BridgeStaking']] class AirdropUser(BaseModel): name = models.CharField( default="unknown", max_length=100, - unique=True, + unique=False, verbose_name='用户名' ) + invite_code = models.CharField( + default="0000-0000-0000", + max_length=100, + unique=True, + verbose_name='邀请码' + ) + invite_me_uuid = models.CharField( + default="0000-0000-0000", + max_length=100, + unique=False, + verbose_name='邀请人' + ) photo = models.ImageField( upload_to='symbol/%Y/%m/%d/', blank=True, @@ -92,7 +104,7 @@ class PointsRecord(BaseModel): ) address = models.CharField( max_length=100, - unique=True, + unique=False, verbose_name='用户地址' ) type = models.CharField( diff --git a/airdrop_api.md b/airdrop_api.md new file mode 100644 index 0000000..ce6d3d2 --- /dev/null +++ b/airdrop_api.md @@ -0,0 +1,111 @@ +# 空投接口 + +## 1. 获取邀请码 + +接口请求 +``` +curl --location 'http://127.0.0.1:8000/api/get_invite_code_by_address' \ +--header 'Content-Type: application/json' \ +--data '{ + "address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F0" +}' +``` +返回值 + +``` +{ + "ok": true, + "code": 200, + "result": { + "invite_code": "0000-0000-0000" + } +} +``` + + +## 2. 提交邀请信息 + +接口请求 +``` +curl --location 'http://127.0.0.1:8000/api/submit_invite_info' \ +--header 'Content-Type: application/json' \ +--data '{ + "invite_code": "0000-0000-0000", + "address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F1" +}' +``` +返回值 + +``` +{ + "ok": true, + "code": 200, + "result": {} +} +``` + + +## 3. 获取个人信息和积分 + +接口请求 +``` +curl --location 'http://127.0.0.1:8000/api/get_points_by_address' \ +--header 'Content-Type: application/json' \ +--data '{ + "address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F1" +}' +``` +返回值 + +``` +{ + "ok": true, + "code": 200, + "result": { + "id": 5, + "name": "unknown", + "photo": "", + "address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F1", + "email": null, + "points": 0, + "x_twitter": "", + "discord": "", + "telegram": "", + "info": "" + } +} +``` + + +## 4. 根据地址获取积分记录 + +接口请求 +``` +curl --location 'http://127.0.0.1:8000/api/get_points_record_by_address' \ +--header 'Content-Type: application/json' \ +--data '{ + "address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F0" +}' +``` +返回值 + +``` +{ + "ok": true, + "code": 200, + "result": { + "total": 1, + "points": [ + { + "id": 1, + "name": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F0", + "type": "BridgeTransfer", + "points": 1 + } + ] + } +} +``` + + + diff --git a/api/airdrop/api.sh b/api/airdrop/api.sh deleted file mode 100644 index 72d49bc..0000000 --- a/api/airdrop/api.sh +++ /dev/null @@ -1,4 +0,0 @@ -curl --location --request POST 'http://127.0.0.1:8000/api/get_points_by_address' --header 'Content-Type: application/json' --data-raw '{"address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F0" }' - - -curl --location --request POST 'http://127.0.0.1:8000/api/get_points_record_by_address' --header 'Content-Type: application/json' --data-raw '{"address": "0xe3b4ECd2EC88026F84cF17fef8bABfD9184C94F0" }' \ No newline at end of file diff --git a/api/airdrop/api_v1.py b/api/airdrop/api_v1.py index b88d372..3bc77b5 100644 --- a/api/airdrop/api_v1.py +++ b/api/airdrop/api_v1.py @@ -1,15 +1,61 @@ -#encoding=utf-8 +# encoding=utf-8 import json +import uuid + from common.helpers import ( ok_json, error_json ) from airdrop.models import ( - AirdropUser, - PointsRecord + AirdropUser, + PointsRecord ) + +# @check_api_token +def get_invite_code_by_address(request): + params = json.loads(request.body.decode()) + address = params.get("address", None) + if address is not None: + airdrop_user = AirdropUser.objects.filter(address=address).first() + if airdrop_user is not None: + data = { + "invite_code": airdrop_user.invite_code, + } + return ok_json(data) + else: + return error_json("address is not exist", 4000) + else: + return error_json("address is none", 4000) + + +# @check_api_token +def submit_invite_info(request): + params = json.loads(request.body.decode()) + address = params.get("address", None) + invite_code = params.get("invite_code", None) + if address is None or invite_code is None: + return error_json("address or invite_code params is empty", 4000) + invite_user = AirdropUser.objects.filter(invite_code=invite_code).first() + if invite_user is None: + return error_json("This user is not exist", 4000) + AirdropUser.objects.create( + invite_code=uuid.uuid4(), + invite_me_uuid=invite_user.uuid, + address=address + ) + if invite_user.points < 10: + invite_user.points = invite_user.points + 2 + invite_user.save() + PointsRecord.objects.create( + user=invite_user, + address=invite_user.address, + type='Invite', + points=2 + ) + return ok_json({}) + # @check_api_token def get_points_by_address(request): params = json.loads(request.body.decode()) @@ -22,6 +68,7 @@ def get_points_by_address(request): else: return error_json("No this user address points", 4000) + # @check_api_token def get_points_record_by_address(request): params = json.loads(request.body.decode()) @@ -42,5 +89,3 @@ def get_points_record_by_address(request): "points": point_list, } return ok_json(data) - - diff --git a/api/urls.py b/api/urls.py index 8d73114..0e9293d 100644 --- a/api/urls.py +++ b/api/urls.py @@ -55,6 +55,8 @@ from api.airdrop.api_v1 import ( get_points_by_address, get_points_record_by_address, + get_invite_code_by_address, + submit_invite_info ) urlpatterns = [ @@ -105,6 +107,8 @@ path(r'get_tokens', get_tokens, name='get_tokens'), # airdrop + path(r'get_invite_code_by_address', get_invite_code_by_address, name='get_invite_code_by_address'), + path(r'submit_invite_info', submit_invite_info, name='submit_invite_info'), path(r'get_points_by_address', get_points_by_address, name='get_points_by_address'), path(r'get_points_record_by_address', get_points_record_by_address, name='get_points_record_by_address'), ] \ No newline at end of file