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 import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from routers import point from routers import point, user, event
from settings import settings from settings import settings
from utils.ta_sdk import TGAnalytics, ToKafka 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(point.router, prefix='/v1')
app.include_router(user.router, prefix='/v1')
app.include_router(event.router, prefix='/v1')
register_redis(app) register_redis(app)
register_ta(app) register_ta(app)

View File

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

View File

@ -1,4 +1,6 @@
import hashlib import hashlib
from enum import Enum
from pydantic import Field from pydantic import Field
from pydantic import BaseModel, validator from pydantic import BaseModel, validator
@ -8,8 +10,52 @@ from settings import settings
from ipaddress import IPv4Address from ipaddress import IPv4Address
FIELD_MAP = { FIELD_MAP = {
'user_id': 'x01',
'account_id': 'x02',
'distinct_id': 'x03',
'event_name': 'x04',
'server_time':'x05',
'ip': 'a01', 'ip': 'a01',
'country': 'a02',
'country_code': 'a03',
'province': 'a04',
'city': 'a05',
'os_version': 'a06',
'manufacturer': 'a07',
'os': 'a08', '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) 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): class Base(BaseModel):
# sign = md5(distinct_id+account_id+act+ts+salt) # sign = md5(game+act+ts+salt)
distinct_id: str = Field(..., title='访客 ID') game: str = Field(..., title='游戏代号')
game: str act: ActEnum = Field(..., title='操作', description='同ta一致')
account_id: str preset: BaseModel
act: str properties: dict = Field(..., title='自定义属性')
event_name: str = None ts: int = Field(..., title='时间戳')
# preset: dict sign: str = Field(..., title='签名')
properties: dict
ts: int
sign: str
@validator('sign') @validator('sign')
def sign_validator(cls, v: str, values: dict): 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: if hashlib.md5(s.encode()).hexdigest() == v:
return 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 from .base import Base, IP4, to_alias
@ -7,9 +8,51 @@ __all__ = ('UserModel',)
class Preset(BaseModel): class Preset(BaseModel):
ip: IP4 ip: IP4 = Field(None, title='ipv4', description='不传该字段默认使用源ip')
os: str country: str = Field(None, title='国家', description='')
ttt: str = None 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): def dict(self, **kwargs):
res = super().dict(**kwargs) res = super().dict(**kwargs)
@ -20,9 +63,4 @@ class Preset(BaseModel):
class UserModel(Base): class UserModel(Base):
preset: Preset preset: Preset = Field(..., title='系统属性')
def dict(self, **kwargs):
kwargs.setdefault('exclude', {'preset'})
self.properties.update(self.preset.dict(**kwargs))
return super().dict(**kwargs)

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

View File

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

View File

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