feat(mining-admin): 添加用户详情页缺失的 API 端点
- 添加 referral-tree API(返回空推荐关系数据) - 添加 planting-ledger API(返回空认种数据) - 添加 wallet-ledger API(返回空钱包流水数据) - 修复前端 referral-tree 组件空数据处理 注:这些 API 目前返回占位数据,完整数据需要通过 CDC 从各服务同步 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
fc3efe6a27
commit
1a7c73e531
|
|
@ -102,4 +102,43 @@ export class UsersController {
|
||||||
status,
|
status,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get(':accountSequence/referral-tree')
|
||||||
|
@ApiOperation({ summary: '获取用户引荐关系树' })
|
||||||
|
@ApiParam({ name: 'accountSequence', type: String })
|
||||||
|
@ApiQuery({ name: 'direction', required: false, type: String, description: 'up, down, both' })
|
||||||
|
@ApiQuery({ name: 'depth', required: false, type: Number })
|
||||||
|
async getReferralTree(
|
||||||
|
@Param('accountSequence') accountSequence: string,
|
||||||
|
@Query('direction') direction?: string,
|
||||||
|
@Query('depth') depth?: number,
|
||||||
|
) {
|
||||||
|
return this.usersService.getReferralTree(accountSequence, direction || 'both', depth || 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':accountSequence/planting-ledger')
|
||||||
|
@ApiOperation({ summary: '获取用户认种分类账' })
|
||||||
|
@ApiParam({ name: 'accountSequence', type: String })
|
||||||
|
@ApiQuery({ name: 'page', required: false, type: Number })
|
||||||
|
@ApiQuery({ name: 'pageSize', required: false, type: Number })
|
||||||
|
async getPlantingLedger(
|
||||||
|
@Param('accountSequence') accountSequence: string,
|
||||||
|
@Query('page') page?: number,
|
||||||
|
@Query('pageSize') pageSize?: number,
|
||||||
|
) {
|
||||||
|
return this.usersService.getPlantingLedger(accountSequence, page ?? 1, pageSize ?? 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':accountSequence/wallet-ledger')
|
||||||
|
@ApiOperation({ summary: '获取用户钱包流水' })
|
||||||
|
@ApiParam({ name: 'accountSequence', type: String })
|
||||||
|
@ApiQuery({ name: 'page', required: false, type: Number })
|
||||||
|
@ApiQuery({ name: 'pageSize', required: false, type: Number })
|
||||||
|
async getWalletLedger(
|
||||||
|
@Param('accountSequence') accountSequence: string,
|
||||||
|
@Query('page') page?: number,
|
||||||
|
@Query('pageSize') pageSize?: number,
|
||||||
|
) {
|
||||||
|
return this.usersService.getWalletLedger(accountSequence, page ?? 1, pageSize ?? 20);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,99 @@ export class UsersService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户引荐关系树
|
||||||
|
* TODO: 从 identity-service 同步推荐关系数据
|
||||||
|
*/
|
||||||
|
async getReferralTree(accountSequence: string, direction: string, depth: number) {
|
||||||
|
const user = await this.prisma.syncedUser.findUnique({
|
||||||
|
where: { accountSequence },
|
||||||
|
include: { contributionAccount: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundException(`用户 ${accountSequence} 不存在`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回基础结构,数据需要从 identity-service 同步
|
||||||
|
return {
|
||||||
|
currentUser: {
|
||||||
|
accountSequence: user.accountSequence,
|
||||||
|
nickname: user.realName || null,
|
||||||
|
avatar: null,
|
||||||
|
personalAdoptions: 0,
|
||||||
|
teamAdoptions: 0,
|
||||||
|
directReferralCount: user.contributionAccount?.directReferralCount || 0,
|
||||||
|
},
|
||||||
|
ancestors: [],
|
||||||
|
directReferrals: [],
|
||||||
|
note: '推荐关系数据需要从 identity-service 同步',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户认种分类账
|
||||||
|
* TODO: 从 adoption-service 同步认种数据
|
||||||
|
*/
|
||||||
|
async getPlantingLedger(accountSequence: string, page: number, pageSize: number) {
|
||||||
|
const user = await this.prisma.syncedUser.findUnique({
|
||||||
|
where: { accountSequence },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundException(`用户 ${accountSequence} 不存在`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回空数据,数据需要从 adoption-service 同步
|
||||||
|
return {
|
||||||
|
summary: {
|
||||||
|
totalOrders: 0,
|
||||||
|
totalTreeCount: 0,
|
||||||
|
totalAmount: '0',
|
||||||
|
effectiveTreeCount: 0,
|
||||||
|
firstPlantingAt: null,
|
||||||
|
lastPlantingAt: null,
|
||||||
|
},
|
||||||
|
items: [],
|
||||||
|
total: 0,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
totalPages: 0,
|
||||||
|
note: '认种数据需要从 adoption-service 同步',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户钱包流水
|
||||||
|
* TODO: 从 mining-service 同步钱包流水数据
|
||||||
|
*/
|
||||||
|
async getWalletLedger(accountSequence: string, page: number, pageSize: number) {
|
||||||
|
const user = await this.prisma.syncedUser.findUnique({
|
||||||
|
where: { accountSequence },
|
||||||
|
include: { miningAccount: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundException(`用户 ${accountSequence} 不存在`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mining = user.miningAccount;
|
||||||
|
|
||||||
|
return {
|
||||||
|
summary: {
|
||||||
|
availableBalance: mining?.availableBalance?.toString() || '0',
|
||||||
|
frozenBalance: mining?.frozenBalance?.toString() || '0',
|
||||||
|
totalMined: mining?.totalMined?.toString() || '0',
|
||||||
|
},
|
||||||
|
items: [],
|
||||||
|
total: 0,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
totalPages: 0,
|
||||||
|
note: '钱包流水数据需要从 mining-service 同步',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// 辅助方法
|
// 辅助方法
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export function ReferralTree({ accountSequence }: ReferralTreeProps) {
|
||||||
|
|
||||||
// 当 referralTree 数据加载完成后,自动展开当前用户的直推下级
|
// 当 referralTree 数据加载完成后,自动展开当前用户的直推下级
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (referralTree && referralTree.directReferrals.length > 0) {
|
if (referralTree?.currentUser && referralTree.directReferrals?.length > 0) {
|
||||||
setExpandedNodes((prev) => ({
|
setExpandedNodes((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[referralTree.currentUser.accountSequence]: referralTree.directReferrals,
|
[referralTree.currentUser.accountSequence]: referralTree.directReferrals,
|
||||||
|
|
@ -112,16 +112,16 @@ export function ReferralTree({ accountSequence }: ReferralTreeProps) {
|
||||||
{/* 向上的引荐人链 */}
|
{/* 向上的引荐人链 */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p className="text-sm font-medium text-muted-foreground">引荐人链 (向上)</p>
|
<p className="text-sm font-medium text-muted-foreground">引荐人链 (向上)</p>
|
||||||
{referralTree.ancestors.length > 0 ? (
|
{(referralTree.ancestors?.length ?? 0) > 0 ? (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{referralTree.ancestors.map((ancestor, index) => (
|
{(referralTree.ancestors || []).map((ancestor, index) => (
|
||||||
<div key={ancestor.accountSequence}>
|
<div key={ancestor.accountSequence}>
|
||||||
<ReferralNodeCard
|
<ReferralNodeCard
|
||||||
node={ancestor}
|
node={ancestor}
|
||||||
onClick={() => handleTreeNodeClick(ancestor)}
|
onClick={() => handleTreeNodeClick(ancestor)}
|
||||||
variant="ancestor"
|
variant="ancestor"
|
||||||
/>
|
/>
|
||||||
{index < referralTree.ancestors.length - 1 && (
|
{index < (referralTree.ancestors?.length ?? 0) - 1 && (
|
||||||
<div className="flex justify-center py-1">
|
<div className="flex justify-center py-1">
|
||||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -154,13 +154,13 @@ export function ReferralTree({ accountSequence }: ReferralTreeProps) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 直推下级列表 */}
|
{/* 直推下级列表 */}
|
||||||
{referralTree.directReferrals.length > 0 && (
|
{(referralTree.directReferrals?.length ?? 0) > 0 && (
|
||||||
<div className="space-y-2 ml-6 border-l-2 border-muted pl-4">
|
<div className="space-y-2 ml-6 border-l-2 border-muted pl-4">
|
||||||
<p className="text-sm font-medium text-muted-foreground">
|
<p className="text-sm font-medium text-muted-foreground">
|
||||||
直推下级 ({referralTree.directReferrals.length})
|
直推下级 ({referralTree.directReferrals?.length ?? 0})
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{referralTree.directReferrals.map((child) => (
|
{(referralTree.directReferrals || []).map((child) => (
|
||||||
<ReferralNodeCard
|
<ReferralNodeCard
|
||||||
key={child.accountSequence}
|
key={child.accountSequence}
|
||||||
node={child}
|
node={child}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue