Files
AIclinicalresearch/backend/compare_schema_db.ts
HaHafeng 66255368b7 feat(admin): Add user management and upgrade to module permission system
Features - User Management (Phase 4.1):
- Database: Add user_modules table for fine-grained module permissions
- Database: Add 4 user permissions (view/create/edit/delete) to role_permissions
- Backend: UserService (780 lines) - CRUD with tenant isolation
- Backend: UserController + UserRoutes (648 lines) - 13 API endpoints
- Backend: Batch import users from Excel
- Frontend: UserListPage (412 lines) - list/filter/search/pagination
- Frontend: UserFormPage (341 lines) - create/edit with module config
- Frontend: UserDetailPage (393 lines) - details/tenant/module management
- Frontend: 3 modal components (592 lines) - import/assign/configure
- API: GET/POST/PUT/DELETE /api/admin/users/* endpoints

Architecture Upgrade - Module Permission System:
- Backend: Add getUserModules() method in auth.service
- Backend: Login API returns modules array in user object
- Frontend: AuthContext adds hasModule() method
- Frontend: Navigation filters modules based on user.modules
- Frontend: RouteGuard checks requiredModule instead of requiredVersion
- Frontend: Remove deprecated version-based permission system
- UX: Only show accessible modules in navigation (clean UI)
- UX: Smart redirect after login (avoid 403 for regular users)

Fixes:
- Fix UTF-8 encoding corruption in ~100 docs files
- Fix pageSize type conversion in userService (String to Number)
- Fix authUser undefined error in TopNavigation
- Fix login redirect logic with role-based access check
- Update Git commit guidelines v1.2 with UTF-8 safety rules

Database Changes:
- CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled)
- ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code)
- INSERT 4 permissions + role assignments
- UPDATE PUBLIC tenant with 8 module subscriptions

Technical:
- Backend: 5 new files (~2400 lines)
- Frontend: 10 new files (~2500 lines)
- Docs: 1 development record + 2 status updates + 1 guideline update
- Total: ~4900 lines of code

Status: User management 100% complete, module permission system operational
2026-01-16 13:42:10 +08:00

117 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('🔍 检查数据库中 Prisma 未管理的对象\n');
console.log('=' .repeat(70));
// 1. 获取所有数据库函数
console.log('\n📋 数据库函数 (Functions):');
const functions: any[] = await prisma.$queryRaw`
SELECT routine_schema, routine_name, routine_type
FROM information_schema.routines
WHERE routine_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY routine_schema, routine_name
`;
if (functions.length === 0) {
console.log(' 无自定义函数');
} else {
functions.forEach(f => console.log(` - ${f.routine_schema}.${f.routine_name} (${f.routine_type})`));
}
// 2. 获取所有索引(非主键、非外键)
console.log('\n📋 自定义索引 (Indexes):');
const indexes: any[] = await prisma.$queryRaw`
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
AND indexname NOT LIKE '%pkey%'
AND indexname NOT LIKE '%_fkey%'
ORDER BY schemaname, tablename, indexname
LIMIT 30
`;
console.log(`${indexes.length} 个索引 (显示前30个)`);
// 3. 获取所有序列
console.log('\n📋 序列 (Sequences):');
const sequences: any[] = await prisma.$queryRaw`
SELECT sequence_schema, sequence_name
FROM information_schema.sequences
WHERE sequence_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY sequence_schema, sequence_name
`;
sequences.forEach(s => console.log(` - ${s.sequence_schema}.${s.sequence_name}`));
// 4. 检查枚举类型
console.log('\n📋 枚举类型 (Enums):');
const enums: any[] = await prisma.$queryRaw`
SELECT n.nspname as schema, t.typname as enum_name,
string_agg(e.enumlabel, ', ' ORDER BY e.enumsortorder) as values
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
JOIN pg_namespace n ON t.typnamespace = n.oid
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
GROUP BY n.nspname, t.typname
ORDER BY n.nspname, t.typname
`;
enums.forEach(e => console.log(` - ${e.schema}.${e.enum_name}: [${e.values}]`));
// 5. 检查触发器
console.log('\n📋 触发器 (Triggers):');
const triggers: any[] = await prisma.$queryRaw`
SELECT trigger_schema, trigger_name, event_object_table
FROM information_schema.triggers
WHERE trigger_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY trigger_schema, trigger_name
`;
if (triggers.length === 0) {
console.log(' 无自定义触发器');
} else {
triggers.forEach(t => console.log(` - ${t.trigger_schema}.${t.trigger_name} on ${t.event_object_table}`));
}
// 6. 检查视图
console.log('\n📋 视图 (Views):');
const views: any[] = await prisma.$queryRaw`
SELECT table_schema, table_name
FROM information_schema.views
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY table_schema, table_name
`;
if (views.length === 0) {
console.log(' 无自定义视图');
} else {
views.forEach(v => console.log(` - ${v.table_schema}.${v.table_name}`));
}
// 7. 列出 Prisma 不管理的重要对象
console.log('\n\n⚠ 需要手动管理的数据库对象 (Prisma 不管理):');
console.log(' 1. platform_schema.create_queue() 函数');
console.log(' 2. platform_schema.delete_queue() 函数');
console.log(' 3. platform_schema.job_state 枚举 (pg-boss 创建)');
console.log(' 4. platform_schema.job_common 表 (pg-boss 运行时创建)');
console.log(' 5. 各种索引和约束');
console.log('\n' + '=' .repeat(70));
}
main()
.catch(console.error)
.finally(() => prisma.$disconnect());