File size: 6,167 Bytes
c8e5eaa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""提示词主记录与分类的锁内局部修改,价格只在副本上投影。"""
import copy
from publication_identity import same_publication
import datetime
from purchase_transaction import effective_resource_price


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


class _Unchanged(Exception):
    def __init__(self, result): self.result = result


def _mutate(store, filename, callback, default):
    try:
        return store.atomic_update(filename, callback, default_data=default)
    except _Unchanged as unchanged:
        return unchanged.result
    except PromptStoreError:
        raise
    except Exception:
        raise PromptStoreError(503, '提示词保存结果暂时无法确认,请稍后核对') from None


def _records(source):
    if not isinstance(source, list): raise ValueError('invalid prompt collection')
    ids = set()
    for record in source:
        if (not isinstance(record, dict) or not isinstance(record.get('id'), str)
                or not record['id'].strip() or record['id'] in ids
                or not isinstance(record.get('author'), str) or not record['author'].strip()):
            raise ValueError('invalid prompt identity')
        ids.add(record['id'])
    return source


def read_prompts(store):
    try:
        return copy.deepcopy(_records(store.load_data('prompts.json', default_data=[], strict=True, skip_cache=True)))
    except Exception:
        raise PromptStoreError(503, '提示词暂时不可读取') from None


def project_price(record):
    result = copy.deepcopy(record)
    result['price'] = effective_resource_price(result)
    if result.get('pending_price') is not None and result.get('pending_price_effective_at'):
        when = datetime.datetime.fromisoformat(result['pending_price_effective_at'])
        now = datetime.datetime.now(when.tzinfo) if when.tzinfo else datetime.datetime.now()
        if now >= when:
            result['pending_price'] = None
            result['pending_price_effective_at'] = None
    return result


def publish_prompt(store, record, replay=False):
    saved = copy.deepcopy(record)
    try:
        _records([saved])
        effective_resource_price(saved)
    except Exception:
        raise PromptStoreError(400, '提示词发布参数无效') from None
    def append(source):
        records = _records(source)
        existing = next((item for item in records if item['id'] == saved['id']), None)
        if existing is not None:
            fields = ('author', 'prompt_type', 'category', 'title', 'content', 'prompt_text', 'cover_image',
                      'media_type', 'video_url', 'audio_url', 'images', 'is_original', 'price', 'tags')
            if replay and same_publication(existing, saved, fields):
                raise _Unchanged(copy.deepcopy(existing))
            raise PromptStoreError(409, '提示词发布请求与已有记录冲突,请核对后再提交')
        records.insert(0, copy.deepcopy(saved))
        return copy.deepcopy(saved)
    return _mutate(store, 'prompts.json', append, [])


def _owned(source, prompt_id, account, action):
    records = _records(source)
    index = next((index for index, record in enumerate(records) if record['id'] == prompt_id), None)
    if index is None: raise PromptStoreError(404, '提示词不存在')
    if records[index]['author'] != account:
        raise PromptStoreError(403, f'无权{action}他人提示词')
    return index


def edit_prompt(store, prompt_id, account, apply):
    def edit(source):
        index = _owned(source, prompt_id, account, '修改')
        edited = project_price(source[index])
        apply(edited)
        source[index] = edited
        return copy.deepcopy(edited)
    return _mutate(store, 'prompts.json', edit, [])


def delete_prompt(store, prompt_id, account):
    def remove(source): source.pop(_owned(source, prompt_id, account, '删除'))
    return _mutate(store, 'prompts.json', remove, [])


def _categories(source, defaults):
    if not isinstance(source, dict): raise ValueError('invalid prompt categories')
    result = copy.deepcopy(source)
    for kind, values in defaults.items():
        existing = result.setdefault(kind, copy.deepcopy(values))
        if not isinstance(existing, list) or not all(isinstance(name, str) and name.strip() for name in existing):
            raise ValueError('invalid prompt category group')
    return result


def load_categories(store, defaults):
    try:
        source = store.load_data('prompt_categories.json', default_data={}, strict=True, skip_cache=True)
        completed = _categories(source, defaults)
    except Exception:
        raise PromptStoreError(503, '提示词分类暂时不可读取') from None
    if completed == source: return completed
    def initialize(latest):
        completed = _categories(latest, defaults)
        if completed == latest: raise _Unchanged(completed)
        latest.clear(); latest.update(completed)
        return copy.deepcopy(completed)
    return _mutate(store, 'prompt_categories.json', initialize, {})


def modify_category(store, defaults, kind, name, *, delete=False, maximum=30):
    if kind not in defaults or not isinstance(name, str) or not name.strip():
        raise PromptStoreError(400, '分类参数无效')
    def update(source):
        completed = _categories(source, defaults)
        values = completed[kind]
        if delete:
            if name not in values: raise PromptStoreError(404, '分类不存在')
            values.remove(name)
        else:
            if name in values: raise PromptStoreError(400, '该分类已存在')
            if len(values) >= maximum: raise PromptStoreError(400, f'分类数量已达上限({maximum})')
            values.append(name)
        source.clear(); source.update(completed)
        return copy.deepcopy(values)
    return _mutate(store, 'prompt_categories.json', update, {})