mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2025-12-14 08:06:23 +08:00
[Enhance] API for purchases, items, operations
- Add API endpoints for purchases, items, and operations - Header checker? :)
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
from flask import Blueprint
|
||||
|
||||
from . import users
|
||||
from . import songs
|
||||
from . import token
|
||||
from . import (users, songs, token, system, items, purchases)
|
||||
|
||||
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||
bp.register_blueprint(users.bp)
|
||||
bp.register_blueprint(songs.bp)
|
||||
bp.register_blueprint(token.bp)
|
||||
bp.register_blueprint(system.bp)
|
||||
bp.register_blueprint(items.bp)
|
||||
bp.register_blueprint(purchases.bp)
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
from functools import wraps
|
||||
from traceback import format_exc
|
||||
from base64 import b64decode
|
||||
from functools import wraps
|
||||
from json import loads
|
||||
from traceback import format_exc
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from core.api_user import APIUser
|
||||
from core.config_manager import Config
|
||||
from core.error import ArcError, NoAccess, PostError
|
||||
from core.error import ArcError, InputError, NoAccess, PostError
|
||||
from core.sql import Connect
|
||||
from flask import current_app
|
||||
|
||||
from .api_code import error_return
|
||||
from .constant import Constant
|
||||
|
||||
|
||||
def role_required(request, powers=[]):
|
||||
@@ -48,7 +50,7 @@ def role_required(request, powers=[]):
|
||||
return decorator
|
||||
|
||||
|
||||
def request_json_handle(request, required_keys: list = [], optional_keys: list = [], must_change: bool = False):
|
||||
def request_json_handle(request, required_keys: list = [], optional_keys: list = [], must_change: bool = False, is_batch: bool = False):
|
||||
'''
|
||||
提取post参数,返回dict,写成了修饰器\
|
||||
parameters: \
|
||||
@@ -64,7 +66,7 @@ def request_json_handle(request, required_keys: list = [], optional_keys: list =
|
||||
|
||||
data = {}
|
||||
if request.data:
|
||||
json_data = request.json
|
||||
json_data = request.get_json()
|
||||
else:
|
||||
if request.method == 'GET' and 'query' in request.args:
|
||||
# 处理axios没法GET传data的问题
|
||||
@@ -78,15 +80,24 @@ def request_json_handle(request, required_keys: list = [], optional_keys: list =
|
||||
|
||||
for key in required_keys:
|
||||
if key not in json_data:
|
||||
return error_return(PostError(f'Missing parameter: {key}', api_error_code=-100))
|
||||
return error_return(InputError(f'Missing parameter: {key}', api_error_code=-100))
|
||||
data[key] = json_data[key]
|
||||
|
||||
for key in optional_keys:
|
||||
if key in json_data:
|
||||
data[key] = json_data[key]
|
||||
if is_batch:
|
||||
for key in Constant.PATCH_KEYS:
|
||||
if key in json_data:
|
||||
data[key] = json_data[key]
|
||||
if not isinstance(data[key], list):
|
||||
return error_return(InputError(f'Parameter {key} must be a list', api_error_code=-100))
|
||||
if not data:
|
||||
return error_return(InputError('No change', api_error_code=-100))
|
||||
else:
|
||||
for key in optional_keys:
|
||||
if key in json_data:
|
||||
data[key] = json_data[key]
|
||||
|
||||
if must_change and not data:
|
||||
return error_return(PostError('No change', api_error_code=-100))
|
||||
if must_change and not data:
|
||||
return error_return(InputError('No change', api_error_code=-100))
|
||||
|
||||
return view(data, *args, **kwargs)
|
||||
|
||||
|
||||
@@ -18,6 +18,11 @@ CODE_MSG = {
|
||||
-104: 'Invalid sort order parameter',
|
||||
-105: 'Invalid URL parameter',
|
||||
-110: 'Invalid user_id',
|
||||
-120: 'Invalid item type',
|
||||
-121: 'No such item',
|
||||
-122: 'Item already exists',
|
||||
-123: 'Purchase already has this item',
|
||||
-124: 'Purchase does not have this item',
|
||||
-200: 'No permission', # 2xx用户相关错误
|
||||
-201: 'Wrong username or password',
|
||||
-202: 'User is banned',
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
class Constant:
|
||||
QUERY_KEYS = ['limit', 'offset', 'query', 'fuzzy_query', 'sort']
|
||||
|
||||
PATCH_KEYS = ['create', 'update', 'remove']
|
||||
|
||||
115
latest version/api/items.py
Normal file
115
latest version/api/items.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from flask import Blueprint, request
|
||||
|
||||
from core.error import DataExist, InputError, NoData
|
||||
from core.item import Item, ItemFactory
|
||||
from core.sql import Connect, Query, Sql
|
||||
|
||||
from .api_auth import api_try, request_json_handle, role_required
|
||||
from .api_code import success_return
|
||||
from .constant import Constant
|
||||
|
||||
bp = Blueprint('items', __name__, url_prefix='/items')
|
||||
|
||||
|
||||
@bp.route('', methods=['GET'])
|
||||
@role_required(request, ['select'])
|
||||
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||
@api_try
|
||||
def items_get(data, user):
|
||||
'''查询全物品信息'''
|
||||
with Connect() as c:
|
||||
query = Query(['item_id', 'type'], ['item_id'],
|
||||
['item_id']).from_dict(data)
|
||||
x = Sql(c).select('item', query=query)
|
||||
r: 'list[Item]' = []
|
||||
for i in x:
|
||||
r.append(ItemFactory.from_dict({
|
||||
'item_id': i[0],
|
||||
'type': i[1],
|
||||
'is_available': i[2] == 1
|
||||
}))
|
||||
|
||||
if not r:
|
||||
raise NoData(api_error_code=-2)
|
||||
|
||||
return success_return([x.to_dict(has_is_available=True, has_amount=False) for x in r])
|
||||
|
||||
|
||||
ALLOW_ITEM_TYPE = ['pack', 'single', 'world_song', 'character']
|
||||
|
||||
|
||||
@bp.route('', methods=['POST'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, required_keys=['item_id', 'type'], optional_keys=['is_available'])
|
||||
@api_try
|
||||
def items_post(data, user):
|
||||
'''新增物品'''
|
||||
if data['type'] not in ALLOW_ITEM_TYPE:
|
||||
raise InputError(
|
||||
f'Invalid item type: `{data["type"]}`', api_error_code=-120)
|
||||
with Connect() as c:
|
||||
item = ItemFactory.from_dict(data, c=c)
|
||||
if item.select_exists():
|
||||
raise DataExist(
|
||||
f'Item `{item.item_type}`: `{item.item_id}` already exists', api_error_code=-122)
|
||||
item.insert()
|
||||
return success_return(item.to_dict(has_is_available=True, has_amount=False))
|
||||
|
||||
|
||||
@bp.route('/<string:item_type>/<string:item_id>', methods=['DELETE'])
|
||||
@role_required(request, ['change'])
|
||||
@api_try
|
||||
def items_item_delete(user, item_type, item_id):
|
||||
'''删除物品'''
|
||||
if item_type not in ALLOW_ITEM_TYPE:
|
||||
raise InputError(
|
||||
f'Invalid item type: `{item_type}`', api_error_code=-120)
|
||||
with Connect() as c:
|
||||
item = ItemFactory.from_dict({
|
||||
'item_id': item_id,
|
||||
'type': item_type
|
||||
}, c=c)
|
||||
if not item.select_exists():
|
||||
raise NoData(
|
||||
f'No such item `{item_type}`: `{item_id}`', api_error_code=-121)
|
||||
item.delete()
|
||||
return success_return()
|
||||
|
||||
|
||||
@bp.route('/<string:item_type>/<string:item_id>', methods=['PUT'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, optional_keys=['is_available'], must_change=True)
|
||||
@api_try
|
||||
def items_item_put(data, user, item_type, item_id):
|
||||
'''修改物品'''
|
||||
if item_type not in ALLOW_ITEM_TYPE:
|
||||
raise InputError(
|
||||
f'Invalid item type: `{item_type}`', api_error_code=-120)
|
||||
if not isinstance(data['is_available'], bool):
|
||||
raise InputError('`is_available` must be a boolean',
|
||||
api_error_code=-101)
|
||||
with Connect() as c:
|
||||
item = ItemFactory.from_dict({
|
||||
'item_id': item_id,
|
||||
'type': item_type,
|
||||
'is_available': data['is_available']
|
||||
}, c=c)
|
||||
if not item.select_exists():
|
||||
raise NoData(
|
||||
f'No such item `{item_type}`: `{item_id}`', api_error_code=-121)
|
||||
item.update()
|
||||
return success_return(item.to_dict(has_is_available=True, has_amount=False))
|
||||
|
||||
|
||||
@bp.route('/<string:item_type>/<string:item_id>', methods=['GET'])
|
||||
@role_required(request, ['select'])
|
||||
@api_try
|
||||
def items_item_get(user, item_type, item_id):
|
||||
'''查询单个物品信息'''
|
||||
with Connect() as c:
|
||||
item = ItemFactory.from_dict({
|
||||
'item_id': item_id,
|
||||
'type': item_type
|
||||
}, c=c)
|
||||
item.select()
|
||||
return success_return(item.to_dict(has_is_available=True, has_amount=False))
|
||||
164
latest version/api/purchases.py
Normal file
164
latest version/api/purchases.py
Normal file
@@ -0,0 +1,164 @@
|
||||
from flask import Blueprint, request
|
||||
|
||||
from core.error import DataExist, InputError, NoData
|
||||
from core.item import ItemFactory
|
||||
from core.purchase import Purchase
|
||||
from core.sql import Connect, Query, Sql
|
||||
|
||||
from .api_auth import api_try, request_json_handle, role_required
|
||||
from .api_code import success_return
|
||||
from .constant import Constant
|
||||
|
||||
bp = Blueprint('purchases', __name__, url_prefix='/purchases')
|
||||
|
||||
|
||||
@bp.route('', methods=['GET'])
|
||||
@role_required(request, ['select'])
|
||||
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||
@api_try
|
||||
def purchases_get(data, user):
|
||||
'''查询全购买信息'''
|
||||
with Connect() as c:
|
||||
query = Query(['purchase_name', 'discount_reason'], ['purchase_name'], [
|
||||
'price', 'orig_price', 'discount_from', 'discount_to']).from_dict(data)
|
||||
x = Sql(c).select('purchase', query=query)
|
||||
r = [Purchase().from_list(i) for i in x]
|
||||
|
||||
if not r:
|
||||
raise NoData(api_error_code=-2)
|
||||
|
||||
return success_return([x.to_dict(has_items=False, show_real_price=False) for x in r])
|
||||
|
||||
|
||||
@bp.route('', methods=['POST'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, required_keys=['purchase_name', 'orig_price'], optional_keys=['price', 'discount_from', 'discount_to', 'discount_reason', 'items'])
|
||||
@api_try
|
||||
def purchases_post(data, user):
|
||||
'''新增购买,注意可以有items,不存在的item会自动创建'''
|
||||
with Connect() as c:
|
||||
purchase = Purchase(c).from_dict(data)
|
||||
if purchase.select_exists():
|
||||
raise DataExist(
|
||||
f'Purchase `{purchase.purchase_name}` already exists')
|
||||
purchase.insert_all()
|
||||
return success_return(purchase.to_dict(has_items='items' in data, show_real_price=False))
|
||||
|
||||
|
||||
@bp.route('/<string:purchase_name>', methods=['GET'])
|
||||
@role_required(request, ['select'])
|
||||
@api_try
|
||||
def purchases_purchase_get(user, purchase_name: str):
|
||||
'''查询单个购买信息'''
|
||||
with Connect() as c:
|
||||
return success_return(Purchase(c).select(purchase_name).to_dict(show_real_price=False))
|
||||
|
||||
|
||||
@bp.route('/<string:purchase_name>', methods=['DELETE'])
|
||||
@role_required(request, ['change'])
|
||||
@api_try
|
||||
def purchases_purchase_delete(user, purchase_name: str):
|
||||
'''删除单个购买信息,会连带删除purchase_item'''
|
||||
with Connect() as c:
|
||||
purchase = Purchase(c).select(purchase_name)
|
||||
purchase.delete_all()
|
||||
return success_return()
|
||||
|
||||
|
||||
@bp.route('/<string:purchase_name>', methods=['PUT'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, optional_keys=['price', 'orig_price', 'discount_from', 'discount_to', 'discount_reason'], must_change=True)
|
||||
@api_try
|
||||
def purchases_purchase_put(data, user, purchase_name: str):
|
||||
'''修改单个购买信息,注意不能有items'''
|
||||
with Connect() as c:
|
||||
purchase = Purchase(c).select(purchase_name)
|
||||
t = ['price', 'orig_price', 'discount_from', 'discount_to']
|
||||
for i in t:
|
||||
if i in data:
|
||||
setattr(purchase, i, int(data[i]))
|
||||
if 'discount_reason' in data:
|
||||
purchase.discount_reason = str(data['discount_reason'])
|
||||
|
||||
purchase.update()
|
||||
return success_return(purchase.to_dict(has_items=False, show_real_price=False))
|
||||
|
||||
|
||||
@bp.route('/<string:purchase_name>/items', methods=['GET'])
|
||||
@role_required(request, ['select'])
|
||||
@api_try
|
||||
def purchases_purchase_items_get(user, purchase_name: str):
|
||||
'''查询单个购买的所有items'''
|
||||
with Connect() as c:
|
||||
p = Purchase(c)
|
||||
p.purchase_name = purchase_name
|
||||
p.select_items()
|
||||
return success_return([x.to_dict(has_is_available=True) for x in p.items])
|
||||
|
||||
|
||||
# @bp.route('/<string:purchase_name>/items', methods=['POST'])
|
||||
# @role_required(request, ['change'])
|
||||
# @request_json_handle(request, required_keys=['item_id', 'type'], optional_keys=['amount'])
|
||||
# @api_try
|
||||
# def purchases_purchase_items_post(data, user, purchase_name: str):
|
||||
# '''新增单个购买的批量items'''
|
||||
# with Connect() as c:
|
||||
# p = Purchase(c)
|
||||
# p.purchase_name = purchase_name
|
||||
# p.select_items()
|
||||
# p.add_items([ItemFactory().from_dict(data)])
|
||||
# return success_return([x.to_dict(has_is_available=True) for x in p.items])
|
||||
|
||||
|
||||
@bp.route('/<string:purchase_name>/items', methods=['PATCH'])
|
||||
@role_required(request, ['change'])
|
||||
@request_json_handle(request, is_batch=True)
|
||||
@api_try
|
||||
def purchases_purchase_items_patch(data, user, purchase_name: str):
|
||||
'''增删改单个购买的批量items'''
|
||||
with Connect() as c:
|
||||
p = Purchase(c)
|
||||
p.purchase_name = purchase_name
|
||||
p.select_items()
|
||||
p.delete_items([ItemFactory.from_dict(x, c=c)
|
||||
for x in data.get('remove', [])])
|
||||
p.add_items([ItemFactory.from_dict(x, c=c)
|
||||
for x in data.get('create', [])])
|
||||
|
||||
updates = data.get('update', [])
|
||||
for x in updates:
|
||||
if 'amount' not in x:
|
||||
raise InputError('`amount` is required in `update`')
|
||||
if not isinstance(x['amount'], int) or x['amount'] <= 0:
|
||||
raise InputError(
|
||||
'`amount` must be a positive integer', api_error_code=-101)
|
||||
|
||||
p.update_items([ItemFactory.from_dict(x, c=c) for x in updates])
|
||||
return success_return([x.to_dict(has_is_available=True) for x in p.items])
|
||||
|
||||
|
||||
# @bp.route('/<string:purchase_name>/items/<string:item_type>/<string:item_id>', methods=['DELETE'])
|
||||
# @role_required(request, ['change'])
|
||||
# @api_try
|
||||
# def purchases_purchase_items_item_delete(user, purchase_name: str, item_type: str, item_id: str):
|
||||
# '''删除单个购买的单个item'''
|
||||
# with Connect() as c:
|
||||
# p = Purchase(c)
|
||||
# p.purchase_name = purchase_name
|
||||
# p.select_items()
|
||||
# p.delete_items([ItemFactory().from_dict(
|
||||
# {'item_type': item_type, 'item_id': item_id})])
|
||||
# return success_return()
|
||||
|
||||
|
||||
# @bp.route('/<string:purchase_name>/items/<string:item_type>/<string:item_id>', methods=['PUT'])
|
||||
# @role_required(request, ['change'])
|
||||
# @request_json_handle(request, optional_keys=['amount', 'is_available'], must_change=True)
|
||||
# @api_try
|
||||
# def purchases_purchase_items_item_put(data, user, purchase_name: str, item_type: str, item_id: str):
|
||||
# '''修改单个购买的单个item'''
|
||||
# with Connect() as c:
|
||||
# p = Purchase(c)
|
||||
# p.purchase_name = purchase_name
|
||||
# pass
|
||||
# return success_return()
|
||||
32
latest version/api/system.py
Normal file
32
latest version/api/system.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from flask import Blueprint, request
|
||||
|
||||
from core.error import ArcError
|
||||
from core.operation import BaseOperation
|
||||
|
||||
from .api_auth import api_try, role_required
|
||||
from .api_code import success_return
|
||||
|
||||
bp = Blueprint('system', __name__, url_prefix='/system')
|
||||
|
||||
|
||||
operation_dict = {i._name: i for i in BaseOperation.__subclasses__()}
|
||||
|
||||
|
||||
@bp.route('/operations', methods=['GET'])
|
||||
@role_required(request, ['system'])
|
||||
@api_try
|
||||
def operations_get(user):
|
||||
return success_return(list(operation_dict.keys()))
|
||||
|
||||
|
||||
@bp.route('/operations/<string:operation_name>', methods=['POST'])
|
||||
@role_required(request, ['system'])
|
||||
@api_try
|
||||
def operations_operation_post(user, operation_name: str):
|
||||
if operation_name not in operation_dict:
|
||||
raise ArcError(
|
||||
f'No such operation: `{operation_name}`', api_error_code=-1, status=404)
|
||||
x = operation_dict[operation_name]()
|
||||
x.set_params(**request.get_json())
|
||||
x.run()
|
||||
return success_return()
|
||||
@@ -1,9 +1,10 @@
|
||||
from base64 import b64decode
|
||||
|
||||
from flask import Blueprint, request
|
||||
|
||||
from core.api_user import APIUser
|
||||
from core.error import PostError
|
||||
from core.sql import Connect
|
||||
from flask import Blueprint, request
|
||||
|
||||
from .api_auth import api_try, request_json_handle, role_required
|
||||
from .api_code import success_return
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from flask import Blueprint, request
|
||||
|
||||
from core.api_user import APIUser
|
||||
from core.error import InputError, NoAccess, NoData
|
||||
from core.score import Potential, UserScoreList
|
||||
from core.sql import Connect, Query, Sql
|
||||
from core.user import UserChanger, UserInfo, UserRegister
|
||||
from core.api_user import APIUser
|
||||
from flask import Blueprint, request
|
||||
|
||||
from .api_auth import api_try, request_json_handle, role_required
|
||||
from .api_code import error_return, success_return
|
||||
|
||||
@@ -97,12 +97,6 @@ class DatabaseInit:
|
||||
with open(self.single_path, 'rb') as f:
|
||||
self.insert_purchase_item(load(f))
|
||||
|
||||
self.c.execute(
|
||||
'''select exists(select * from item where item_id='epilogue')''')
|
||||
if self.c.fetchone() == (0,):
|
||||
self.c.execute(
|
||||
'''insert into item values('epilogue','pack',1)''')
|
||||
|
||||
def course_init(self) -> None:
|
||||
'''初始化课题信息'''
|
||||
courses = []
|
||||
|
||||
@@ -5,10 +5,14 @@ from .error import InputError, ItemNotEnough, ItemUnavailable, NoData
|
||||
class Item:
|
||||
item_type = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, c=None) -> None:
|
||||
self.item_id = None
|
||||
self.__amount = None
|
||||
self.is_available = None
|
||||
self.c = c
|
||||
|
||||
def __eq__(self, other: 'Item') -> bool:
|
||||
return self.item_id == other.item_id and self.item_type == other.item_type
|
||||
|
||||
@property
|
||||
def amount(self):
|
||||
@@ -18,12 +22,13 @@ class Item:
|
||||
def amount(self, value: int):
|
||||
self.__amount = int(value)
|
||||
|
||||
def to_dict(self, has_is_available: bool = False) -> dict:
|
||||
def to_dict(self, has_is_available: bool = False, has_amount: bool = True) -> dict:
|
||||
r = {
|
||||
'id': self.item_id,
|
||||
'amount': self.amount,
|
||||
'type': self.item_type
|
||||
}
|
||||
if has_amount:
|
||||
r['amount'] = self.amount
|
||||
if has_is_available:
|
||||
r['is_available'] = self.is_available
|
||||
return r
|
||||
@@ -32,6 +37,32 @@ class Item:
|
||||
# parameter: user - User类或子类的实例
|
||||
pass
|
||||
|
||||
def select_exists(self):
|
||||
self.c.execute('''select exists(select * from item where item_id=? and type=?)''',
|
||||
(self.item_id, self.item_type))
|
||||
return bool(self.c.fetchone()[0])
|
||||
|
||||
def insert(self):
|
||||
self.c.execute('''insert into item values(?,?,?)''',
|
||||
(self.item_id, self.item_type, self.is_available))
|
||||
|
||||
def delete(self):
|
||||
self.c.execute('''delete from item where item_id=? and type=?''',
|
||||
(self.item_id, self.item_type))
|
||||
|
||||
def update(self):
|
||||
self.c.execute('''update item set is_available=? where item_id=? and type=?''',
|
||||
(self.is_available, self.item_id, self.item_type))
|
||||
|
||||
def select(self):
|
||||
self.c.execute('''select is_available from item where item_id=? and type=?''',
|
||||
(self.item_id, self.item_type))
|
||||
x = self.c.fetchone()
|
||||
if not x:
|
||||
raise NoData(
|
||||
f'No such item `{self.item_type}`: `{self.item_id}`', api_error_code=-121)
|
||||
self.is_available = x[0]
|
||||
|
||||
|
||||
class UserItem(Item):
|
||||
|
||||
@@ -40,7 +71,7 @@ class UserItem(Item):
|
||||
self.c = c
|
||||
self.user = None
|
||||
|
||||
def select(self, user=None):
|
||||
def select_user_item(self, user=None):
|
||||
'''
|
||||
查询用户item\
|
||||
parameter: `user` - `User`类或子类的实例
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from .download import DownloadList
|
||||
from .error import NoData
|
||||
from .save import SaveData
|
||||
from .score import Score
|
||||
from .sql import Connect, Sql
|
||||
@@ -214,6 +215,11 @@ class UnlockUserItem(BaseOperation):
|
||||
|
||||
def _one_user_insert(self):
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select exists(select * from user where user_id = ?)''', (self.user.user_id,))
|
||||
if not c.fetchone()[0]:
|
||||
raise NoData(
|
||||
f'No such user: `{self.user.user_id}`', api_error_code=-110)
|
||||
c.execute(
|
||||
f'''select item_id, type from item where type in ({','.join(['?'] * len(self.item_types))})''', self.item_types)
|
||||
sql_list = [(self.user.user_id, i[0], i[1])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from time import time
|
||||
|
||||
from .error import NoData, TicketNotEnough
|
||||
from .error import DataExist, InputError, NoData, TicketNotEnough
|
||||
from .item import ItemFactory
|
||||
|
||||
|
||||
@@ -34,31 +34,35 @@ class Purchase:
|
||||
if self.discount_reason == 'anni5tix':
|
||||
x = ItemFactory(self.c).get_item('anni5tix')
|
||||
x.item_id = 'anni5tix'
|
||||
x.select(self.user)
|
||||
x.select_user_item(self.user)
|
||||
if x.amount >= 1:
|
||||
return 0
|
||||
return self.price
|
||||
return self.orig_price
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
price = self.price_displayed
|
||||
def to_dict(self, has_items: bool = True, show_real_price: bool = True) -> dict:
|
||||
if show_real_price:
|
||||
price = self.price_displayed
|
||||
r = {
|
||||
'name': self.purchase_name,
|
||||
'price': price,
|
||||
'price': price if show_real_price else self.price,
|
||||
'orig_price': self.orig_price,
|
||||
'items': [x.to_dict(has_is_available=True) for x in self.items]
|
||||
}
|
||||
if has_items:
|
||||
r['items'] = [x.to_dict(has_is_available=True) for x in self.items]
|
||||
if self.discount_from > 0 and self.discount_to > 0:
|
||||
r['discount_from'] = self.discount_from
|
||||
r['discount_to'] = self.discount_to
|
||||
if self.discount_reason == 'anni5tix' and price == 0:
|
||||
if not show_real_price or (self.discount_reason == 'anni5tix' and price == 0):
|
||||
r['discount_reason'] = self.discount_reason
|
||||
return r
|
||||
|
||||
def from_dict(self, d: dict) -> 'Purchase':
|
||||
self.purchase_name = d['name']
|
||||
self.price = d['price']
|
||||
self.orig_price = d['orig_price']
|
||||
self.purchase_name = d.get('name') or d.get('purchase_name')
|
||||
if not self.purchase_name:
|
||||
raise InputError('purchase_name is required')
|
||||
self.orig_price = int(d['orig_price'])
|
||||
self.price = d.get('price', self.orig_price)
|
||||
self.discount_from = d.get('discount_from', -1)
|
||||
self.discount_to = d.get('discount_to', -1)
|
||||
self.discount_reason = d.get('discount_reason', '')
|
||||
@@ -67,24 +71,50 @@ class Purchase:
|
||||
|
||||
return self
|
||||
|
||||
def from_list(self, l: list) -> 'Purchase':
|
||||
self.purchase_name = l[0]
|
||||
self.price = l[1]
|
||||
self.orig_price = l[2]
|
||||
self.discount_from = l[3] if l[3] else -1
|
||||
self.discount_to = l[4] if l[4] else -1
|
||||
self.discount_reason = l[5] if l[5] else ''
|
||||
return self
|
||||
|
||||
def insert_all(self) -> None:
|
||||
'''向数据库插入,包括item表和purchase_item表'''
|
||||
self.insert()
|
||||
self.insert_items()
|
||||
|
||||
def insert(self) -> None:
|
||||
'''向数据库插入,不包括item表和purchase_item表'''
|
||||
self.c.execute('''insert into purchase values(?,?,?,?,?,?)''',
|
||||
(self.purchase_name, self.price, self.orig_price, self.discount_from, self.discount_to, self.discount_reason))
|
||||
self.insert_items()
|
||||
|
||||
# def insert_only_purchase_item(self) -> None:
|
||||
# '''向数据库插入purchase_item表'''
|
||||
# for i in self.items:
|
||||
# self.c.execute('''insert into purchase_item values(?,?,?,?)''',
|
||||
# (self.purchase_name, i.item_id, i.item_type, i.amount))
|
||||
|
||||
def insert_items(self) -> None:
|
||||
'''向数据库插入物品,注意已存在的物品不会变更'''
|
||||
for i in self.items:
|
||||
self.c.execute(
|
||||
'''select exists(select * from item where item_id=?)''', (i.item_id,))
|
||||
if self.c.fetchone() == (0,):
|
||||
self.c.execute('''insert into item values(?,?,?)''',
|
||||
(i.item_id, i.item_type, i.is_available))
|
||||
self.c.execute('''insert or ignore into item values(?,?,?)''',
|
||||
(i.item_id, i.item_type, i.is_available))
|
||||
|
||||
self.c.execute('''insert into purchase_item values(?,?,?,?)''',
|
||||
self.c.execute('''insert or ignore into purchase_item values(?,?,?,?)''',
|
||||
(self.purchase_name, i.item_id, i.item_type, i.amount))
|
||||
|
||||
def select_exists(self, purchase_name: str = None) -> bool:
|
||||
'''
|
||||
用purchase_name查询存在性
|
||||
'''
|
||||
if purchase_name:
|
||||
self.purchase_name = purchase_name
|
||||
self.c.execute(
|
||||
'''select exists(select * from purchase where purchase_name=?)''', (self.purchase_name,))
|
||||
return self.c.fetchone() == (1,)
|
||||
|
||||
def select(self, purchase_name: str = None) -> 'Purchase':
|
||||
'''
|
||||
用purchase_name查询信息
|
||||
@@ -93,11 +123,11 @@ class Purchase:
|
||||
self.purchase_name = purchase_name
|
||||
|
||||
self.c.execute(
|
||||
'''select * from purchase where purchase_name=:name''', {'name': purchase_name})
|
||||
'''select * from purchase where purchase_name=:name''', {'name': self.purchase_name})
|
||||
x = self.c.fetchone()
|
||||
if not x:
|
||||
raise NoData('The purchase `%s` does not exist.' %
|
||||
purchase_name, 501)
|
||||
raise NoData(
|
||||
f'Purchase `{self.purchase_name}` does not exist.', 501)
|
||||
|
||||
self.price = x[1]
|
||||
self.orig_price = x[2]
|
||||
@@ -112,9 +142,9 @@ class Purchase:
|
||||
self.c.execute(
|
||||
'''select item_id, type, amount from purchase_item where purchase_name=:a''', {'a': self.purchase_name})
|
||||
x = self.c.fetchall()
|
||||
if not x:
|
||||
raise NoData('The items of the purchase `%s` does not exist.' %
|
||||
self.purchase_name, 501)
|
||||
# if not x:
|
||||
# raise NoData(
|
||||
# f'The items of the purchase `{self.purchase_name}` does not exist.', 501)
|
||||
|
||||
self.items = []
|
||||
t = None
|
||||
@@ -162,6 +192,61 @@ class Purchase:
|
||||
for i in self.items:
|
||||
i.user_claim_item(self.user)
|
||||
|
||||
def delete_purchase_item(self) -> None:
|
||||
'''删除purchase_item表'''
|
||||
self.c.execute(
|
||||
'''delete from purchase_item where purchase_name=?''', (self.purchase_name, ))
|
||||
|
||||
def delete(self) -> None:
|
||||
'''删除purchase表'''
|
||||
self.c.execute(
|
||||
'''delete from purchase where purchase_name=?''', (self.purchase_name, ))
|
||||
|
||||
def delete_all(self) -> None:
|
||||
'''删除purchase表和purchase_item表'''
|
||||
self.delete_purchase_item()
|
||||
self.delete()
|
||||
|
||||
def update(self) -> None:
|
||||
'''更新purchase表'''
|
||||
self.c.execute('''update purchase set price=:a, orig_price=:b, discount_from=:c, discount_to=:d, discount_reason=:e where purchase_name=:f''', {
|
||||
'a': self.price, 'b': self.orig_price, 'c': self.discount_from, 'd': self.discount_to, 'e': self.discount_reason, 'f': self.purchase_name})
|
||||
|
||||
def add_items(self, items: list) -> None:
|
||||
'''添加purchase_item表'''
|
||||
for i in items:
|
||||
if not i.select_exists():
|
||||
raise NoData(
|
||||
f'No such item `{i.item_type}`: `{i.item_id}`', api_error_code=-121)
|
||||
if i in self.items:
|
||||
raise DataExist(
|
||||
f'Item `{i.item_type}`: `{i.item_id}` already exists in purchase `{self.purchase_name}`', api_error_code=-123)
|
||||
self.c.executemany('''insert into purchase_item values (?, ?, ?, ?)''', [
|
||||
(self.purchase_name, i.item_id, i.item_type, i.amount) for i in items])
|
||||
self.items.extend(items)
|
||||
|
||||
def delete_items(self, items: list) -> None:
|
||||
'''删除purchase_item表'''
|
||||
for i in items:
|
||||
if i not in self.items:
|
||||
raise NoData(
|
||||
f'No such item `{i.item_type}`: `{i.item_id}` in purchase `{self.purchase_name}`', api_error_code=-124)
|
||||
self.c.executemany('''delete from purchase_item where purchase_name=? and item_id=? and type=?''', [
|
||||
(self.purchase_name, i.item_id, i.item_type) for i in items])
|
||||
for i in items:
|
||||
self.items.remove(i)
|
||||
|
||||
def update_items(self, items: list) -> None:
|
||||
'''更新purchase_item表,只能更新amount'''
|
||||
for i in items:
|
||||
if i not in self.items:
|
||||
raise NoData(
|
||||
f'No such item `{i.item_type}`: `{i.item_id}` in purchase `{self.purchase_name}`', api_error_code=-124)
|
||||
self.c.executemany('''update purchase_item set amount=? where purchase_name=? and item_id=? and type=?''', [
|
||||
(i.amount, self.purchase_name, i.item_id, i.item_type) for i in items])
|
||||
for i in items:
|
||||
self.items[self.items.index(i)].amount = i.amount
|
||||
|
||||
|
||||
class PurchaseList:
|
||||
'''
|
||||
|
||||
@@ -381,13 +381,6 @@ class DatabaseMigrator:
|
||||
c.executemany('''insert into user_char_full values(?,?,?,?,?,?)''', [
|
||||
(j[0], i[0], i[1], exp, i[2], 0) for j in y])
|
||||
|
||||
@staticmethod
|
||||
def update_user_epilogue(c) -> None:
|
||||
'''给用户添加epilogue包'''
|
||||
c.execute('''select user_id from user''')
|
||||
Sql(c).insert_many('user_item', [], [(i[0], 'epilogue', 'pack', 1)
|
||||
for i in c.fetchall()], insert_type='ignore')
|
||||
|
||||
def update_database(self) -> None:
|
||||
'''
|
||||
将c1数据库不存在数据加入或覆盖到c2数据库上
|
||||
@@ -402,7 +395,6 @@ class DatabaseMigrator:
|
||||
self.update_one_table(c1, c2, 'character')
|
||||
|
||||
self.update_user_char_full(c2) # 更新user_char_full
|
||||
self.update_user_epilogue(c2) # 更新user的epilogue
|
||||
|
||||
|
||||
class MemoryDatabase:
|
||||
|
||||
@@ -301,7 +301,7 @@ class UserMap(Map):
|
||||
if self.require_type in ['pack', 'single']:
|
||||
item = ItemFactory(self.c).get_item(self.require_type)
|
||||
item.item_id = self.require_id
|
||||
item.select(self.user)
|
||||
item.select_user_item(self.user)
|
||||
if not item.amount:
|
||||
self.is_locked = True
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import base64
|
||||
from functools import wraps
|
||||
|
||||
from core.config_manager import Config
|
||||
from core.error import ArcError, NoAccess
|
||||
from core.sql import Connect
|
||||
from core.user import UserAuth, UserLogin
|
||||
from flask import Blueprint, g, jsonify, request
|
||||
from flask import Blueprint, g, jsonify, request, current_app
|
||||
|
||||
from .func import arc_try, error_return
|
||||
from .func import arc_try, error_return, header_check
|
||||
|
||||
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
||||
|
||||
@@ -16,9 +15,9 @@ bp = Blueprint('auth', __name__, url_prefix='/auth')
|
||||
@arc_try
|
||||
def login():
|
||||
headers = request.headers
|
||||
if Config.ALLOW_APPVERSION: # 版本检查
|
||||
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
raise NoAccess('Invalid app version.', 1203)
|
||||
e = header_check(request)
|
||||
if e is not None:
|
||||
raise e
|
||||
|
||||
request.form['grant_type']
|
||||
with Connect() as c:
|
||||
@@ -44,9 +43,11 @@ def auth_required(request):
|
||||
|
||||
headers = request.headers
|
||||
|
||||
if Config.ALLOW_APPVERSION: # 版本检查
|
||||
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return error_return(NoAccess('Invalid app version.', 1203))
|
||||
e = header_check(request)
|
||||
if e is not None:
|
||||
current_app.logger.warning(
|
||||
f' - {e.error_code}|{e.api_error_code}: {e}')
|
||||
return error_return(e)
|
||||
|
||||
with Connect() as c:
|
||||
try:
|
||||
|
||||
@@ -19,7 +19,7 @@ def course_me(user_id):
|
||||
user = UserOnline(c, user_id)
|
||||
core = ItemCore(c)
|
||||
core.item_id = 'core_course_skip_purchase'
|
||||
core.select(user)
|
||||
core.select_user_item(user)
|
||||
x = UserCourseList(c, user)
|
||||
x.select_all()
|
||||
return success_return({
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
from functools import wraps
|
||||
from traceback import format_exc
|
||||
|
||||
from core.config_manager import Config
|
||||
from core.error import ArcError
|
||||
from flask import current_app, g, jsonify
|
||||
|
||||
from core.config_manager import Config
|
||||
from core.error import ArcError, NoAccess
|
||||
|
||||
has_arc_hash = False
|
||||
try:
|
||||
from core.arc_crypto import ArcHashChecker # type: ignore
|
||||
has_arc_hash = True
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
default_error = ArcError('Unknown Error', status=500)
|
||||
|
||||
|
||||
@@ -89,3 +97,16 @@ def arc_try(view):
|
||||
return error_return(e)
|
||||
|
||||
return wrapped_view
|
||||
|
||||
|
||||
def header_check(request) -> ArcError:
|
||||
'''检查请求头是否合法'''
|
||||
headers = request.headers
|
||||
if Config.ALLOW_APPVERSION: # 版本检查
|
||||
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return NoAccess('Invalid app version', 1203)
|
||||
|
||||
if has_arc_hash and not ArcHashChecker(request).check():
|
||||
return NoAccess('Invalid request')
|
||||
|
||||
return None
|
||||
|
||||
@@ -8,7 +8,7 @@ from core.user import User, UserLogin, UserOnline, UserRegister
|
||||
from flask import Blueprint, request
|
||||
|
||||
from .auth import auth_required
|
||||
from .func import arc_try, success_return
|
||||
from .func import arc_try, header_check, success_return
|
||||
|
||||
bp = Blueprint('user', __name__, url_prefix='/user')
|
||||
|
||||
@@ -17,9 +17,9 @@ bp = Blueprint('user', __name__, url_prefix='/user')
|
||||
@arc_try
|
||||
def register():
|
||||
headers = request.headers
|
||||
if Config.ALLOW_APPVERSION: # 版本检查
|
||||
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
raise NoAccess('Invalid app version.', 1203)
|
||||
error = header_check(request)
|
||||
if error is not None:
|
||||
raise error
|
||||
|
||||
with Connect() as c:
|
||||
new_user = UserRegister(c)
|
||||
|
||||
Reference in New Issue
Block a user