优化渠道

This commit is contained in:
wong
2025-12-20 16:45:25 +08:00
parent 20f567da37
commit f208bbdb9a
5 changed files with 230 additions and 154 deletions

View File

@@ -1,5 +1,6 @@
// 渠道详情 API(模拟数据)
// 渠道详情 API
import request from "@/api/request";
import type {
ChannelDetail,
ChannelStatistics,
@@ -7,186 +8,204 @@ import type {
RevenueType,
WithdrawalDetailRecord,
WithdrawalDetailStatus,
ChannelHomeData,
} from "./data";
// 模拟延迟
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// 获取渠道详情
// 获取渠道详情(使用真实接口)
export const fetchChannelDetail = async (
id: string,
channelCode: string,
): Promise<ChannelDetail> => {
await delay(500);
const data = await request(
"/v1/frontend/distribution/user/home",
{ channelCode },
"GET",
) as ChannelHomeData;
// 将接口返回的 channelInfo 转换为 ChannelDetail 格式
return {
id: "CH1765355105571YX9UWX",
name: "张大大",
code: "CH1765355105571YX9UWX",
phone: undefined,
wechatId: undefined,
createType: "manual",
remark: undefined,
createTime: "2025-12-10 16:25",
id: data.channelInfo.channelCode, // 使用 channelCode 作为 id
name: data.channelInfo.channelName,
code: data.channelInfo.channelCode,
phone: data.channelInfo.phone,
wechatId: data.channelInfo.wechatId,
createType: data.channelInfo.createType,
remark: data.channelInfo.remark,
createTime: data.channelInfo.createTime,
};
};
// 获取渠道统计数据
// 获取渠道统计数据(使用真实接口)
export const fetchChannelStatistics = async (
id: string,
channelCode: string,
): Promise<ChannelStatistics> => {
await delay(500);
const data = await request(
"/v1/frontend/distribution/user/home",
{ channelCode },
"GET",
) as ChannelHomeData;
// 将接口返回的数据转换为 ChannelStatistics 格式
return {
totalFriends: 0,
todayFriends: 0,
totalCustomers: 0,
todayCustomers: 0,
totalRevenue: 8.0,
pendingWithdrawal: 8.0,
pendingReview: 0.0,
withdrawn: 0.0,
totalFriends: data.customerStats.totalFriends,
todayFriends: data.customerStats.todayFriends,
totalCustomers: data.customerStats.totalCustomers,
todayCustomers: data.customerStats.todayCustomers,
totalRevenue: data.financialStats.totalRevenue,
pendingWithdrawal: data.financialStats.withdrawableAmount,
pendingReview: data.financialStats.pendingReview,
withdrawn: data.financialStats.withdrawn,
};
};
// 获取收益明细列表
// 获取渠道首页数据(完整数据)
export const fetchChannelHomeData = async (
channelCode: string,
): Promise<ChannelHomeData> => {
return request("/v1/frontend/distribution/user/home", { channelCode }, "GET") as Promise<ChannelHomeData>;
};
// 将接口返回的 type 映射到前端的 RevenueType
const mapApiTypeToRevenueType = (apiType: string): "addFriend" | "customer" | "other" => {
if (apiType === "customer_acquisition") {
return "customer";
}
if (apiType === "add_friend" || apiType === "addFriend") {
return "addFriend";
}
return "other";
};
// 将前端的 RevenueType 映射到接口的 filterType
const mapRevenueTypeToFilterType = (type?: RevenueType): string => {
if (!type || type === "all") {
return "";
}
if (type === "customer") {
return "customer_acquisition";
}
if (type === "addFriend") {
return "add_friend";
}
return "";
};
// 获取收益明细列表(使用真实接口)
export const fetchRevenueList = async (params: {
channelId: string;
channelCode: string;
page?: number;
limit?: number;
type?: RevenueType;
date?: string;
}): Promise<{ list: RevenueRecord[]; total: number; page: number }> => {
await delay(500);
// 模拟数据
const mockRecords: RevenueRecord[] = [
const response = await request(
"/v1/frontend/distribution/user/revenue-records",
{
id: "REV001",
title: "加好友任务-春季活动",
type: "addFriend",
typeLabel: "加好友",
amount: 5.0,
date: "2025-12-09 16:54",
channelCode: params.channelCode,
page: params.page || 1,
limit: params.limit || 10,
filterType: mapRevenueTypeToFilterType(params.type),
date: params.date || "",
},
];
"GET",
) as {
list: Array<{
id: string;
sourceType: string;
type: string;
typeLabel: string;
amount: number;
remark?: string;
createTime: string;
}>;
total: number;
page: number;
limit: number;
};
// 筛选过滤
let filteredList = mockRecords;
if (params.type && params.type !== "all") {
filteredList = filteredList.filter(item => item.type === params.type);
}
if (params.date) {
filteredList = filteredList.filter(item => item.date.startsWith(params.date!));
}
const page = params.page || 1;
const limit = params.limit || 10;
const start = (page - 1) * limit;
const end = start + limit;
const paginatedList = filteredList.slice(start, end);
// 将接口返回的数据转换为 RevenueRecord 格式
const list: RevenueRecord[] = response.list.map(item => ({
id: item.id,
title: item.sourceType || item.typeLabel, // 使用 sourceType 作为 title如果没有则使用 typeLabel
type: mapApiTypeToRevenueType(item.type),
typeLabel: item.typeLabel,
amount: item.amount, // 金额是分
date: item.createTime,
remark: item.remark,
}));
return {
list: paginatedList,
total: filteredList.length,
page,
list,
total: response.total,
page: response.page,
};
};
// 获取提现明细列表
// 获取提现明细列表(使用真实接口)
export const fetchWithdrawalDetailList = async (params: {
channelId: string;
channelCode: string;
page?: number;
limit?: number;
status?: WithdrawalDetailStatus;
payType?: "all" | "wechat" | "alipay" | "bankcard";
date?: string;
}): Promise<{
list: WithdrawalDetailRecord[];
total: number;
page: number;
}> => {
await delay(500);
const requestParams: any = {
channelCode: params.channelCode,
page: params.page || 1,
limit: params.limit || 10,
status: params.status || "all",
payType: params.payType || "all",
};
// 模拟数据
const mockRecords: WithdrawalDetailRecord[] = [
{
id: "WD001",
amount: 100.0,
status: "pending",
applyDate: "2025-12-10 14:30",
},
{
id: "WD002",
amount: 50.0,
status: "approved",
applyDate: "2025-12-09 10:20",
reviewDate: "2025-12-09 15:30",
},
{
id: "WD003",
amount: 200.0,
status: "paid",
applyDate: "2025-12-08 09:15",
reviewDate: "2025-12-08 14:20",
paidDate: "2025-12-08 16:45",
},
{
id: "WD004",
amount: 80.0,
status: "rejected",
applyDate: "2025-12-07 11:00",
reviewDate: "2025-12-07 16:00",
remark: "提现金额超出限制",
},
{
id: "WD005",
amount: 150.0,
status: "approved",
applyDate: "2025-12-06 13:25",
reviewDate: "2025-12-06 18:10",
},
{
id: "WD006",
amount: 120.0,
status: "paid",
applyDate: "2025-12-05 08:30",
reviewDate: "2025-12-05 12:15",
paidDate: "2025-12-05 14:20",
},
{
id: "WD007",
amount: 60.0,
status: "pending",
applyDate: "2025-12-04 15:40",
},
{
id: "WD008",
amount: 90.0,
status: "paid",
applyDate: "2025-12-03 10:10",
reviewDate: "2025-12-03 14:30",
paidDate: "2025-12-03 16:00",
remark: "已到账",
},
];
// 筛选过滤
let filteredList = mockRecords;
if (params.status && params.status !== "all") {
filteredList = filteredList.filter(item => item.status === params.status);
}
// 如果有日期参数,添加到请求参数中
if (params.date) {
filteredList = filteredList.filter(item =>
item.applyDate.startsWith(params.date!),
);
requestParams.date = params.date;
}
const page = params.page || 1;
const limit = params.limit || 10;
const start = (page - 1) * limit;
const end = start + limit;
const paginatedList = filteredList.slice(start, end);
const response = await request(
"/v1/frontend/distribution/user/withdrawal-records",
requestParams,
"GET",
) as {
list: Array<{
id: string;
amount: number;
status: "pending" | "approved" | "rejected" | "paid";
statusLabel: string;
payType: string;
payTypeLabel: string;
applyTime: string;
reviewTime: string | null;
reviewer: string | null;
remark: string | null;
}>;
total: number;
page: number;
limit: number;
};
// 将接口返回的数据转换为 WithdrawalDetailRecord 格式
const list: WithdrawalDetailRecord[] = response.list.map(item => ({
id: item.id,
amount: item.amount, // 金额是分
status: item.status,
applyDate: item.applyTime,
reviewDate: item.reviewTime || undefined,
paidDate: undefined, // 接口没有单独的 paidDate 字段
remark: item.remark || undefined,
reviewer: item.reviewer || undefined,
}));
return {
list: paginatedList,
total: filteredList.length,
page,
list,
total: response.total,
page: response.page,
};
};

View File

@@ -35,6 +35,7 @@ export interface RevenueRecord {
typeLabel: string;
amount: number;
date: string;
remark?: string; // 备注
}
// 提现明细状态
@@ -49,4 +50,30 @@ export interface WithdrawalDetailRecord {
reviewDate?: string;
paidDate?: string;
remark?: string;
reviewer?: string; // 审核人
}
// 渠道前端首页数据
export interface ChannelHomeData {
channelInfo: {
channelName: string;
channelCode: string;
phone?: string;
wechatId?: string;
remark?: string;
createTime: string;
createType: "manual" | "auto";
};
financialStats: {
withdrawableAmount: number; // 可提现金额
totalRevenue: number; // 总收益
pendingReview: number; // 待审核
withdrawn: number; // 已提现
};
customerStats: {
totalFriends: number; // 总加好友数
todayFriends: number; // 今日加好友数
totalCustomers: number; // 总获客数
todayCustomers: number; // 今日获客数
};
}

View File

@@ -19,6 +19,7 @@ import {
import {
fetchChannelDetail,
fetchChannelStatistics,
fetchChannelHomeData,
fetchRevenueList,
fetchWithdrawalDetailList,
} from "./api";
@@ -92,7 +93,7 @@ const ChannelDetailPage: React.FC = () => {
revenueDate,
revenuePage,
withdrawalDetailStatus,
withdrawalDetailDate,
withdrawalDetailDate, // 恢复日期筛选依赖
withdrawalDetailPage,
]);
@@ -100,12 +101,33 @@ const ChannelDetailPage: React.FC = () => {
if (!id) return;
setLoading(true);
try {
const [detail, stats] = await Promise.all([
fetchChannelDetail(id),
fetchChannelStatistics(id),
]);
setChannelDetail(detail);
setStatistics(stats);
// 路由参数 id 实际上就是 channelCode对应列表的 code
// 使用同一个接口同时获取详情和统计数据,只调用一次接口
const homeData = await fetchChannelHomeData(id);
// 设置渠道详情
setChannelDetail({
id: homeData.channelInfo.channelCode,
name: homeData.channelInfo.channelName,
code: homeData.channelInfo.channelCode,
phone: homeData.channelInfo.phone,
wechatId: homeData.channelInfo.wechatId,
createType: homeData.channelInfo.createType,
remark: homeData.channelInfo.remark,
createTime: homeData.channelInfo.createTime,
});
// 设置统计数据
setStatistics({
totalFriends: homeData.customerStats.totalFriends,
todayFriends: homeData.customerStats.todayFriends,
totalCustomers: homeData.customerStats.totalCustomers,
todayCustomers: homeData.customerStats.todayCustomers,
totalRevenue: homeData.financialStats.totalRevenue,
pendingWithdrawal: homeData.financialStats.withdrawableAmount,
pendingReview: homeData.financialStats.pendingReview,
withdrawn: homeData.financialStats.withdrawn,
});
} catch (e) {
// 处理错误
} finally {
@@ -118,7 +140,7 @@ const ChannelDetailPage: React.FC = () => {
setRevenueLoading(true);
try {
const res = await fetchRevenueList({
channelId: id,
channelCode: id, // 使用 channelCode路由参数 id 就是 channelCode
page: revenuePage,
limit: 10,
type: revenueType,
@@ -142,10 +164,11 @@ const ChannelDetailPage: React.FC = () => {
setWithdrawalDetailLoading(true);
try {
const res = await fetchWithdrawalDetailList({
channelId: id,
channelCode: id, // 使用 channelCode路由参数 id 就是 channelCode
page: withdrawalDetailPage,
limit: 10,
status: withdrawalDetailStatus,
payType: "all", // 暂时默认 all如果后续需要添加 payType 筛选,可以添加状态
date: withdrawalDetailDate
? `${withdrawalDetailDate.getFullYear()}-${String(
withdrawalDetailDate.getMonth() + 1,

View File

@@ -659,7 +659,7 @@ const DistributionManagement: React.FC = () => {
key={channel.id}
className={styles.channelCard}
onClick={() =>
navigate(`/workspace/distribution-management/${channel.id}`)
navigate(`/workspace/distribution-management/${channel.code}`)
}
style={{ cursor: "pointer" }}
>

View File

@@ -62,7 +62,7 @@ class ChannelUserController extends Controller
// 获取参数
$phone = $this->request->param('phone', '');
$password = $this->request->param('password', '');
$companyId = $this->request->param('companyId', 0);
// 参数验证
if (empty($phone)) {
return $this->setCorsHeaders(json([
@@ -86,10 +86,12 @@ class ChannelUserController extends Controller
$channel = Db::name('distribution_channel')
->where([
['phone', '=', $phone],
['companyId', '=', $companyId],
['deleteTime', '=', 0]
])
->find();
if (!$channel) {
return $this->setCorsHeaders(json([
'code' => 404,
@@ -224,11 +226,16 @@ class ChannelUserController extends Controller
$channelInfo = [
'channelName' => $channel['name'] ?? '',
'channelCode' => $channel['code'] ?? '',
'phone' => $channel['phone'] ?? '',
'wechatId' => $channel['wechatId'] ?? '',
'remark' => $channel['remark'] ?? '',
'createTime' => !empty($channel['createTime']) ? date('Y-m-d H:i:s', $channel['createTime']) : '',
'createType' => $channel['createType'] ?? '',
];
// 2. 财务统计
// 当前可提现金额
$withdrawableAmount = intval($channel['withdrawableAmount'] ?? 0);
$withdrawableAmount = $channel['withdrawableAmount'] ?? 0;
// 已提现金额(已打款的提现申请)
$withdrawnAmount = Db::name('distribution_withdrawal')
@@ -238,7 +245,7 @@ class ChannelUserController extends Controller
['status', '=', DistributionWithdrawal::STATUS_PAID]
])
->sum('amount');
$withdrawnAmount = intval($withdrawnAmount ?? 0);
$withdrawnAmount = $withdrawnAmount ?? 0;
// 待审核金额(待审核的提现申请)
$pendingReviewAmount = Db::name('distribution_withdrawal')
@@ -248,7 +255,7 @@ class ChannelUserController extends Controller
['status', '=', DistributionWithdrawal::STATUS_PENDING]
])
->sum('amount');
$pendingReviewAmount = intval($pendingReviewAmount ?? 0);
$pendingReviewAmount = $pendingReviewAmount ?? 0;
// 总收益(所有收益记录的总和)
$totalRevenue = Db::name('distribution_revenue_record')
@@ -257,13 +264,13 @@ class ChannelUserController extends Controller
['channelId', '=', $channelId]
])
->sum('amount');
$totalRevenue = intval($totalRevenue ?? 0);
$totalRevenue = $totalRevenue ?? 0;
$financialStats = [
'withdrawableAmount' => round($withdrawableAmount / 100, 2), // 当前可提现金额(元)
'totalRevenue' => round($totalRevenue / 100, 2), // 总收益(元)
'pendingReview' => round($pendingReviewAmount / 100, 2), // 待审核(元)
'withdrawn' => round($withdrawnAmount / 100, 2), // 已提现(元)
'withdrawableAmount' => $withdrawableAmount, // 当前可提现金额
'totalRevenue' => $totalRevenue, // 总收益
'pendingReview' => $pendingReviewAmount, // 待审核
'withdrawn' => $withdrawnAmount, // 已提现
];
// 3. 客户和好友统计