Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f5284e067 | |||
| 50d2e62694 | |||
| 70dee494f3 | |||
| 4b2a94002b | |||
| 9eb872391e | |||
| 784830bd62 | |||
| 252bda696f | |||
| ef822d0eba | |||
| cc54fd52c6 | |||
| e4115b8baa | |||
| b99e3e88c9 | |||
| 35198a8edb | |||
| c9142c3f8a | |||
| 9797ddf3f7 | |||
| b92239fb1b | |||
| 105f4d5492 | |||
| e92349e111 | |||
| 0912d658b8 | |||
| 8e63db4424 |
1000
xian_favor/api.py
1000
xian_favor/api.py
File diff suppressed because it is too large
Load Diff
@@ -187,6 +187,15 @@ class Database:
|
|||||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_folders_type ON folders(type)")
|
cursor.execute("CREATE INDEX IF NOT EXISTS idx_folders_type ON folders(type)")
|
||||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_folders_parent ON folders(parent_id)")
|
cursor.execute("CREATE INDEX IF NOT EXISTS idx_folders_parent ON folders(parent_id)")
|
||||||
|
|
||||||
|
# 检查并添加 is_pinned 字段(置顶功能,兼容旧数据库)
|
||||||
|
try:
|
||||||
|
cursor.execute("SELECT is_pinned FROM items LIMIT 1")
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
cursor.execute("ALTER TABLE items ADD COLUMN is_pinned INTEGER DEFAULT 0")
|
||||||
|
|
||||||
|
# 创建 is_pinned 索引
|
||||||
|
cursor.execute("CREATE INDEX IF NOT EXISTS idx_items_pinned ON items(is_pinned)")
|
||||||
|
|
||||||
# 待办事务表(关联到文本类别的items)
|
# 待办事务表(关联到文本类别的items)
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS todo_events (
|
CREATE TABLE IF NOT EXISTS todo_events (
|
||||||
@@ -305,18 +314,16 @@ class Database:
|
|||||||
if conditions:
|
if conditions:
|
||||||
query += " WHERE " + " AND ".join(conditions)
|
query += " WHERE " + " AND ".join(conditions)
|
||||||
|
|
||||||
# 排序逻辑
|
# 排序逻辑:置顶 > 关注 > 创建时间
|
||||||
if sort_by == 'updated_at':
|
if sort_by == 'updated_at':
|
||||||
order_field = 'i.updated_at'
|
order_field = 'i.updated_at'
|
||||||
elif sort_by == 'created_at':
|
elif sort_by == 'created_at':
|
||||||
order_field = 'i.created_at'
|
order_field = 'i.created_at'
|
||||||
else:
|
else:
|
||||||
# 默认:重点关注优先 + 创建时间降序
|
# 默认:置顶优先 + 关注优先 + 创建时间降序
|
||||||
order_field = 'i.created_at'
|
order_field = 'i.created_at'
|
||||||
|
|
||||||
order_dir = 'DESC' if (sort_order == 'asc' or sort_order is None) else 'ASC'
|
# 确定排序方向
|
||||||
# 这里反转逻辑:用户选择"降序"时用DESC,选择"升序"时用ASC
|
|
||||||
|
|
||||||
if sort_order == 'asc':
|
if sort_order == 'asc':
|
||||||
order_dir = 'ASC'
|
order_dir = 'ASC'
|
||||||
elif sort_order == 'desc':
|
elif sort_order == 'desc':
|
||||||
@@ -324,12 +331,12 @@ class Database:
|
|||||||
else:
|
else:
|
||||||
order_dir = 'DESC' # 默认降序
|
order_dir = 'DESC' # 默认降序
|
||||||
|
|
||||||
# 如果有指定排序字段,按该字段排序;否则默认重点关注优先
|
# 如果有指定排序字段,按该字段排序,但置顶始终优先
|
||||||
if sort_by:
|
if sort_by:
|
||||||
query += f" ORDER BY {order_field} {order_dir} LIMIT ? OFFSET ?"
|
query += f" ORDER BY i.is_pinned DESC, {order_field} {order_dir} LIMIT ? OFFSET ?"
|
||||||
else:
|
else:
|
||||||
# 默认:重点关注优先,然后创建时间降序
|
# 默认:置顶 > 关注 > 创建时间降序
|
||||||
query += f" ORDER BY i.is_starred DESC, i.created_at DESC LIMIT ? OFFSET ?"
|
query += f" ORDER BY i.is_pinned DESC, i.is_starred DESC, i.created_at DESC LIMIT ? OFFSET ?"
|
||||||
params.extend([limit, offset])
|
params.extend([limit, offset])
|
||||||
|
|
||||||
cursor.execute(query, params)
|
cursor.execute(query, params)
|
||||||
@@ -391,7 +398,7 @@ class Database:
|
|||||||
|
|
||||||
def update_item(self, item_id: int, **kwargs) -> bool:
|
def update_item(self, item_id: int, **kwargs) -> bool:
|
||||||
"""更新条目"""
|
"""更新条目"""
|
||||||
allowed_fields = ['type', 'title', 'content', 'url', 'source', 'status', 'priority', 'due_date', 'note', 'is_starred']
|
allowed_fields = ['type', 'title', 'content', 'url', 'source', 'status', 'priority', 'due_date', 'note', 'is_starred', 'folder_id']
|
||||||
update_fields = {k: v for k, v in kwargs.items() if k in allowed_fields}
|
update_fields = {k: v for k, v in kwargs.items() if k in allowed_fields}
|
||||||
|
|
||||||
# 只有 tags 变化也算有效更新
|
# 只有 tags 变化也算有效更新
|
||||||
@@ -517,6 +524,31 @@ class Database:
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
return cursor.rowcount > 0
|
return cursor.rowcount > 0
|
||||||
|
|
||||||
|
def toggle_pin(self, item_id: int) -> bool:
|
||||||
|
"""切换置顶状态"""
|
||||||
|
with self.get_conn() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
# 先获取当前状态
|
||||||
|
cursor.execute("SELECT is_pinned FROM items WHERE id = ?", (item_id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if not row:
|
||||||
|
return False
|
||||||
|
|
||||||
|
new_status = 0 if row['is_pinned'] else 1
|
||||||
|
now = datetime.now().isoformat()
|
||||||
|
cursor.execute("UPDATE items SET is_pinned = ?, updated_at = ? WHERE id = ?", (new_status, now, item_id))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_pin(self, item_id: int, pinned: bool = True) -> bool:
|
||||||
|
"""设置置顶状态"""
|
||||||
|
with self.get_conn() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
now = datetime.now().isoformat()
|
||||||
|
cursor.execute("UPDATE items SET is_pinned = ?, updated_at = ? WHERE id = ?", (1 if pinned else 0, now, item_id))
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount > 0
|
||||||
|
|
||||||
def increment_views(self, item_id: int) -> bool:
|
def increment_views(self, item_id: int) -> bool:
|
||||||
"""增加阅读数"""
|
"""增加阅读数"""
|
||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
@@ -919,15 +951,23 @@ class Database:
|
|||||||
stats = {}
|
stats = {}
|
||||||
|
|
||||||
# 总数
|
# 总数
|
||||||
cursor.execute("SELECT COUNT(*) as count FROM items")
|
cursor.execute("SELECT COUNT(*) as count FROM items WHERE is_deleted = 0")
|
||||||
stats['total'] = cursor.fetchone()['count']
|
stats['total'] = cursor.fetchone()['count']
|
||||||
|
|
||||||
# 按类型统计
|
# 按类型统计
|
||||||
cursor.execute("SELECT type, COUNT(*) as count FROM items GROUP BY type")
|
cursor.execute("SELECT type, COUNT(*) as count FROM items WHERE is_deleted = 0 GROUP BY type")
|
||||||
stats['by_type'] = {row['type']: row['count'] for row in cursor.fetchall()}
|
stats['by_type'] = {row['type']: row['count'] for row in cursor.fetchall()}
|
||||||
|
|
||||||
|
# 未读数量统计(views = 0)
|
||||||
|
cursor.execute("SELECT COUNT(*) as count FROM items WHERE is_deleted = 0 AND (views IS NULL OR views = 0)")
|
||||||
|
stats['unread'] = cursor.fetchone()['count']
|
||||||
|
|
||||||
|
# 按类型统计未读数量
|
||||||
|
cursor.execute("SELECT type, COUNT(*) as count FROM items WHERE is_deleted = 0 AND (views IS NULL OR views = 0) GROUP BY type")
|
||||||
|
stats['unread_by_type'] = {row['type']: row['count'] for row in cursor.fetchall()}
|
||||||
|
|
||||||
# 待办状态统计
|
# 待办状态统计
|
||||||
cursor.execute("SELECT status, COUNT(*) as count FROM items WHERE type = 'todo' GROUP BY status")
|
cursor.execute("SELECT status, COUNT(*) as count FROM items WHERE type = 'todo' AND is_deleted = 0 GROUP BY status")
|
||||||
stats['todo_status'] = {row['status']: row['count'] for row in cursor.fetchall()}
|
stats['todo_status'] = {row['status']: row['count'] for row in cursor.fetchall()}
|
||||||
|
|
||||||
# 标签数
|
# 标签数
|
||||||
|
|||||||
Reference in New Issue
Block a user