File size: 3,550 Bytes
4bcc697
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""下载代理共用资源/有效价格/当前所有权核查,不把存储故障视为免费。"""
from owned_thread import OwnedThreadGroup
from models_sql import Ownership
from purchase_transaction import effective_resource_price


class ProxyAccessError(Exception):
    def __init__(self, status_code, message):
        self.status_code = status_code
        self.message = message
        super().__init__(message)


def read_proxy_item(store, item_id):
    try:
        source = store.load_data('items.json', default_data=[], strict=True, skip_cache=True)
        if not isinstance(source, list):
            raise ValueError('invalid resource source')
        if any(not isinstance(item, dict) or not isinstance(item.get('id'), str) or not item['id'] for item in source):
            raise ValueError('invalid resource record')
        matches = [item for item in source if item.get('id') == item_id]
        if not matches:
            raise ProxyAccessError(404, '资源不存在')
        if len(matches) != 1:
            raise ValueError('ambiguous resource')
        item = matches[0]
        author = item.get('author')
        if not isinstance(author, str) or not author.strip():
            raise ValueError('invalid resource author')
        effective_resource_price(item)
        return item
    except ProxyAccessError:
        raise
    except Exception:
        raise ProxyAccessError(503, '资源信息暂时无法核实,请稍后核对') from None


def _authorized_proxy_item(store, session, item_id, account):
    try:
        item = read_proxy_item(store, item_id)
        author = item['author']
        price = effective_resource_price(item)
        if price > 0 and account != author:
            # SQL会话仍留在调用线程;不用陈旧的身份/拥有缓存,也不授予已退款记录。
            ownerships = session.query(Ownership).filter(Ownership.account == account, Ownership.item_id == item_id).populate_existing().all()
            if any(type(row.is_refunded) is not bool for row in ownerships):
                raise ValueError('invalid ownership refund state')
            rows = [row for row in ownerships if not row.is_refunded]
            if len(rows) > 1:
                raise ValueError('ambiguous ownership')
            if not rows:
                raise ProxyAccessError(403, '尚未获取该资源')
        return item
    except ProxyAccessError:
        raise
    except Exception:
        raise ProxyAccessError(503, '下载授权暂时无法核实,请稍后核对') from None
    finally:
        try:
            session.rollback()  # 不让下载流长期持有所有权读取事务。
        except Exception:
            raise ProxyAccessError(503, '下载授权暂时无法核实,请稍后核对') from None


MAX_PROXY_ACCESS = 8
_proxy_access_io = OwnedThreadGroup(MAX_PROXY_ACCESS,
    lambda: ProxyAccessError(503, '下载授权暂时无法核实,请稍后核对'))
start_proxy_access = _proxy_access_io.start
stop_proxy_access = _proxy_access_io.stop
close_proxy_access = _proxy_access_io.close


async def authorized_proxy_item(store, session_factory, item_id, account):
    def execute():
        try:
            with session_factory() as session:
                return _authorized_proxy_item(store, session, item_id, account)
        except ProxyAccessError:
            raise
        except Exception:
            raise ProxyAccessError(503, '下载授权暂时无法核实,请稍后核对') from None
    return await _proxy_access_io.run(execute)