feat(小宝预警): 增加权限与数据键
This commit is contained in:
@@ -9,6 +9,8 @@ export const APP_DATA_KEYS = [
|
||||
'task-categories',
|
||||
'task-worklogs',
|
||||
'work-activities',
|
||||
'xiaobao-risk-insights',
|
||||
'xiaobao-risk-snapshots',
|
||||
'overtime',
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -43,6 +43,15 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||
{ action: 'export', label: '导出', permission: 'overtime:export' },
|
||||
],
|
||||
},
|
||||
{
|
||||
module: 'xiaobao.warning',
|
||||
moduleLabel: '小宝预警',
|
||||
category: 'main',
|
||||
actions: [
|
||||
{ action: 'view', label: '查看', permission: 'xiaobao.warning:view' },
|
||||
{ action: 'manage', label: '管理', permission: 'xiaobao.warning:manage' },
|
||||
],
|
||||
},
|
||||
{ module: 'member', moduleLabel: '成员', category: 'main', actions: std4('member') },
|
||||
{ module: 'role', moduleLabel: '角色', category: 'main', actions: std4('role') },
|
||||
{ module: 'version.req', moduleLabel: '需求 Tab', category: 'version_tab', actions: stdTab('version.req') },
|
||||
@@ -67,6 +76,7 @@ export const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]> = {
|
||||
...std4('requirement').map((a) => a.permission),
|
||||
'version.req:view', 'version.req:manage',
|
||||
'version.product_plan:view', 'version.product_plan:manage',
|
||||
'xiaobao.warning:view', 'xiaobao.warning:manage',
|
||||
'overtime:view', 'member:view', 'role:view',
|
||||
'version.research:view', 'version.ui_plan:view', 'version.devtask:view',
|
||||
'version.testcase:view', 'version.bug:view',
|
||||
@@ -76,6 +86,7 @@ export const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]> = {
|
||||
'version.devtask:view', 'version.devtask:manage',
|
||||
'version.bug:view', 'version.bug:edit',
|
||||
'version.research:view', 'version.product_plan:view', 'version.ui_plan:view', 'version.testcase:view',
|
||||
'xiaobao.warning:view',
|
||||
'overtime:view', 'overtime:create',
|
||||
],
|
||||
'role-test': [
|
||||
@@ -83,6 +94,7 @@ export const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]> = {
|
||||
'version.testcase:view', 'version.testcase:manage',
|
||||
'version.bug:view', 'version.bug:create', 'version.bug:edit', 'version.bug:delete',
|
||||
'version.research:view', 'version.product_plan:view', 'version.ui_plan:view', 'version.devtask:view',
|
||||
'xiaobao.warning:view',
|
||||
'overtime:view', 'overtime:create',
|
||||
],
|
||||
'role-design': [
|
||||
@@ -90,6 +102,7 @@ export const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]> = {
|
||||
'version.ui_plan:view', 'version.ui_plan:manage',
|
||||
'version.research:view', 'version.product_plan:view', 'version.devtask:view',
|
||||
'version.testcase:view', 'version.bug:view',
|
||||
'xiaobao.warning:view',
|
||||
'overtime:view', 'overtime:create',
|
||||
],
|
||||
};
|
||||
|
||||
38
apps/web/lib/role-permission-migration.test.ts
Normal file
38
apps/web/lib/role-permission-migration.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import type { RoleItem } from './members';
|
||||
import { mergePresetRolePermissions } from './role-permission-migration';
|
||||
|
||||
test('mergePresetRolePermissions adds new preset permissions to existing product manager role', () => {
|
||||
const roles: RoleItem[] = [
|
||||
{
|
||||
id: 'role-pm',
|
||||
name: '产品经理',
|
||||
createdAt: '2024-01-01',
|
||||
permissions: ['product:view'],
|
||||
},
|
||||
];
|
||||
|
||||
const result = mergePresetRolePermissions(roles);
|
||||
|
||||
assert.equal(result.changed, true);
|
||||
assert.ok(result.roles[0].permissions.includes('product:view'));
|
||||
assert.ok(result.roles[0].permissions.includes('xiaobao.warning:view'));
|
||||
assert.ok(result.roles[0].permissions.includes('xiaobao.warning:manage'));
|
||||
});
|
||||
|
||||
test('mergePresetRolePermissions keeps custom role permissions unchanged', () => {
|
||||
const roles: RoleItem[] = [
|
||||
{
|
||||
id: 'role-custom',
|
||||
name: '自定义角色',
|
||||
createdAt: '2024-01-01',
|
||||
permissions: ['version:view'],
|
||||
},
|
||||
];
|
||||
|
||||
const result = mergePresetRolePermissions(roles);
|
||||
|
||||
assert.equal(result.changed, false);
|
||||
assert.deepEqual(result.roles[0].permissions, ['version:view']);
|
||||
});
|
||||
15
apps/web/lib/role-permission-migration.ts
Normal file
15
apps/web/lib/role-permission-migration.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { RoleItem } from './members';
|
||||
import { DEFAULT_ROLE_PERMISSIONS } from './permissions';
|
||||
|
||||
export function mergePresetRolePermissions(roles: RoleItem[]): { roles: RoleItem[]; changed: boolean } {
|
||||
let changed = false;
|
||||
const next = roles.map((role) => {
|
||||
const defaults = DEFAULT_ROLE_PERMISSIONS[role.id];
|
||||
if (!defaults || role.permissions.includes('*')) return role;
|
||||
const merged = Array.from(new Set([...role.permissions, ...defaults]));
|
||||
if (merged.length === role.permissions.length) return role;
|
||||
changed = true;
|
||||
return { ...role, permissions: merged };
|
||||
});
|
||||
return { roles: next, changed };
|
||||
}
|
||||
@@ -11,6 +11,8 @@ export type ServerDataKey =
|
||||
| 'task-categories'
|
||||
| 'task-worklogs'
|
||||
| 'work-activities'
|
||||
| 'xiaobao-risk-insights'
|
||||
| 'xiaobao-risk-snapshots'
|
||||
| 'overtime';
|
||||
|
||||
interface ServerDataResponse<T> {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
sanitizeSystemAdminPatch,
|
||||
} from '@/lib/member-system';
|
||||
import { DEFAULT_ROLE_PERMISSIONS } from '@/lib/permissions';
|
||||
import { mergePresetRolePermissions } from '@/lib/role-permission-migration';
|
||||
import { loadServerData, saveServerData } from '@/lib/server-data';
|
||||
|
||||
const PRESET_DEPARTMENTS: Department[] = [
|
||||
@@ -81,13 +82,16 @@ export const useMemberStore = create<MemberState>((set, get) => ({
|
||||
}
|
||||
return r;
|
||||
});
|
||||
const permissionMerged = mergePresetRolePermissions(migratedRoles);
|
||||
const rolesWithNewDefaults = permissionMerged.roles;
|
||||
const adminRole = PRESET_ROLES.find((r) => r.id === SYSTEM_ADMIN_ROLE_ID)!;
|
||||
const roles = migratedRoles.some((r) => r.id === SYSTEM_ADMIN_ROLE_ID)
|
||||
? migratedRoles.map((r) => r.id === SYSTEM_ADMIN_ROLE_ID ? { ...adminRole, ...r, isSystem: true, permissions: DEFAULT_ROLE_PERMISSIONS[SYSTEM_ADMIN_ROLE_ID] } : r)
|
||||
: [adminRole, ...migratedRoles];
|
||||
const roles = rolesWithNewDefaults.some((r) => r.id === SYSTEM_ADMIN_ROLE_ID)
|
||||
? rolesWithNewDefaults.map((r) => r.id === SYSTEM_ADMIN_ROLE_ID ? { ...adminRole, ...r, isSystem: true, permissions: DEFAULT_ROLE_PERMISSIONS[SYSTEM_ADMIN_ROLE_ID] } : r)
|
||||
: [adminRole, ...rolesWithNewDefaults];
|
||||
const rolesChanged =
|
||||
roles.length !== migratedRoles.length ||
|
||||
roles.some((role, index) => JSON.stringify(role) !== JSON.stringify(migratedRoles[index]));
|
||||
permissionMerged.changed ||
|
||||
roles.length !== rolesWithNewDefaults.length ||
|
||||
roles.some((role, index) => JSON.stringify(role) !== JSON.stringify(rolesWithNewDefaults[index]));
|
||||
const ensured = ensureSystemAdminMember(cached.members);
|
||||
const next = {
|
||||
departments: cached.departments,
|
||||
|
||||
Reference in New Issue
Block a user