This commit is contained in:
pengtao 2021-11-23 18:04:58 +08:00
commit 3b9e293083
16 changed files with 458 additions and 0 deletions

0
__init__.py Normal file
View File

0
config/__init__.py Normal file
View File

34
config/config.py Normal file
View File

@ -0,0 +1,34 @@
from pydantic import BaseSettings
from typing import Optional
import os
from pydantic.fields import T
class Settings(BaseSettings):
app_name: str = "Kingsome API"
admin_email: str = "pengtao@kingsome.cn"
items_per_user: int = 50
is_debug: bool = True
MONGODB_URL = "mongodb://admin:kingsome@localhost"
platform_url = "https://switch.jumpvg.com/jump/platform/order/v2?needCount=1&needFilter=1&version=3"
all_game_url = "https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset={offset}&platForm={platformid}&systemList=&type=4&version=3"
game_info_url = "https://switch.jumpvg.com/jump/game/detail?clickFrom=-1&id={gameid}&path=find_discount_gamedetail_switch&platform={platformid}&version=3"
game_price_url = "https://switch.jumpvg.com/jump/price/getAllPriceByGame?id={gameid}&platform={platformid}"
game_history_price_url = "https://switch.jumpvg.com/switch/getDiscount?appid={gameid}&platform={platformid}&zone=all"
x_token: str = "abc"
root_path_in_servers: Optional[bool] = False
root_path: str = '/api/v1'
origins: list = [
"http://*.kingsome.cn",
"https://*.kingsome.cn",
"http://localhost",
"http://localhost:8080",
]
settings = Settings()

19
dependencies.py Normal file
View File

@ -0,0 +1,19 @@
from fastapi import Header, HTTPException
from pydantic.fields import T
from starlette.requests import Request
import pdb
from config.config import settings
async def get_token_header(request: Request, x_token: str = Header(...)):
dd = settings.x_token
if x_token == settings.x_token or x_token == dd:
return True
else:
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def get_query_token(token: str):
if token != "jessica":
raise HTTPException(status_code=400,
detail="No Jessica token provided")

0
interface/__init__.py Normal file
View File

31
interface/route.py Normal file
View File

@ -0,0 +1,31 @@
from fastapi import APIRouter, BackgroundTasks, UploadFile, File, Form
from typing import List
from pydantic import BaseModel
from starlette.requests import Request
from starlette.responses import JSONResponse
from config.config import settings
from scripts.sync_data import get_jump_data
router = APIRouter()
@router.get("/sync_data")
async def get_lists(request: Request) -> bool:
db = request.app.state.mongo
await get_jump_data(db)
return True
@router.get("/get_list")
async def get_lists(request: Request, keys: str) -> JSONResponse:
return {"url": keys}
@router.get("/get_allprice")
async def get_allprice(request: Request, keys: str) -> JSONResponse:
return {"url": keys}
@router.get("/get_gameinfo")
async def get_gameinfo(request: Request, id: int) -> JSONResponse:
return {"url": 'keys'}

BIN
jobs.sqlite Normal file

Binary file not shown.

74
main.py Normal file
View File

