diff --git a/backend/app/routers/yichens.py b/backend/app/routers/yichens.py
index e163b4a..331be20 100644
--- a/backend/app/routers/yichens.py
+++ b/backend/app/routers/yichens.py
@@ -32,6 +32,7 @@ class PostItem(BaseModel):
reply_count: int
view_count: int
url: Optional[str]
+ content: Optional[str]
class UserStat(BaseModel):
total_users: int
@@ -42,6 +43,7 @@ class UserItem(BaseModel):
user_id: str
username: str
avatar_url: Optional[str]
+ content: Optional[str]
credit_level: Optional[str]
credit_score: Optional[int]
post_count: int
@@ -105,7 +107,7 @@ def get_user_stats(db: Session = Depends(get_coolbot_db)):
@router.get("/posts", response_model=List[PostItem])
def get_posts(
- limit: int = Query(20, ge=1, le=100),
+ limit: int = Query(20, ge=1, le=500),
offset: int = Query(0, ge=0),
category: Optional[str] = None,
post_type: Optional[str] = None,
@@ -113,7 +115,7 @@ def get_posts(
):
"""获取帖子列表"""
query = """
- SELECT post_id, title, category, post_type, price,
+ SELECT post_id, title, content, category, post_type, price,
author_username, post_time, reply_count, view_count, url
FROM yichens_posts
WHERE 1=1
@@ -135,19 +137,20 @@ def get_posts(
return [PostItem(
post_id=r[0],
title=r[1] or "",
- category=r[2],
- post_type=r[3] or "",
- price=float(r[4]) if r[4] else None,
- author_username=r[5] or "",
- post_time=str(r[6]) if r[6] else "",
- reply_count=r[7] or 0,
- view_count=r[8] or 0,
- url=r[9]
+ content=r[2] or "",
+ category=r[3],
+ post_type=r[4] or "",
+ price=float(r[5]) if r[5] else None,
+ author_username=r[6] or "",
+ post_time=str(r[7]) if r[7] else "",
+ reply_count=r[8] or 0,
+ view_count=r[9] or 0,
+ url=r[10]
) for r in results]
@router.get("/users", response_model=List[UserItem])
def get_users(
- limit: int = Query(20, ge=1, le=100),
+ limit: int = Query(20, ge=1, le=500),
offset: int = Query(0, ge=0),
is_seller: Optional[bool] = None,
db: Session = Depends(get_coolbot_db)
@@ -179,3 +182,69 @@ def get_users(
is_seller=r[6] or False,
registration_date=str(r[7]) if r[7] else None
) for r in results]
+
+
+@router.get("/stats/today")
+async def get_today_stats(db: Session = Depends(get_coolbot_db)):
+ """获取今日新增帖子统计"""
+ query = """
+ SELECT
+ COUNT(*) as total,
+ SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
+ SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants,
+ SUM(CASE WHEN post_type = 'normal' THEN 1 ELSE 0 END) as others,
+ SUM(CASE WHEN category LIKE '%龙%' THEN 1 ELSE 0 END) as dragons,
+ SUM(CASE WHEN category LIKE '%马%' THEN 1 ELSE 0 END) as horses,
+ SUM(CASE WHEN category LIKE '%蛇%' THEN 1 ELSE 0 END) as snakes
+ FROM yichens_posts
+ WHERE post_time >= CURRENT_DATE
+ """
+ result = db.execute(text(query)).fetchone()
+ return {
+ "total": result[0] or 0,
+ "deals": result[1] or 0,
+ "wants": result[2] or 0,
+ "others": result[3] or 0,
+ "dragons": result[3] or 0,
+ "horses": result[4] or 0,
+ "snakes": result[5] or 0
+ }
+
+@router.get("/stats/hour")
+async def get_hour_stats(db: Session = Depends(get_coolbot_db)):
+ """获取近一个小时新增帖子统计"""
+ query = """
+ SELECT
+ COUNT(*) as total,
+ SUM(CASE WHEN post_type = 'deal' THEN 1 ELSE 0 END) as deals,
+ SUM(CASE WHEN post_type = 'want' THEN 1 ELSE 0 END) as wants,
+ SUM(CASE WHEN post_type = 'normal' THEN 1 ELSE 0 END) as others,
+ SUM(CASE WHEN category LIKE '%龙%' THEN 1 ELSE 0 END) as dragons,
+ SUM(CASE WHEN category LIKE '%马%' THEN 1 ELSE 0 END) as horses,
+ SUM(CASE WHEN category LIKE '%蛇%' THEN 1 ELSE 0 END) as snakes
+ FROM yichens_posts
+ WHERE post_time >= NOW() - INTERVAL '1 hour'
+ """
+ result = db.execute(text(query)).fetchone()
+ return {
+ "total": result[0] or 0,
+ "deals": result[1] or 0,
+ "wants": result[2] or 0,
+ "others": result[3] or 0,
+ "dragons": result[3] or 0,
+ "horses": result[4] or 0,
+ "snakes": result[5] or 0
+ }
+
+@router.get("/stats/today-category")
+async def get_today_category_stats(db: Session = Depends(get_coolbot_db)):
+ """获取今日帖子分类统计"""
+ query = """
+ SELECT category, COUNT(*) as count
+ FROM yichens_posts
+ WHERE post_time >= CURRENT_DATE
+ GROUP BY category
+ ORDER BY count DESC
+ """
+ results = db.execute(text(query)).fetchall()
+ return [{"category": r[0] or "未分类", "count": r[1]} for r in results]
diff --git a/config/VERSION b/config/VERSION
index 4aee9f9..79ee983 100644
--- a/config/VERSION
+++ b/config/VERSION
@@ -1 +1 @@
-VERSION=1.2.42
+VERSION=1.2.43
diff --git a/frontend/index.html b/frontend/index.html
index bf5cc5a..42efbe9 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -4,7 +4,7 @@
-
甲辰收藏 v1.2.41
+ 甲辰收藏 v1.2.42
diff --git a/frontend/src/pages/YichensBoard.jsx b/frontend/src/pages/YichensBoard.jsx
index f105ce4..0785a51 100644
--- a/frontend/src/pages/YichensBoard.jsx
+++ b/frontend/src/pages/YichensBoard.jsx
@@ -1,66 +1,99 @@
import React, { useState, useEffect } from 'react'
export default function YichensBoard() {
- const [stats, setStats] = useState({ posts: null, categories: [], users: null })
+ const [todayStats, setTodayStats] = useState({ total: 0, deals: 0, wants: 0, others: 0 })
+ const [todayCategory, setTodayCategory] = useState([])
const [posts, setPosts] = useState([])
const [loading, setLoading] = useState(false)
const [expandedPosts, setExpandedPosts] = useState({})
const [postTypeFilter, setPostTypeFilter] = useState('all')
+ const [categoryFilter, setCategoryFilter] = useState('')
+ const [page, setPage] = useState(1)
+ const [totalPosts, setTotalPosts] = useState(0)
const API_BASE = localStorage.getItem('API_BASE') || ''
- useEffect(() => { fetchStats(); fetchPosts() }, [])
+ const today = new Date()
+ const dateStr = today.getFullYear() + '/' + (today.getMonth() + 1) + '/' + today.getDate()
- const fetchStats = async () => {
+ useEffect(() => { fetchTodayStats(); fetchPosts(1, '') }, [])
+
+ const fetchTodayStats = async () => {
try {
- const [p, c, u] = await Promise.all([
- fetch(`${API_BASE}/api/yichens/stats/posts?days=30`),
- fetch(`${API_BASE}/api/yichens/stats/categories?days=30`),
- fetch(`${API_BASE}/api/yichens/stats/users`)
- ])
- setStats({ posts: await p.json(), categories: await c.json(), users: await u.json() })
+ const res = await fetch(API_BASE + '/api/yichens/stats/today')
+ setTodayStats(await res.json())
} catch(e) { console.error(e) }
}
- const fetchPosts = async () => {
+ const fetchTodayCategory = async () => {
+ try {
+ const res = await fetch(API_BASE + '/api/yichens/stats/today-category')
+ setTodayCategory(await res.json())
+ } catch(e) { console.error(e) }
+ }
+
+ const fetchPosts = async (p, cat) => {
setLoading(true)
- let url = `${API_BASE}/api/yichens/posts?limit=50`
- if (postTypeFilter !== 'all') url += `&post_type=${postTypeFilter}`
+ const currentPage = p !== undefined ? p : page
+ const currentCat = cat !== undefined ? cat : categoryFilter
+
+ let url = API_BASE + '/api/yichens/posts?limit=390&offset=' + ((currentPage - 1) * 390)
+ if (postTypeFilter === 'deal') url += '&post_type=deal'
+ else if (postTypeFilter === 'want') url += '&post_type=want'
+ else if (postTypeFilter === 'other') url += '&post_type=normal'
try {
const res = await fetch(url)
- setPosts((await res.json()) || [])
+ let data = await res.json() || []
+ if (currentCat) {
+ if (currentCat === '龙') {
+ data = data.filter(p => p.category && (p.category.includes('龙') || p.category.includes('纪念钞')))
+ } else if (currentCat === '蛇') {
+ data = data.filter(p => p.category && p.category.includes('蛇'))
+ } else if (currentCat === '马') {
+ data = data.filter(p => p.category && p.category.includes('马'))
+ } else if (currentCat === '其他') {
+ data = data.filter(p => p.category && !p.category.includes('龙') && !p.category.includes('纪念钞') && !p.category.includes('蛇') && !p.category.includes('马'))
+ }
+ }
+ setPosts(data)
+ setTotalPosts(todayStats.total || 0)
} catch { setPosts([]) }
setLoading(false)
}
+ useEffect(() => { if (todayStats.total > 0) fetchTodayCategory() }, [todayStats])
+
const togglePost = id => setExpandedPosts(p => ({ ...p, [id]: !p[id] }))
- const filtered = postTypeFilter === 'all' ? posts : posts.filter(p => p.post_type === postTypeFilter)
+
+ const StatCard = ({ label, value, color, onClick }) => (
+
+ )
return (
- {stats.posts && (
-
-
-
30天帖子
-
{stats.posts.total_posts}
-
出售{stats.posts.total_deals} 求购{stats.posts.total_wants}
-
-
-
浏览量
-
{(stats.posts.total_views/10000).toFixed(1)}万
-
回复{stats.posts.total_replies}
-
-
-
用户数
-
{stats.users?.total_users||0}
-
今日新增{stats.users?.new_users_today||0}
-
+
+
+ 📈 连体钞/纪念钞 {dateStr}
- )}
+
+ { setPostTypeFilter('all'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+ { setPostTypeFilter('deal'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+ { setPostTypeFilter('want'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+ { setPostTypeFilter('other'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+
+
-
📊 分类统计
+
📊 今日分类统计
- {stats.categories.map(cat => (
+ {todayCategory.map(cat => (
{cat.category} ({cat.count})
@@ -68,9 +101,9 @@ export default function YichensBoard() {
-
- {[{key:'all',label:'全部'},{key:'deal',label:'出售'},{key:'want',label:'求购'}].map(k => (
-