完成五人团队正式内用最小收口(07-18-small-team-internal-readiness)

这个提交包含在:
2026-07-18 15:46:14 +08:00
父节点 58c8decef9
当前提交 5d0a5f9a38
共修改 134 个文件,包含 10790 行新增和 1413 行删除
+130 -3
查看文件
@@ -9,12 +9,18 @@ function envelope(data: unknown) {
function mockCommonAuth(page: import("@playwright/test").Page, options: {
mustChangePassword?: boolean;
meId?: string;
meName?: string;
meEmployeeNo?: string;
permissions?: string[];
roleCodes?: string[];
}) {
const mustChangePassword = options.mustChangePassword === true;
const meId = options.meId ?? "employee-admin-1";
const meName = options.meName ?? "Admin";
const meEmployeeNo = options.meEmployeeNo ?? "HYD000001";
const permissions = options.permissions ?? ["system.users", "system.accounts.manage", "system.roles.manage"];
const roleCodes = options.roleCodes ?? ["admin"];
return Promise.all([
page.route("**/api/auth/sso/providers", async (route) => {
@@ -64,7 +70,7 @@ function mockCommonAuth(page: import("@playwright/test").Page, options: {
contentType: "application/json",
body: JSON.stringify(
envelope({
id: "employee-admin-1",
id: meId,
employeeNo: meEmployeeNo,
name: meName,
phone: "13800000000",
@@ -73,8 +79,8 @@ function mockCommonAuth(page: import("@playwright/test").Page, options: {
directManagerId: null,
status: "active",
notificationPushEnabled: true,
roleCodes: ["admin"],
permissions: ["system.users"],
roleCodes,
permissions,
profileCompleteness: {
hasEmergencyContact: true,
hasBankAccount: true,
@@ -135,6 +141,73 @@ function mockCommonAuth(page: import("@playwright/test").Page, options: {
]);
}
type MockUser = {
id: string;
employeeNo: string;
name: string;
phone: string;
email: string;
status: "active" | "archived";
departmentId: string;
title: string;
roleCodes: string[];
createdAt: string;
updatedAt: string;
};
function buildMockUser(id: string, name: string, roleCodes: string[]): MockUser {
return {
id,
employeeNo: `MOCK-${id}`,
name,
phone: "00000000000",
email: `${id}@example.test`,
status: "active",
departmentId: "dept-1",
title: "员工",
roleCodes,
createdAt: "2026-07-18T00:00:00.000Z",
updatedAt: "2026-07-18T00:00:00.000Z",
};
}
async function mockUserManagementData(
page: import("@playwright/test").Page,
items: MockUser[],
) {
await Promise.all([
page.route((url) => url.pathname === "/api/users", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(envelope({ page: 1, pageSize: 20, total: items.length, items })),
});
}),
page.route((url) => url.pathname === "/api/departments", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(envelope({ includeArchived: false, items: [], tree: [] })),
});
}),
page.route((url) => url.pathname === "/api/employees", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(envelope({ page: 1, pageSize: 200, total: 0, items: [] })),
});
}),
]);
}
async function loginToUserManagement(page: import("@playwright/test").Page) {
await page.goto("/login?redirect=%2Fsystem%2Fusers");
await page.getByTestId("login-identifier").fill("[email protected]");
await page.getByTestId("login-password").fill("SyntheticPassword1234");
await page.getByTestId("login-submit").click();
await expect(page).toHaveURL(/\/system\/users/);
}
test("E2E 用户开通:新建用户时由系统生成工号和一次性口令", async ({ page }) => {
let createPayload: Record<string, unknown> | null = null;
@@ -234,6 +307,60 @@ test("E2E 用户开通:新建用户时由系统生成工号和一次性口令"
await expect(page.getByTestId("users-reset-result")).toContainText("ResetA1abc123def456");
});
test("E2E 账号保护:OA 系统管理员只能操作普通账号", async ({ page }) => {
const currentId = "system-admin-current";
const ordinary = buildMockUser("ordinary-user", "普通员工", ["employee"]);
const protectedAdmin = buildMockUser("protected-admin", "应急账号甲", ["employee", "admin"]);
const protectedSystemAdmin = buildMockUser("protected-system-admin", "运维账号乙", ["employee", "system_admin"]);
const currentSystemAdmin = buildMockUser(currentId, "当前运维账号", ["employee", "system_admin"]);
await mockCommonAuth(page, {
meId: currentId,
meName: "当前运维账号",
roleCodes: ["system_admin"],
permissions: ["system.accounts.manage", "system.roles.manage"],
});
await mockUserManagementData(page, [ordinary, protectedAdmin, protectedSystemAdmin, currentSystemAdmin]);
await loginToUserManagement(page);
const ordinaryRow = page.getByTestId("users-row").filter({ hasText: ordinary.name });
await expect(ordinaryRow.getByTestId("users-toggle")).toHaveCount(1);
await expect(ordinaryRow.getByTestId("users-reset-password")).toHaveCount(1);
await expect(ordinaryRow.getByTestId("users-edit-roles")).toHaveCount(1);
for (const user of [protectedAdmin, protectedSystemAdmin, currentSystemAdmin]) {
const row = page.getByTestId("users-row").filter({ hasText: user.name });
await expect(row.getByTestId("users-toggle")).toHaveCount(0);
await expect(row.getByTestId("users-reset-password")).toHaveCount(0);
await expect(row.getByTestId("users-edit-roles")).toHaveCount(0);
await expect(row.getByTestId("users-protected-account")).toHaveText("受保护账号");
}
});
test("E2E 账号保护:应急超级管理员可操作其他受保护账号但不能停用或改自己的角色", async ({ page }) => {
const currentId = "employee-admin-1";
const otherAdmin = buildMockUser("other-admin", "其他应急账号", ["employee", "admin"]);
const otherSystemAdmin = buildMockUser("other-system-admin", "其他运维账号", ["employee", "system_admin"]);
const currentAdmin = buildMockUser(currentId, "当前应急账号", ["employee", "admin"]);
await mockCommonAuth(page, { meId: currentId, meName: currentAdmin.name });
await mockUserManagementData(page, [otherAdmin, otherSystemAdmin, currentAdmin]);
await loginToUserManagement(page);
for (const user of [otherAdmin, otherSystemAdmin]) {
const row = page.getByTestId("users-row").filter({ hasText: user.name });
await expect(row.getByTestId("users-toggle")).toHaveCount(1);
await expect(row.getByTestId("users-reset-password")).toHaveCount(1);
await expect(row.getByTestId("users-edit-roles")).toHaveCount(1);
await expect(row.getByTestId("users-protected-account")).toHaveCount(0);
}
const currentRow = page.getByTestId("users-row").filter({ hasText: currentAdmin.name });
await expect(currentRow.getByTestId("users-toggle")).toHaveCount(0);
await expect(currentRow.getByTestId("users-edit-roles")).toHaveCount(0);
await expect(currentRow.getByTestId("users-reset-password")).toHaveCount(1);
});
test("E2E 首次登录:一次性口令登录后强制改密", async ({ page }) => {
let changePasswordPayload: Record<string, unknown> | null = null;