feat(points): 新增今日消耗接口 + 个人中心字体调整

后端:
- CRUD 新增 sum_consumed_today() 方法,统计用户今日消费积分总和
- API 新增 GET /points/today-consumed 路由

前端:
- 个人中心积分数字从 40px 改为 32px
- 今日消耗从本地计算改为调用后端接口
This commit is contained in:
小鱼开发
2026-05-16 09:46:41 +08:00
parent 7421e9dd7c
commit 83b10945c8
4 changed files with 52 additions and 17 deletions
+12
View File
@@ -497,6 +497,18 @@ async def get_points_rules(
# ── 今日消费统计 ──────────────────────────────────────
@router.get("/today-consumed", response_model=ApiResponse[dict])
async def get_today_consumed(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取当前用户今日消费积分总额"""
total = await point_transaction.sum_consumed_today(db, user_id=current_user.id)
return success_response(data={"total": total})
# ── 直接消费扣费(前端/Rust 层调用)───────────────────
@router.post("/consume", response_model=ApiResponse[dict])
+22 -2
View File
@@ -5,9 +5,9 @@
只增不改,用于审计和对账。
"""
from datetime import datetime
from datetime import datetime, time
from sqlalchemy import select
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.crud.base import CRUDBase
@@ -101,6 +101,26 @@ class PointTransactionCRUD(CRUDBase[PointTransaction]):
)
return list(result.scalars().all())
async def sum_consumed_today(
self,
db: AsyncSession,
*,
user_id: str,
) -> int:
"""统计用户今日消费积分总和"""
now = datetime.now()
start_of_day = datetime.combine(now.date(), time.min)
stmt = (
select(func.coalesce(func.sum(PointTransaction.amount), 0))
.where(
PointTransaction.user_id == user_id,
PointTransaction.type == "consume",
PointTransaction.created_at >= start_of_day,
)
)
result = await db.execute(stmt)
return result.scalar() or 0
# 导出实例
point_transaction = PointTransactionCRUD()
+9
View File
@@ -165,6 +165,15 @@ export const pointsApi = {
);
},
/**
* GET /points/today-consumed
*
* 获取今日消费积分总额。
*/
getTodayConsumed: async (): Promise<{ total: number }> => {
return client.get<{ total: number }>('/points/today-consumed');
},
/**
* 直接消费积分(前端/Rust 层业务扣费)
* POST /points/consume
+9 -15
View File
@@ -44,6 +44,7 @@ export default function Profile() {
const [user, setUser] = useState<UserProfile | null>(null);
const [balance, setBalance] = useState<PointBalance | null>(null);
const [recentTx, setRecentTx] = useState<PointTransaction[]>([]);
const [todayConsumed, setTodayConsumed] = useState(0);
const [loading, setLoading] = useState(true);
const [showRechargeModal, setShowRechargeModal] = useState(false);
@@ -66,10 +67,12 @@ export default function Profile() {
}
if (balanceData) {setBalance(balanceData);}
const txData = await pointsApi
.getTransactions({ page: 1, pageSize: 10 })
.catch(() => null);
const [txData, todayData] = await Promise.all([
pointsApi.getTransactions({ page: 1, pageSize: 10 }).catch(() => null),
pointsApi.getTodayConsumed().catch(() => null),
]);
if (txData) {setRecentTx(txData.items);}
if (todayData) {setTodayConsumed(todayData.total);}
} catch (e) {
console.error('[Profile] 加载数据失败:', e);
} finally {
@@ -212,7 +215,7 @@ export default function Profile() {
<div style={{ fontSize: '13px', color: 'var(--text-secondary)', marginBottom: '8px' }}>
</div>
<div style={{ fontSize: '40px', fontWeight: 700, color: '#36b26a', lineHeight: 1 }}>
<div style={{ fontSize: '32px', fontWeight: 700, color: '#36b26a', lineHeight: 1 }}>
{balance?.balance ?? 0}
</div>
</div>
@@ -220,17 +223,8 @@ export default function Profile() {
<div style={{ fontSize: '13px', color: 'var(--text-secondary)', marginBottom: '8px' }}>
</div>
<div style={{ fontSize: '40px', fontWeight: 700, color: '#ff6b6b', lineHeight: 1 }}>
{(() => {
const todayStr = new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-');
return recentTx
.filter(tx => {
if (tx.type !== 'consume') return false;
const txDate = new Date(tx.createdAt).toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-');
return txDate === todayStr;
})
.reduce((sum, tx) => sum + tx.amount, 0);
})()}
<div style={{ fontSize: '32px', fontWeight: 700, color: '#ff6b6b', lineHeight: 1 }}>
{todayConsumed}
</div>
</div>
</div>