This commit is contained in:
kf_wuhao 2021-04-01 14:36:39 +08:00
parent 029b88c5cd
commit 6e677eda58
9 changed files with 225 additions and 42 deletions

View File

@ -3,7 +3,7 @@ from aioredis import create_redis_pool
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from routers import point
from routers import point, user, event
from settings import settings
from utils.ta_sdk import TGAnalytics, ToKafka
@ -36,6 +36,8 @@ def register_ta(app: FastAPI) -> None:
app.include_router(point.router, prefix='/v1')
app.include_router(user.router, prefix='/v1')
app.include_router(event.router, prefix='/v1')
register_redis(app)
register_ta(app)

View File

@ -1 +1,2 @@
from .user import *
from .event import *

View File

@ -1,4 +1,6 @@
import hashlib
from enum import Enum
from pydantic import Field
from pydantic import BaseModel, validator
@ -8,8 +10,52 @@ from settings import settings
from ipaddress import IPv4Address
FIELD_MAP = {
'user_id': 'x01',
'account_id': 'x02',
'distinct_id': 'x03',
'event_name': 'x04',
'server_time':'x05',
'ip': 'a01',
'country': 'a02',
'country_code': 'a03',
'province': 'a04',
'city': 'a05',
'os_version': 'a06',
'manufacturer': 'a07',
'os': 'a08',
'device_id': 'a09',
'screen_height': 'a10',
'screen_width': 'a11',
'device_model': 'a12',
'app_version': 'a13',
'bundle_id': 'a14',
'lib': 'a15',
'lib_version': 'a16',
'network_type': 'a17',
'carrier': 'a18',
'browser': 'a19',
'browser_version': 'a20',
'duration': 'a21',
'url': 'a22',
'url_path': 'a23',
'referrer': 'a24',
'referrer_host': 'a25',
'title': 'a26',
'screen_name': 'a27',
'element_id': 'a28',
'element_type': 'a29',
'resume_from_background': 'a30',
'element_selector': 'a31',
'element_position': 'a32',
'element_content': 'a33',
'scene': 'a34',
'mp_platform': 'a35',
'app_crashed_reason': 'a36',
'zone_offset': 'a37',
'app_id':'b01',
'event_time':'b06'
}
@ -29,21 +75,33 @@ class IP4(str):
return str(v)
class ActEnum(str, Enum):
track = 'track'
user_set = 'user_set'
user_setOnce = 'user_setOnce'
user_add = 'user_add'
user_unset = 'user_unset'
user_append = 'user_append'
user_del = 'user_del'
class Base(BaseModel):
# sign = md5(distinct_id+account_id+act+ts+salt)
distinct_id: str = Field(..., title='访客 ID')
game: str
account_id: str
act: str
event_name: str = None
# preset: dict
properties: dict
ts: int
sign: str
# sign = md5(game+act+ts+salt)
game: str = Field(..., title='游戏代号')
act: ActEnum = Field(..., title='操作', description='同ta一致')
preset: BaseModel
properties: dict = Field(..., title='自定义属性')
ts: int = Field(..., title='时间戳')
sign: str = Field(..., title='签名')
@validator('sign')
def sign_validator(cls, v: str, values: dict):
s = f'{values.get("distinct_id")}{values.get("account_id", "")}{values.get("act", "")}{values.get("ts", "")}{settings.SALT}'
s = f'{values.get("game", "")}{values.get("act", "")}{values.get("ts", "")}{settings.SALT}'
if hashlib.md5(s.encode()).hexdigest() == v:
return v
raise ValueError(f'打击违法犯罪行为{hashlib.md5(s.encode()).hexdigest()}')
raise ValueError(f'sign {hashlib.md5(s.encode()).hexdigest()}')
def dict(self, **kwargs):
kwargs.setdefault('exclude', {'preset', 'account_id', 'distinct_id', 'event_name'})
self.properties.update(self.preset.dict(**kwargs))
return super().dict(**kwargs)

View File

