feat(admin): Complete tenant management and module access control system

Major Features:
- Tenant management CRUD (list, create, edit, delete, module configuration)
- Dynamic module management system (modules table with 8 modules)
- Multi-tenant module permission merging (ModuleService)
- Module access control middleware (requireModule)
- User module permission API (GET /api/v1/auth/me/modules)
- Frontend module permission filtering (HomePage + TopNavigation)

Module Integration:
- RVW module integrated with PromptService (editorial + methodology)
- All modules (RVW/PKB/ASL/DC) added authenticate + requireModule middleware
- Fixed ReviewTask foreign key constraint (cross-schema issue)
- Removed all MOCK_USER_ID, unified to request.user?.userId

Prompt Management Enhancements:
- Module names displayed in Chinese (RVW -> 智能审稿)
- Enhanced version history with view content and rollback features
- List page shows both activeVersion and draftVersion columns

Database Changes:
- Added platform_schema.modules table
- Modified tenant_modules table (added index and UUID)
- Removed ReviewTask foreign key to public.users (cross-schema fix)
- Seeded 8 modules: RVW, PKB, ASL, DC, IIT, AIA, SSA, ST

Documentation Updates:
- Updated ADMIN module development status
- Updated TODO checklist (89% progress)
- Updated Prompt management plan (Phase 3.5.5 completed)
- Added module authentication specification

Files Changed: 80+
Status: All features tested and verified locally
Next: User management module development
This commit is contained in:
2026-01-13 07:34:30 +08:00
parent 5523ef36ea
commit d595037316
51 changed files with 3550 additions and 287 deletions

View File

@@ -56,21 +56,43 @@ export async function listPrompts(
const templates = await promptService.listTemplates(module);
// 转换为 API 响应格式
const result = templates.map(t => ({
id: t.id,
code: t.code,
name: t.name,
module: t.module,
description: t.description,
variables: t.variables,
latestVersion: t.versions[0] ? {
version: t.versions[0].version,
status: t.versions[0].status,
createdAt: t.versions[0].created_at,
} : null,
updatedAt: t.updated_at,
}));
// 转换为 API 响应格式,分别返回 ACTIVE 和 DRAFT 版本
const result = templates.map(t => {
const activeVersion = t.versions.find(v => v.status === 'ACTIVE');
const draftVersion = t.versions.find(v => v.status === 'DRAFT');
// 调试日志
console.log(`[PromptController] ${t.code} 版本数量: ${t.versions.length}`);
console.log(` - ACTIVE: ${activeVersion ? 'v' + activeVersion.version : '无'}`);
console.log(` - DRAFT: ${draftVersion ? 'v' + draftVersion.version : '无'}`);
return {
id: t.id,
code: t.code,
name: t.name,
module: t.module,
description: t.description,
variables: t.variables,
activeVersion: activeVersion ? {
version: activeVersion.version,
status: activeVersion.status,
createdAt: activeVersion.created_at,
} : null,
draftVersion: draftVersion ? {
version: draftVersion.version,
status: draftVersion.status,
createdAt: draftVersion.created_at,
} : null,
latestVersion: t.versions[0] ? {
version: t.versions[0].version,
status: t.versions[0].status,
createdAt: t.versions[0].created_at,
} : null,
updatedAt: t.updated_at,
};
});
console.log('[PromptController] 返回数据示例:', JSON.stringify(result[0], null, 2));
return reply.send({
success: true,
@@ -155,7 +177,7 @@ export async function saveDraft(
try {
const { code } = request.params;
const { content, modelConfig, changelog } = request.body;
const userId = (request as any).user?.id;
const userId = (request as any).user?.userId;
const promptService = getPromptService(prisma);
@@ -202,7 +224,7 @@ export async function publishPrompt(
) {
try {
const { code } = request.params;
const userId = (request as any).user?.id;
const userId = (request as any).user?.userId;
const promptService = getPromptService(prisma);
@@ -238,7 +260,7 @@ export async function rollbackPrompt(
try {
const { code } = request.params;
const { version } = request.body;
const userId = (request as any).user?.id;
const userId = (request as any).user?.userId;
const promptService = getPromptService(prisma);
@@ -273,7 +295,7 @@ export async function setDebugMode(
) {
try {
const { modules, enabled } = request.body;
const userId = (request as any).user?.id;
const userId = (request as any).user?.userId;
if (!userId) {
return reply.status(401).send({
@@ -317,7 +339,7 @@ export async function getDebugStatus(
reply: FastifyReply
) {
try {
const userId = (request as any).user?.id;
const userId = (request as any).user?.userId;
if (!userId) {
return reply.status(401).send({
@@ -416,3 +438,4 @@ export async function invalidateCache(
}
}