Keep project_role as member/viewer, send document_duty=reviewer for USER_B, and clear the seed 李主管 reviewer so the assigned reviewer is the one that syncs. Co-authored-by: Cursor <cursoragent@cursor.com>
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { ApiProjectMemberRecord, ApiProjectMemberRole } from '../api/projectMembers';
|
|
import { ProjectMember, ProjectMemberRole } from '../types/workbench';
|
|
import { memberRoleFromDocumentDuty } from './projectDocumentDuty';
|
|
|
|
/** UI 角色 → 服务端 project_role(禁止 author/reviewer/approver)。 */
|
|
export function toApiProjectRole(
|
|
role: ProjectMemberRole,
|
|
userId: number,
|
|
creatorId: number
|
|
): ApiProjectMemberRole {
|
|
if (role === 'creator' || role === 'owner' || (userId === creatorId && role !== 'viewer')) {
|
|
return 'owner';
|
|
}
|
|
if (role === 'viewer') {
|
|
return 'viewer';
|
|
}
|
|
return 'member';
|
|
}
|
|
|
|
/** 服务端 project_role → UI 角色。 */
|
|
export function fromApiProjectRole(
|
|
apiRole: string,
|
|
userId: number,
|
|
creatorId: number
|
|
): ProjectMemberRole {
|
|
const normalized = apiRole.trim().toLowerCase();
|
|
if (normalized === 'owner') {
|
|
return userId === creatorId ? 'creator' : 'owner';
|
|
}
|
|
if (normalized === 'viewer') {
|
|
return 'viewer';
|
|
}
|
|
return 'member';
|
|
}
|
|
|
|
export function mapApiMemberToProjectMember(
|
|
rec: ApiProjectMemberRecord,
|
|
creatorId: number
|
|
): ProjectMember {
|
|
return {
|
|
userId: rec.user_id,
|
|
userName: rec.name?.trim() || rec.username?.trim() || `用户 #${rec.user_id}`,
|
|
role: memberRoleFromDocumentDuty(
|
|
rec.document_duty,
|
|
fromApiProjectRole(rec.project_role, rec.user_id, creatorId),
|
|
),
|
|
status: rec.status === 'exited' ? 'exited' : 'active',
|
|
joinedAt: rec.joined_at || new Date().toLocaleString(),
|
|
exitedAt: rec.status === 'exited' ? rec.joined_at : undefined,
|
|
};
|
|
}
|
|
|
|
export function mapApiMembersToProjectMembers(
|
|
records: ApiProjectMemberRecord[],
|
|
creatorId: number
|
|
): ProjectMember[] {
|
|
return records.map((rec) => mapApiMemberToProjectMember(rec, creatorId));
|
|
}
|
|
|
|
/** 项目成员管理可选角色(与后端校验一致)。 */
|
|
export const ASSIGNABLE_PROJECT_MEMBER_ROLES: ProjectMemberRole[] = ['member', 'viewer'];
|