@ -0,0 +1,70 @@
from datetime import datetime
from pydantic import BaseModel, Field
from .base import Base, IP4, to_alias
__all__ = ('EventModel',)
class Preset(BaseModel):
ip: IP4 = Field(None, title='ipv4',description='不传该字段默认使用源ip')
country: str = Field(None, title='国家',description='')
country_code: str = Field(None, title='国家代码',description='')
province: str = Field(None, title='省份',description='')
city: str = Field(None, title='城市',description='')
os_version: str = Field(None, title='操作系统版本',description='')
manufacturer: str = Field(None, title='设备制造商',description='')
os: str = Field(None, title='操作系统',description='')
device_id: str = Field(None, title='设备 ID',description='')
screen_height: int = Field(None, title='屏幕高度',description='')
screen_width: int = Field(None, title='屏幕宽度',description='')
device_model: str = Field(None, title='设备型号',description='')
app_version: str = Field(None, title='APP 版本',description='')
bundle_id: str = Field(None, title='APP包名',description='')
lib: str = Field(None, title='SDK 类型',description='')
lib_version: str = Field(None, title='SDK 版本',description='')
network_type: str = Field(None, title='网络状态',description='')
carrier: str = Field(None, title='网络运营商',description='')
browser: str = Field(None, title='浏览器类型',description='')
browser_version: str = Field(None, title='浏览器版本',description='')
duration: int = Field(None, title='事件时长',description='')
url: str = Field(None, title='页面地址',description='')
url_path: str = Field(None, title='页面路径',description='')
referrer: str = Field(None, title='前向地址',description='')
referrer_host: str = Field(None, title='前向路径',description='')
title: str = Field(None, title='页面标题',description='')
screen_name: str = Field(None, title='页面名称',description='')
element_id: str = Field(None, title='元素 ID',description='')
element_type: str = Field(None, title='元素类型',description='')
resume_from_background: str = Field(None, title='是否从后台唤醒',description='')
element_selector: str = Field(None, title='元素选择器',description='')
element_position: str = Field(None, title='元素位置',description='')
element_content: str = Field(None, title='元素内容',description='')
scene: str = Field(None, title='场景值',description='')
mp_platform: str = Field(None, title='小程序平台',description='')
app_crashed_reason: str = Field(None, title='异常信息',description='')
zone_offset: str = Field(None, title='时区偏移',description='')
user_id: str = Field(..., title='用户唯一 ID',description='')
account_id: str = Field(..., title='账户 ID', description='')
distinct_id: str = Field(..., title='访客 ID',description='')
event_name: str = Field(..., title='事件名称',description='')
# 事件
app_id: str = Field(None, description='')
event_time: datetime = Field(None,title='事件时间', description='')
server_time: datetime = Field(None,title='服务端时间', description='')
def dict(self, **kwargs):
res = super().dict(**kwargs)
return {'#' + k: v for k, v in res.items() if v is not None}
class Config:
alias_generator = to_alias
class EventModel(Base):
preset: Preset = Field(..., title='系统属性')

View File

@ -1,5 +1,6 @@
from pydantic import BaseModel
from datetime import datetime
from pydantic import BaseModel, Field
from .base import Base, IP4, to_alias
@ -7,9 +8,51 @@ __all__ = ('UserModel',)
class Preset(BaseModel):
ip: IP4
os: str
ttt: str = None
ip: IP4 = Field(None, title='ipv4', description='不传该字段默认使用源ip')
country: str = Field(None, title='国家', description='')
country_code: str = Field(None, title='国家代码', description='')
province: str = Field(None, title='省份', description='')
city: str = Field(None, title='城市', description='')
os_version: str = Field(None, title='操作系统版本', description='')
manufacturer: str = Field(None, title='设备制造商', description='')
os: str = Field(None, title='操作系统', description='')
device_id: str = Field(None, title='设备 ID', description='')
screen_height: int = Field(None, title='屏幕高度', description='')
screen_width: int = Field(None, title='屏幕宽度', description='')
device_model: str = Field(None, title='设备型号', description='')
app_version: str = Field(None, title='APP 版本', description='')
bundle_id: str = Field(None, title='APP包名', description='')
lib: str = Field(None, title='SDK 类型', description='')
lib_version: str = Field(None, title='SDK 版本', description='')
network_type: str = Field(None, title='网络状态', description='')
carrier: str = Field(None, title='网络运营商', description='')
browser: str = Field(None, title='浏览器类型', description='')
browser_version: str = Field(None, title='浏览器版本', description='')
duration: int = Field(None, title='事件时长', description='')
url: str = Field(None, title='页面地址', description='')
url_path: str = Field(None, title='页面路径', description='')
referrer: str = Field(None, title='前向地址', description='')
referrer_host: str = Field(None, title='前向路径', description='')
title: str = Field(None, title='页面标题', description='')
screen_name: str = Field(None, title='页面名称', description='')
element_id: str = Field(None, title='元素 ID', description='')
element_type: str = Field(None, title='元素类型', description='')
resume_from_background: str = Field(None, title='是否从后台唤醒', description='')
element_selector: str = Field(None, title='元素选择器', description='')
element_position: str = Field(None, title='元素位置', description='')
element_content: str = Field(None, title='元素内容', description='')
scene: str = Field(None, title='场景值', description='')
mp_platform: str = Field(None, title='小程序平台', description='')
app_crashed_reason: str = Field(None, title='异常信息', description='')
zone_offset: str = Field(None, title='时区偏移', description='')
user_id: str = Field(..., title='用户唯一 ID', description='')
account_id: str = Field(..., title='账户 ID', description='')
distinct_id: str = Field(..., title='访客 ID', description='')
# event_name: str = Field(..., title='事件名称',description='')
# 用户
server_time: datetime = Field(None, title='服务端时间', description='')
def dict(self, **kwargs):
res = super().dict(**kwargs)
@ -20,9 +63,4 @@ class Preset(BaseModel):
class UserModel(Base):
preset: Preset
def dict(self, **kwargs):
kwargs.setdefault('exclude', {'preset'})
self.properties.update(self.preset.dict(**kwargs))
return super().dict(**kwargs)
preset: Preset = Field(..., title='系统属性')