@ -0,0 +1,74 @@
# uvicorn main:app --host=127.0.0.1 --port=8030 --reload
from config.config import settings
from fastapi import Depends, FastAPI, BackgroundTasks, Request
from dependencies import get_token_header
from interface import route as interface_route
from scripts.common.mongodb import get_mongo
from typing import Optional
from scripts.logger import logger
from fastapi.middleware.cors import CORSMiddleware
# from apscheduler.events import EVENT_JOB_EXECUTED
# from jobs.jobs import Schedule, job_execute
tags_metadata = [
# {
# "name": "common",
# "description": "Operations with users. The **login** logic is also here.",
# },
{
"name": "common",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.tiangolo.com/",
},
},
]
def create_app():
application = FastAPI(dependencies=[Depends(get_token_header)],
openapi_tags=tags_metadata)
application.include_router(interface_route.router, prefix="/interface")
return application
app = create_app()
app.add_middleware(
CORSMiddleware,
allow_origins=settings.origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.on_event("startup")
async def startup_event():
app.state.mongo = await get_mongo()
# Schedule.start()
# Schedule.add_listener(job_execute, EVENT_JOB_EXECUTED)
@app.on_event("shutdown")
async def shutdown_event():
app.state.mongo.close()
await app.state.mongo.wait_close()
@app.get("/")
async def root(request: Request):
# db = request.app.state.mongo
# keys = await db.get("online_devices")
logger.info("starting!")
return {"message": "kajsk"}
if __name__ == '__main__':
import uvicorn
uvicorn.run(app='main:app',
host="0.0.0.0",
port=8030,
reload=True,
debug=True)

89
readme.md Normal file
View File

@ -0,0 +1,89 @@
### 项目简介:
从jump接口获得游戏列表及历史价格信息写入mongodb提供接口返回数据
new project with fastapi
https://fastapi.tiangolo.com/zh/tutorial/bigger-applications/
采用异步方式实现,考虑并发性能
### 功能
获得平台信息:包含区域->国家
platform_url="https://switch.jumpvg.com/jump/platform/order/v2?needCount=1&needFilter=1&version=3"
获得游戏列表
https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset=1&platForm=1&systemList=&type=4&version=3
获得游戏信息:
id=**oldGameId**
https://switch.jumpvg.com/jump/game/detail?clickFrom=-1&id=33113&path=find_discount_gamedetail_switch&platform=1&version=3
获得全区游戏价格:
https://switch.jumpvg.com/jump/price/getAllPriceByGame?id=20160&platform=1
获得历史价格:
https://switch.jumpvg.com/switch/getDiscount?appid=20160&platform=1&zone=all
获得史低信息:
程序判断 实时价格vs历史价格
> https://switch.jumpvg.com/jump/platform/order/v2?needCount=1&needFilter=1&version=3
>
> **platformAlias**
>
> 游戏列表
> termsId 18:史低 17:全部 16:精选
> https://switch.jumpvg.com/jump/discount/find4Discount/5/v2?offset=0&platform=1&session=1637580994841&size=10&termsId=18&version=3
>
>
>
> 游戏详情
> https://switch.jumpvg.com/jump/game/detail?clickFrom=-1&id=20160&path=find_discount_gamedetail_switch&platform=1&version=3
> 所有商店价格
> https://switch.jumpvg.com/jump/price/getAllPriceByGame?id=20160&platform=1
> 历史价格
> 全区:all 港区:HK 欧区:UK 日区:JP 美区:US
> https://switch.jumpvg.com/switch/getDiscount?appid=20160&platform=1&zone=all
>
> 查询游戏
> https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=%E6%8A%98%E6%89%A3%3B%E5%8C%85%E5%90%AB%E8%AF%95%E7%8E%A9&limit=10&offset=0&platForm=1&systemList=&type=5&version=3
>
> featureList 折扣 试玩等标签
> categoryList 是 多个中文标签(urlencode) 以 %3B分割
> limit 最大30, 一般10个
>
> switch 最新发布
> https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset=0&platForm=1&systemList=&type=4&version=3
> 正在流行
> https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset=0&platForm=1&systemList=&type=3&version=3
> 即将推出
> https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset=0&platForm=1&systemList=&type=1&version=3
> 热门新游
> https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset=0&platForm=1&systemList=&type=2&version=3
> 好评游戏
> https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset=0&platForm=1&systemList=&type=6&version=3
> 高分
> https://switch.jumpvg.com/jump/findGame/list?categoryList=&featureList=&limit=10&offset=0&platForm=1&systemList=&type=5&version=3
>
>
> 搜索游戏
> 综合1 游戏3 评测2 用户4
> platform: switch 1 steam 4 xbox 8 ps4 51
> https://switch.jumpvg.com/jump/searchGame/list?limit=10&offset=0&searchName=%E6%9A%97%E9%BB%91&type=1&version=3
> https://switch.jumpvg.com/jump/searchGame/list?limit=10&offset=0&searchName=%E6%9A%97%E9%BB%91&type=2&version=3
> https://switch.jumpvg.com/jump/searchGame/list?limit=10&offset=0&platForm=&searchName=%E6%9A%97%E9%BB%91&type=3&version=3
> https://switch.jumpvg.com/jump/searchGame/list?limit=10&offset=0&searchName=%E6%9A%97%E9%BB%91&type=4&version=3
>
>

9
requirements.txt Normal file
View File

@ -0,0 +1,9 @@
fastapi==0.65.2
fastapi-mail==0.3.7
GitPython==3.1.18
pydantic==1.8.2
uvicorn==0.15.0
oss2==2.15.0
motor==2.3.0
mypy-extensions==0.4.3
pymongo==3.11.1

0
scripts/__init__.py Normal file
View File

View File

88
scripts/common/mongodb.py Normal file
View File

@ -0,0 +1,88 @@
# sample as https://github.com/mongodb-developer/mongodb-with-fastapi/blob/master/app.py
import os
from fastapi import FastAPI, Body, HTTPException, status
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, Field, EmailStr
from bson import ObjectId
from typing import Optional, List
import motor.motor_asyncio
from pydantic.types import Json
from config.config import settings
async def get_mongo():
client = motor.motor_asyncio.AsyncIOMotorClient(settings.MONGODB_URL)
db = client.jump
return db
class PyObjectId(ObjectId):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
if not ObjectId.is_valid(v):
raise ValueError("Invalid objectid")
return ObjectId(v)
@classmethod
def __modify_schema__(cls, field_schema):
field_schema.update(type="string")
class PlatformModel(BaseModel):
id: PyObjectId = Field(default_factory=PyObjectId, alias="_id")
moduleId: int = Field(...)
platformAlias: str = Field(...)
iconRes: str = Field(...)
gameNum: int = Field(...)
filter: list = []
countryFilter: bool
class Config:
allow_population_by_field_name = True
arbitrary_types_allowed = True
json_encoders = {ObjectId: str}
schema_extra = {
"example": {
"moduleId":
4,
"platformAlias":
'Steam',
"iconRes":
'http://switch-cdn.vgjump.com/869270335915032576',
"gameNum":
759,
"filter": [{
'termsId': 26,
'terms': '精选'
}, {
'termsId': 27,
'terms': '全部'
}],
"countryFilter":
None
}
}
class UpdateStudentModel(BaseModel):
name: Optional[str]
email: Optional[EmailStr]
course: Optional[str]
gpa: Optional[float]
class Config:
arbitrary_types_allowed = True
json_encoders = {ObjectId: str}
schema_extra = {
"example": {
"name": "Jane Doe",
"email": "jdoe@example.com",
"course": "Experiments, Science, and Fashion in Nanophotonics",
"gpa": "3.0",
}
}

20
scripts/logger.py Normal file
View File

@ -0,0 +1,20 @@
import os
import time
from loguru import logger
basedir = os.path.dirname(
os.path.dirname(os.path.abspath(__file__)))
# print(f"log basedir{basedir}") # /xxx/python_code/FastAdmin/backend/app
# 定位到log日志文件
log_path = os.path.join(basedir, 'logs')
if not os.path.exists(log_path):
os.mkdir(log_path)
log_path_error = os.path.join(
log_path, f'{time.strftime("%Y-%m-%d")}_error.log')
# 日志简单配置
# 具体其他配置 可自行参考 https://github.com/Delgan/loguru
logger.add(log_path_error, rotation="12:00", retention="5 days", enqueue=True)

64
scripts/sync_data.py Normal file
View File

@ -0,0 +1,64 @@
import json
from pydantic.fields import T
from config.config import settings
import requests
from starlette.requests import Request
from starlette.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from scripts.logger import logger
async def get_jump_data(db) -> bool:
platform_info = get_platform()
platforms = platform_info.get('data')
for platform in platforms:
platformAlias = platform.get('platformAlias')
existing_platform = await db["platform"].find_one(
{"platformAlias": platformAlias})
if not existing_platform:
platform = jsonable_encoder(a)
new_platform = await db["platform"].insert_one(platform)
created_platform = await db["platform"].find_one(
{"_id": new_platform.inserted_id})
logger.info(f"insert platform {created_platform}")
else:
logger.info(f"key found with {existing_platform}")
# all_game = get_all_game()
# #gameids = all_game['data']
# print(all_game)
return True
# for gameid in gameids:
# game_info = get_game_info(gameid)
def get_platform() -> json:
info = get_url_data(settings.platform_url)
return info
def get_all_game() -> json:
info = get_url_data(settings.all_game_url)
print(info)
return None
def get_game_info(gameid: int) -> dict:
pass
def get_game_price(gameid: int) -> dict:
pass
def get_history_price(gameid) -> dict:
pass
def get_url_data(url: str, data=None) -> json:
requests.adapters.DEFAULT_RETRIES = 5
s = requests.session()
s.keep_alive = False
headers = {'Connection': 'close'}
res = s.get(url=url, params=data, timeout=10, headers=headers).json()
return res

30
test.py Normal file
View File

@ -0,0 +1,30 @@
from fastapi.testclient import TestClient
from ops.common.common import EmailSchema
from main import app
import time
import pdb
import json
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello Bigger Applications!"}
def test_post_mail():
data = {"email": ['pengtao@kingsome.cn'], "body": 'test message!',
"subject": 'ttt {}'.format(time.time())}
response = client.post("/common/email", headers={"X-Token": "fake-super-scret-token", "accept": "application/json", "Content-Type": "application/json"},
data=json.dumps(data),
)
# data=EmailSchema(**data).json()
print(response)
# pdb.set_trace()
assert response.status_code == 200
assert response.json() == {"message": "email has been sent"}
if __name__ == "__main__":
test_post_mail()