File size: 3,703 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
"""帖子主记录在最新文件上的局部修改;不处理资金或评论跨文件清理。"""
import copy
from publication_identity import same_publication


class PostStoreError(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 _records(source):
    if not isinstance(source, list):
        raise ValueError('invalid post source')
    identities = 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 identities
                or not isinstance(record.get('author'), str) or not record['author'].strip()):
            raise ValueError('invalid or ambiguous post record')
        identities.add(record['id'])
    return source


def _mutate(store, callback):
    try:
        return store.atomic_update('posts.json', lambda source: callback(_records(source)), default_data=[])
    except _Unchanged as unchanged:
        return unchanged.result
    except PostStoreError:
        raise
    except Exception:
        # 替换后的同步故障也属于未知结果,不推断为未保存,不自动重放。
        raise PostStoreError(503, '帖子保存结果暂时无法确认,请稍后核对') from None


def publish_post(store, record, replay=False):
    saved = copy.deepcopy(record)
    try:
        _records([saved])
    except ValueError:
        raise PostStoreError(400, '帖子发布参数无效') from None
    def append(records):
        existing = next((item for item in records if item['id'] == saved['id']), None)
        if existing is not None:
            fields = ('author', 'title', 'content', 'cover_image', 'images', 'is_original', 'post_type', 'video_url')
            if replay and same_publication(existing, saved, fields):
                raise _Unchanged(copy.deepcopy(existing))
            raise PostStoreError(409, '帖子发布请求与已有记录冲突,请核对后再提交')
        records.insert(0, copy.deepcopy(saved))
        return copy.deepcopy(saved)
    return _mutate(store, append)


def _owned(records, post_id, account, action):
    index = next((index for index, post in enumerate(records) if post['id'] == post_id), None)
    if index is None:
        raise PostStoreError(404, '帖子不存在')
    if records[index]['author'] != account:
        raise PostStoreError(403, f'无权{action}他人帖子')
    return index


def edit_post(store, post_id, account, changes):
    fields = {'title', 'content', 'cover_image', 'images', 'is_original', 'post_type', 'video_url'}
    if not isinstance(changes, dict) or set(changes) - fields:
        raise PostStoreError(400, '帖子编辑参数无效')
    saved = {key: copy.deepcopy(value) for key, value in changes.items() if value is not None}
    def edit(records):
        index = _owned(records, post_id, account, '修改')
        edited = copy.deepcopy(records[index])
        edited.update(copy.deepcopy(saved))
        if 'images' in saved:
            edited['images'] = saved['images'][:9]
        if saved.get('post_type') == 'video':
            edited['images'] = []
        records[index] = edited
        return copy.deepcopy(edited)
    return _mutate(store, edit)


def delete_post(store, post_id, account):
    def remove(records):
        index = _owned(records, post_id, account, '删除')
        records.pop(index)
    return _mutate(store, remove)