View File

@ -0,0 +1,23 @@
import asyncio
from fastapi import APIRouter, Request
from handler_data import HandlerEvent
router = APIRouter()
from models import EventModel
@router.post("/event/")
async def event(request: Request, item: EventModel):
item.preset.ip = item.preset.ip or request.client.host
ta = getattr(request.app.state.ta, item.act)
# 将不同游戏发送到不同 topic_name
request.app.state.ta.consumer.topic_name = item.game
rdb = request.app.state.redis
await asyncio.gather(*map(lambda o: asyncio.create_task(o(rdb, item)), HandlerEvent.handler_link))
properties = item.dict()['properties']
ta(item.preset.distinct_id, item.preset.account_id, item.preset.event_name, properties)
results = {"code": 0, 'msg': 'ok'}
return results

View File

@ -11,7 +11,7 @@ router = APIRouter()
class Item(BaseModel):
# sign = md5(distinct_id+account_id+act+ts+salt)
# sign = md5(game+act+ts+salt)
distinct_id: str
game: str
account_id: str
@ -23,14 +23,15 @@ class Item(BaseModel):
@validator('sign')
def sign_validator(cls, v: str, values: dict):
s = f'{values.get("distinct_id")}{values.get("account_id", "")}{values.get("act", "")}{values.get("ts", "")}{settings.SALT}'
s = f'{values.get("game")}{values.get("act", "")}{values.get("ts", "")}{settings.SALT}'
if hashlib.md5(s.encode()).hexdigest() == v:
return v
raise ValueError(f'打击违法犯罪行为{hashlib.md5(s.encode()).hexdigest()}')
raise ValueError(f'签名 {hashlib.md5(s.encode()).hexdigest()}')
@router.post("/point/")
async def point(request: Request, item: Item):
ip = request.client.host
ta = getattr(request.app.state.ta, item.act)
# 将不同游戏发送到不同 topic_name
request.app.state.ta.consumer.topic_name = item.game
@ -42,7 +43,7 @@ async def point(request: Request, item: Item):
else:
await asyncio.gather(*map(lambda o: asyncio.create_task(o(rdb, item)), HandlerUser.handler_link))
await user_set(ta, item)
results = {"code": 0, 'msg': 'ok'}
results = {"code": 0, 'msg': 'ok','ip':ip}
return results

View File

@ -1,11 +1,8 @@
import asyncio
import hashlib
from fastapi import APIRouter, Request
from pydantic import BaseModel, validator
from handler_data import HandlerUser, HandlerEvent
from settings import settings
from handler_data import HandlerUser
router = APIRouter()
from models import UserModel
@ -13,17 +10,13 @@ from models import UserModel
@router.post("/user/")
async def user(request: Request, item: UserModel):
item.preset.ip = item.preset.ip or request.client.host
ta = getattr(request.app.state.ta, item.act)
# 将不同游戏发送到不同 topic_name
request.app.state.ta.consumer.topic_name = item.game
rdb = request.app.state.redis
await asyncio.gather(*map(lambda o: asyncio.create_task(o(rdb, item)), HandlerUser.handler_link))
ta(item.distinct_id, item.account_id, item.properties)
properties = item.dict()['properties']
ta(item.preset.distinct_id, item.preset.account_id, properties)
results = {"code": 0, 'msg': 'ok'}
return results

View File

@ -1,6 +1,3 @@
import json
class Config:
REDIS_CONF = {
'address': ('192.168.0.161', 6379),