私域操盘手 - 流量池数据统计

This commit is contained in:
柳清爽
2025-05-13 16:11:58 +08:00
parent fa9e5c4897
commit e9f33309a0
5 changed files with 124 additions and 19 deletions

View File

@@ -88,10 +88,11 @@ interface TrafficPoolResponse {
pageSize: number
totalPages: number
}
statistics: {
total: number
todayNew: number
}
}
interface Statistics {
totalCount: number
todayAddCount: number
}
export default function TrafficPoolPage() {
@@ -108,9 +109,9 @@ export default function TrafficPoolPage() {
const [currentPage, setCurrentPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const [isFetching, setIsFetching] = useState(false)
const [stats, setStats] = useState({
total: 0,
todayNew: 0,
const [stats, setStats] = useState<Statistics>({
totalCount: 0,
todayAddCount: 0
})
const [selectedUser, setSelectedUser] = useState<TrafficUser | null>(null)
const [showUserDetail, setShowUserDetail] = useState(false)
@@ -179,7 +180,7 @@ export default function TrafficPoolPage() {
} as any)
if (response.code === 200) {
const { list, pagination, statistics } = response.data
const { list, pagination } = response.data
const transformedUsers = list.map(user => ({
id: user.id.toString(),
@@ -200,10 +201,6 @@ export default function TrafficPoolPage() {
setUsers(prev => isNewSearch ? transformedUsers : [...prev, ...transformedUsers])
setCurrentPage(page)
setHasMore(list.length > 0 && page < pagination.totalPages)
setStats({
total: statistics.total,
todayNew: statistics.todayNew
})
} else {
toast({
title: "获取数据失败",
@@ -281,6 +278,33 @@ export default function TrafficPoolPage() {
}
}, [])
const fetchStatistics = useCallback(async () => {
try {
const response = await api.get<ApiResponse<Statistics>>('/v1/traffic/pool/statistics', {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`
}
} as any)
if (response.code === 200) {
setStats(response.data)
} else {
toast({
title: "获取统计数据失败",
description: response.msg || "请稍后重试",
variant: "destructive",
})
}
} catch (error) {
console.error("获取统计数据失败:", error)
toast({
title: "获取统计数据失败",
description: "请检查网络连接或稍后重试",
variant: "destructive",
})
}
}, [])
// 处理搜索
const handleSearch = useCallback(() => {
setUsers([])
@@ -315,6 +339,7 @@ export default function TrafficPoolPage() {
useEffect(() => {
fetchStatusTypes()
fetchSourceTypes()
fetchStatistics()
fetchUsers(1, true)
}, [])
@@ -377,11 +402,11 @@ export default function TrafficPoolPage() {
<div className="grid grid-cols-2 gap-4">
<Card className="p-4">
<div className="text-sm text-gray-500"></div>
<div className="text-2xl font-bold text-blue-600">{stats.total}</div>
<div className="text-2xl font-bold text-blue-600">{stats.totalCount}</div>
</Card>
<Card className="p-4">
<div className="text-sm text-gray-500"></div>
<div className="text-2xl font-bold text-green-600">{stats.todayNew}</div>
<div className="text-2xl font-bold text-green-600">{stats.todayAddCount}</div>
</Card>
</div>

View File

@@ -9,6 +9,14 @@ use think\Model;
*/
class TrafficSource extends Model
{
const STATUS_PENDING = 1; // 待处理
const STATUS_WORKING = 2; // 处理中
const STATUS_PASSED = 3; // 已通过
const STATUS_REFUSED = 4; // 已拒绝
const STATUS_EXPIRED = 5; // 已过期
const STATUS_CANCELED = 6; // 已取消
// 设置数据表名
protected $name = 'traffic_source';

View File

@@ -44,6 +44,7 @@ Route::group('v1/', function () {
Route::get('', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@index');
Route::get('types', 'app\cunkebao\controller\traffic\GetPotentialTypeSectionV1Controller@index');
Route::get('sources', 'app\cunkebao\controller\traffic\GetTrafficSourceSectionV1Controller@index');
Route::get('statistics', 'app\cunkebao\controller\traffic\GetPoolStatisticsV1Controller@index');
});
// 工作台相关

View File

@@ -0,0 +1,70 @@
<?php
namespace app\cunkebao\controller\traffic;
use app\common\model\TrafficSource as TrafficSourceModel;
use app\cunkebao\controller\BaseController;
use library\ResponseHelper;
/**
* 流量池控制器
*/
class GetPoolStatisticsV1Controller extends BaseController
{
/**
* 获取今日转化数量
*
* @return int
*/
protected function getTodayAddedCount(): int
{
return TrafficSourceModel::where(
[
'companyId' => $this->getUserInfo('companyId'),
'status' => TrafficSourceModel::STATUS_PASSED,
]
)
->whereBetween('updateTime',
[
strtotime(date('Y-m-d 00:00:00')),
strtotime(date('Y-m-d 23:59:59'))
]
)
->count('*');
}
/**
* 获取流量池总数
*
* @return int
* @throws \Exception
*/
protected function getTotalCount(): int
{
return TrafficSourceModel::where(
[
'companyId' => $this->getUserInfo('companyId')
]
)
->count('*');
}
/**
* 获取流量池数据统计
*
* @return \think\response\Json
*/
public function index()
{
try {
return ResponseHelper::success(
[
'totalCount' => $this->getTotalCount(),
'todayAddCount' => $this->getTodayAddedCount(),
]
);
} catch (\Exception $e) {
return ResponseHelper::error($e->getMessage(), $e->getCode());
}
}
}

View File

@@ -3,6 +3,7 @@
namespace app\cunkebao\controller\traffic;
use app\common\model\TrafficPool as TrafficPoolModel;
use app\common\model\TrafficSource as TrafficSourceModel;
use app\common\model\WechatFriendShip as WechatFriendShipModel;
use app\cunkebao\controller\BaseController;
use library\ResponseHelper;
@@ -21,23 +22,23 @@ class GetPotentialTypeSectionV1Controller extends BaseController
{
return [
[
'id' => 1,
'id' => TrafficSourceModel::STATUS_PENDING,
'name' => '待处理'
],
[
'id' => 2,
'id' => TrafficSourceModel::STATUS_WORKING,
'name' => '处理中'
],
[
'id' => 4,
'id' => TrafficSourceModel::STATUS_REFUSED,
'name' => '已拒绝'
],
[
'id' => 5,
'id' => TrafficSourceModel::STATUS_EXPIRED,
'name' => '已过期'
],
[
'id' => 6,
'id' => TrafficSourceModel::STATUS_CANCELED,
'name' => '已取消'
]
];