forked from bai/curriculum-project-hub
Compare commits
2 Commits
ef02428bb6
...
2395671693
| Author | SHA1 | Date | |
|---|---|---|---|
| 2395671693 | |||
| cc4d9d907c |
@@ -60,6 +60,7 @@
|
|||||||
(g) =>
|
(g) =>
|
||||||
g.principalId.toLowerCase().includes(q) ||
|
g.principalId.toLowerCase().includes(q) ||
|
||||||
(g.principalName ?? "").toLowerCase().includes(q) ||
|
(g.principalName ?? "").toLowerCase().includes(q) ||
|
||||||
|
(g.principalOpenId ?? "").toLowerCase().includes(q) ||
|
||||||
(g.principalType === "USER" ? "个人" : "group").includes(q),
|
(g.principalType === "USER" ? "个人" : "group").includes(q),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -241,12 +242,12 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<table class="list">
|
<table class="list">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>成员</th><th>类型</th><th>权限</th><th>加入时间</th><th></th></tr>
|
<tr><th>成员</th><th>userId</th><th>飞书 ID</th><th>类型</th><th>权限</th><th>加入时间</th><th></th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#if shown.length === 0}
|
{#if shown.length === 0}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="quiet !py-[18px] text-center">
|
<td colspan="7" class="quiet !py-[18px] text-center">
|
||||||
{searchText.trim() === "" ? "暂无授权" : `无匹配「${searchText.trim()}」的授权`}
|
{searchText.trim() === "" ? "暂无授权" : `无匹配「${searchText.trim()}」的授权`}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -256,15 +257,14 @@
|
|||||||
<td>
|
<td>
|
||||||
<a class="inline-flex items-center gap-2 text-ink hover:text-accent" href={principalHref(g)}>
|
<a class="inline-flex items-center gap-2 text-ink hover:text-accent" href={principalHref(g)}>
|
||||||
<Avatar displayName={g.principalName} userId={g.principalId} size={26} />
|
<Avatar displayName={g.principalName} userId={g.principalId} size={26} />
|
||||||
<span>
|
|
||||||
<span class="flex items-center gap-1.5 text-[13px]">
|
<span class="flex items-center gap-1.5 text-[13px]">
|
||||||
{g.principalName ?? g.principalId}
|
{g.principalName ?? g.principalId}
|
||||||
{#if g.isCreatorGrant}<span class="quiet">(创建者)</span>{/if}
|
{#if g.isCreatorGrant}<span class="quiet">(创建者)</span>{/if}
|
||||||
</span>
|
</span>
|
||||||
<span class="block font-mono text-[11px] text-ink-3">{g.principalId}</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="file-meta font-mono">{g.principalType === "USER" ? g.principalId : "—"}</td>
|
||||||
|
<td class="file-meta font-mono">{g.principalOpenId ?? "—"}</td>
|
||||||
<td><span class="tag">{g.principalType === "USER" ? "个人" : "Group"}</span></td>
|
<td><span class="tag">{g.principalType === "USER" ? "个人" : "Group"}</span></td>
|
||||||
<td>
|
<td>
|
||||||
<!-- 创建者授权不可动(契约 8.1);非 MANAGE 持有者只读。 -->
|
<!-- 创建者授权不可动(契约 8.1);非 MANAGE 持有者只读。 -->
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ export interface Grant {
|
|||||||
readonly principalId: string;
|
readonly principalId: string;
|
||||||
/** 主体显示名(用户 displayName / 组 name);主体已删为 null,展示回落 principalId。 */
|
/** 主体显示名(用户 displayName / 组 name);主体已删为 null,展示回落 principalId。 */
|
||||||
readonly principalName: string | null;
|
readonly principalName: string | null;
|
||||||
|
/** USER 主体的飞书 openId;GROUP 或主体已删为 null。 */
|
||||||
|
readonly principalOpenId: string | null;
|
||||||
readonly role: Role;
|
readonly role: Role;
|
||||||
/** 创建者授权不可收回、不可改(契约 8.1)。 */
|
/** 创建者授权不可收回、不可改(契约 8.1)。 */
|
||||||
readonly isCreatorGrant: boolean;
|
readonly isCreatorGrant: boolean;
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export interface GrantDto {
|
|||||||
readonly principalId: string;
|
readonly principalId: string;
|
||||||
/** 主体显示名(用户 displayName / 组 name);主体已删时为 null,前端回落 principalId。 */
|
/** 主体显示名(用户 displayName / 组 name);主体已删时为 null,前端回落 principalId。 */
|
||||||
readonly principalName: string | null;
|
readonly principalName: string | null;
|
||||||
|
/** USER 主体的飞书 openId;GROUP 或主体已删时为 null。 */
|
||||||
|
readonly principalOpenId: string | null;
|
||||||
readonly role: FileLibRole;
|
readonly role: FileLibRole;
|
||||||
readonly isCreatorGrant: boolean;
|
readonly isCreatorGrant: boolean;
|
||||||
readonly createdAt: Date;
|
readonly createdAt: Date;
|
||||||
@@ -38,13 +40,14 @@ function toDto(grant: FileLibGrant): GrantDto {
|
|||||||
principalType: grant.principalType,
|
principalType: grant.principalType,
|
||||||
principalId: grant.principalId,
|
principalId: grant.principalId,
|
||||||
principalName: null,
|
principalName: null,
|
||||||
|
principalOpenId: null,
|
||||||
role: grant.role,
|
role: grant.role,
|
||||||
isCreatorGrant: grant.isCreatorGrant,
|
isCreatorGrant: grant.isCreatorGrant,
|
||||||
createdAt: grant.createdAt,
|
createdAt: grant.createdAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 批量回填主体显示名(两次查询,不做 per-row 往返)。可在事务内调用。 */
|
/** 批量回填主体显示名与飞书 openId(两次查询,不做 per-row 往返)。可在事务内调用。 */
|
||||||
async function withPrincipalNames(
|
async function withPrincipalNames(
|
||||||
prisma: Pick<PrismaClient, "user" | "memberGroup">,
|
prisma: Pick<PrismaClient, "user" | "memberGroup">,
|
||||||
grants: readonly GrantDto[],
|
grants: readonly GrantDto[],
|
||||||
@@ -53,7 +56,10 @@ async function withPrincipalNames(
|
|||||||
const groupIds = [...new Set(grants.filter((g) => g.principalType === "GROUP").map((g) => g.principalId))];
|
const groupIds = [...new Set(grants.filter((g) => g.principalType === "GROUP").map((g) => g.principalId))];
|
||||||
const users = userIds.length === 0
|
const users = userIds.length === 0
|
||||||
? []
|
? []
|
||||||
: await prisma.user.findMany({ where: { id: { in: userIds } }, select: { id: true, displayName: true } });
|
: await prisma.user.findMany({
|
||||||
|
where: { id: { in: userIds } },
|
||||||
|
select: { id: true, displayName: true, feishuOpenId: true },
|
||||||
|
});
|
||||||
const groups = groupIds.length === 0
|
const groups = groupIds.length === 0
|
||||||
? []
|
? []
|
||||||
: await prisma.memberGroup.findMany({ where: { id: { in: groupIds } }, select: { id: true, name: true } });
|
: await prisma.memberGroup.findMany({ where: { id: { in: groupIds } }, select: { id: true, name: true } });
|
||||||
@@ -61,7 +67,12 @@ async function withPrincipalNames(
|
|||||||
...users.map((u) => [u.id, u.displayName] as const),
|
...users.map((u) => [u.id, u.displayName] as const),
|
||||||
...groups.map((g) => [g.id, g.name] as const),
|
...groups.map((g) => [g.id, g.name] as const),
|
||||||
]);
|
]);
|
||||||
return grants.map((g) => ({ ...g, principalName: nameById.get(g.principalId) ?? null }));
|
const openIdById = new Map<string, string>(users.map((u) => [u.id, u.feishuOpenId] as const));
|
||||||
|
return grants.map((g) => ({
|
||||||
|
...g,
|
||||||
|
principalName: nameById.get(g.principalId) ?? null,
|
||||||
|
principalOpenId: g.principalType === "USER" ? openIdById.get(g.principalId) ?? null : null,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tx = Prisma.TransactionClient;
|
type Tx = Prisma.TransactionClient;
|
||||||
|
|||||||
Reference in New Issue
Block a user