chore(deploy): finalize 0309 SAE rollout updates
Sync deployment documentation to the final successful SAE state and clear pending deployment checklist items. Include backend/frontend/R hardening and diagnostics improvements required for stable production behavior. Made-with: Cursor
This commit is contained in:
@@ -102,7 +102,7 @@ async function main() {
|
||||
// ============================================
|
||||
console.log('📌 创建超级管理员...');
|
||||
|
||||
const superAdmin = await prisma.User.upsert({
|
||||
const superAdmin = await prisma.user.upsert({
|
||||
where: { phone: '13800000001' },
|
||||
update: {},
|
||||
create: {
|
||||
@@ -123,7 +123,7 @@ async function main() {
|
||||
// ============================================
|
||||
console.log('📌 创建Prompt工程师账号...');
|
||||
|
||||
const promptEngineer = await prisma.User.upsert({
|
||||
const promptEngineer = await prisma.user.upsert({
|
||||
where: { phone: '13800000002' },
|
||||
update: {},
|
||||
create: {
|
||||
@@ -232,7 +232,7 @@ async function main() {
|
||||
// ============================================
|
||||
console.log('📌 创建医院管理员...');
|
||||
|
||||
const hospitalAdmin = await prisma.User.upsert({
|
||||
const hospitalAdmin = await prisma.user.upsert({
|
||||
where: { phone: '13800138001' },
|
||||
update: {},
|
||||
create: {
|
||||
@@ -266,7 +266,7 @@ async function main() {
|
||||
// ============================================
|
||||
console.log('📌 创建普通医生用户...');
|
||||
|
||||
const doctor1 = await prisma.User.upsert({
|
||||
const doctor1 = await prisma.user.upsert({
|
||||
where: { phone: '13800138002' },
|
||||
update: {},
|
||||
create: {
|
||||
@@ -282,7 +282,7 @@ async function main() {
|
||||
},
|
||||
});
|
||||
|
||||
const doctor2 = await prisma.User.upsert({
|
||||
const doctor2 = await prisma.user.upsert({
|
||||
where: { phone: '13800138003' },
|
||||
update: {},
|
||||
create: {
|
||||
@@ -343,6 +343,8 @@ async function main() {
|
||||
|
||||
// 审计日志权限
|
||||
{ code: 'audit:view', name: '查看审计日志', description: '查看操作审计日志', module: 'audit' },
|
||||
// 用户运营权限(可访问租户管理/用户管理/运营日志)
|
||||
{ code: 'ops:user-ops', name: '用户运营', description: '运营管理端用户运营视图权限', module: 'ops' },
|
||||
];
|
||||
|
||||
for (const perm of permissionsData) {
|
||||
|
||||
@@ -394,6 +394,64 @@ export async function logout(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录前端行为埋点(如顶部导航点击)
|
||||
*
|
||||
* POST /api/v1/auth/activity
|
||||
*/
|
||||
export async function logActivity(
|
||||
request: FastifyRequest<{
|
||||
Body: {
|
||||
module: 'SYSTEM' | 'AIA' | 'PKB' | 'ASL' | 'DC' | 'RVW' | 'IIT' | 'SSA' | 'ST';
|
||||
feature: string;
|
||||
action?: 'LOGIN' | 'USE' | 'EXPORT' | 'CREATE' | 'DELETE' | 'CLICK' | 'START' | 'COMPLETE' | 'ERROR';
|
||||
info?: string;
|
||||
};
|
||||
}>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
if (!request.user) {
|
||||
return reply.status(401).send({
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
message: '未认证',
|
||||
});
|
||||
}
|
||||
|
||||
const { module, feature, action = 'USE', info } = request.body;
|
||||
if (!module || !feature?.trim()) {
|
||||
return reply.status(400).send({
|
||||
success: false,
|
||||
error: 'BadRequest',
|
||||
message: 'module 和 feature 不能为空',
|
||||
});
|
||||
}
|
||||
|
||||
const user = await authService.getCurrentUser(request.user.userId);
|
||||
activityService.log(
|
||||
user.tenantId,
|
||||
user.tenantName || null,
|
||||
user.id,
|
||||
user.name,
|
||||
module,
|
||||
feature.trim(),
|
||||
action,
|
||||
info,
|
||||
);
|
||||
|
||||
return reply.status(200).send({ success: true });
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '埋点记录失败';
|
||||
logger.warn('记录埋点失败', { error: message, userId: request.user?.userId });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
error: 'InternalServerError',
|
||||
message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <20><>ȡ<EFBFBD><C8A1>ǰ<EFBFBD>û<EFBFBD><C3BB>ɷ<EFBFBD><C9B7>ʵ<EFBFBD>ģ<EFBFBD><C4A3>
|
||||
|
||||
@@ -183,20 +183,73 @@ export function requirePermission(requiredPermission: string): preHandlerHookHan
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: 从缓存或数据库获取用户权限
|
||||
// 目前简化处理:超级管理员拥有所有权限
|
||||
if (request.user.role === 'SUPER_ADMIN') {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: 实现权限检查逻辑
|
||||
// const hasPermission = await checkUserPermission(request.user.userId, requiredPermission);
|
||||
// if (!hasPermission) {
|
||||
// return reply.status(403).send({
|
||||
// error: 'Forbidden',
|
||||
// message: `需要权限: ${requiredPermission}`,
|
||||
// });
|
||||
// }
|
||||
const allowed = await prisma.role_permissions.findFirst({
|
||||
where: {
|
||||
role: request.user.role as any,
|
||||
permissions: {
|
||||
code: requiredPermission,
|
||||
},
|
||||
},
|
||||
select: { permission_id: true },
|
||||
});
|
||||
|
||||
if (!allowed) {
|
||||
logger.warn('权限不足', {
|
||||
userId: request.user.userId,
|
||||
role: request.user.role,
|
||||
requiredPermission,
|
||||
});
|
||||
return reply.status(403).send({
|
||||
error: 'Forbidden',
|
||||
message: `需要权限: ${requiredPermission}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 任一权限检查中间件工厂
|
||||
*
|
||||
* @param requiredPermissions 允许的权限code列表(命中任意一个即放行)
|
||||
*/
|
||||
export function requireAnyPermission(...requiredPermissions: string[]): preHandlerHookHandler {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
if (!request.user) {
|
||||
return reply.status(401).send({
|
||||
error: 'Unauthorized',
|
||||
message: '未认证',
|
||||
});
|
||||
}
|
||||
|
||||
if (request.user.role === 'SUPER_ADMIN') {
|
||||
return;
|
||||
}
|
||||
|
||||
const allowed = await prisma.role_permissions.findFirst({
|
||||
where: {
|
||||
role: request.user.role as any,
|
||||
permissions: {
|
||||
code: { in: requiredPermissions },
|
||||
},
|
||||
},
|
||||
select: { permission_id: true },
|
||||
});
|
||||
|
||||
if (!allowed) {
|
||||
logger.warn('权限不足(任一权限检查失败)', {
|
||||
userId: request.user.userId,
|
||||
role: request.user.role,
|
||||
requiredPermissions,
|
||||
});
|
||||
return reply.status(403).send({
|
||||
error: 'Forbidden',
|
||||
message: `需要权限之一: ${requiredPermissions.join(', ')}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
uploadAvatar,
|
||||
refreshToken,
|
||||
logout,
|
||||
logActivity,
|
||||
} from './auth.controller.js';
|
||||
import { authenticate } from './auth.middleware.js';
|
||||
|
||||
@@ -87,6 +88,26 @@ const refreshTokenSchema = {
|
||||
},
|
||||
};
|
||||
|
||||
const logActivitySchema = {
|
||||
body: {
|
||||
type: 'object',
|
||||
required: ['module', 'feature'],
|
||||
properties: {
|
||||
module: {
|
||||
type: 'string',
|
||||
enum: ['SYSTEM', 'AIA', 'PKB', 'ASL', 'DC', 'RVW', 'IIT', 'SSA', 'ST'],
|
||||
},
|
||||
feature: { type: 'string', minLength: 1, maxLength: 120 },
|
||||
action: {
|
||||
type: 'string',
|
||||
enum: ['LOGIN', 'USE', 'EXPORT', 'CREATE', 'DELETE', 'CLICK', 'START', 'COMPLETE', 'ERROR'],
|
||||
default: 'USE',
|
||||
},
|
||||
info: { type: 'string', maxLength: 2000 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 注册认证路由
|
||||
*/
|
||||
@@ -170,6 +191,14 @@ export async function authRoutes(
|
||||
fastify.post('/logout', {
|
||||
preHandler: [authenticate],
|
||||
}, logout);
|
||||
|
||||
/**
|
||||
* 前端行为埋点上报(如顶部导航点击)
|
||||
*/
|
||||
fastify.post('/activity', {
|
||||
preHandler: [authenticate],
|
||||
schema: logActivitySchema,
|
||||
}, logActivity as any);
|
||||
}
|
||||
|
||||
export default authRoutes;
|
||||
|
||||
@@ -34,6 +34,9 @@ export type ActionType =
|
||||
| 'EXPORT' // 导出/下载
|
||||
| 'CREATE' // 创建资源
|
||||
| 'DELETE' // 删除资源
|
||||
| 'CLICK' // 点击行为
|
||||
| 'START' // 启动任务
|
||||
| 'COMPLETE'// 完成任务
|
||||
| 'ERROR'; // 错误记录
|
||||
|
||||
/**
|
||||
@@ -111,22 +114,35 @@ export const activityService = {
|
||||
*/
|
||||
async getTodayOverview(): Promise<{
|
||||
dau: number;
|
||||
mau: number;
|
||||
dat: number;
|
||||
exportCount: number;
|
||||
apiTokenTotal: number;
|
||||
topActiveUser: { userId: string; userName: string | null; actionCount: number } | null;
|
||||
moduleStats: Record<string, number>;
|
||||
}> {
|
||||
const todayStart = new Date();
|
||||
todayStart.setHours(0, 0, 0, 0);
|
||||
const monthStart = new Date(todayStart);
|
||||
monthStart.setDate(monthStart.getDate() - 29);
|
||||
|
||||
// 查询 DAU/DAT/导出数
|
||||
// 查询 DAU/MAU/DAT/导出数/Token总量(从 info 文本中提取 tokens: N)
|
||||
const stats = await prisma.$queryRaw`
|
||||
SELECT
|
||||
COUNT(DISTINCT user_id) as dau,
|
||||
COUNT(DISTINCT CASE WHEN created_at >= ${monthStart} THEN user_id END) as mau,
|
||||
COUNT(DISTINCT tenant_id) as dat,
|
||||
COUNT(CASE WHEN action = 'EXPORT' THEN 1 END) as export_count
|
||||
COUNT(CASE WHEN action = 'EXPORT' THEN 1 END) as export_count,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN info ~* 'tokens?\\D*\\d+'
|
||||
THEN (regexp_match(info, '(?i)tokens?\\D*(\\d+)'))[1]::bigint
|
||||
ELSE 0
|
||||
END
|
||||
), 0) as api_token_total
|
||||
FROM admin_schema.simple_logs
|
||||
WHERE created_at >= ${todayStart}
|
||||
` as Array<{ dau: bigint; dat: bigint; export_count: bigint }>;
|
||||
` as Array<{ dau: bigint; mau: bigint; dat: bigint; export_count: bigint; api_token_total: bigint }>;
|
||||
|
||||
// 查询模块使用统计
|
||||
const moduleStats = await prisma.$queryRaw`
|
||||
@@ -141,10 +157,31 @@ export const activityService = {
|
||||
moduleMap[m.module] = Number(m.count);
|
||||
});
|
||||
|
||||
// 今日最活跃用户(按行为数)
|
||||
const topUsers = await prisma.$queryRaw`
|
||||
SELECT user_id, user_name, COUNT(*) as action_count
|
||||
FROM admin_schema.simple_logs
|
||||
WHERE created_at >= ${todayStart}
|
||||
GROUP BY user_id, user_name
|
||||
ORDER BY action_count DESC
|
||||
LIMIT 1
|
||||
` as Array<{ user_id: string; user_name: string | null; action_count: bigint }>;
|
||||
|
||||
const topActiveUser = topUsers[0]
|
||||
? {
|
||||
userId: topUsers[0].user_id,
|
||||
userName: topUsers[0].user_name,
|
||||
actionCount: Number(topUsers[0].action_count),
|
||||
}
|
||||
: null;
|
||||
|
||||
return {
|
||||
dau: Number(stats[0]?.dau || 0),
|
||||
mau: Number(stats[0]?.mau || 0),
|
||||
dat: Number(stats[0]?.dat || 0),
|
||||
exportCount: Number(stats[0]?.export_count || 0),
|
||||
apiTokenTotal: Number(stats[0]?.api_token_total || 0),
|
||||
topActiveUser,
|
||||
moduleStats: moduleMap,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ import { createSkillRunner } from '../../iit-manager/engines/SkillRunner.js';
|
||||
import { QcExecutor } from '../../iit-manager/engines/QcExecutor.js';
|
||||
import { QcReportService } from '../../iit-manager/services/QcReportService.js';
|
||||
import { dailyQcOrchestrator } from '../../iit-manager/services/DailyQcOrchestrator.js';
|
||||
import { activityService } from '../../../common/services/activity.service.js';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -45,10 +46,30 @@ export class IitBatchController {
|
||||
) {
|
||||
const { projectId } = request.params;
|
||||
const startTime = Date.now();
|
||||
const requestUserId = (request as any).user?.userId as string | undefined;
|
||||
|
||||
try {
|
||||
logger.info('[V3.1] Batch QC started', { projectId });
|
||||
|
||||
if (requestUserId) {
|
||||
const actor = await prisma.user.findUnique({
|
||||
where: { id: requestUserId },
|
||||
include: { tenants: true },
|
||||
});
|
||||
if (actor) {
|
||||
activityService.log(
|
||||
actor.tenant_id,
|
||||
actor.tenants?.name || null,
|
||||
actor.id,
|
||||
actor.name,
|
||||
'IIT',
|
||||
'CRA质控',
|
||||
'START',
|
||||
`projectId:${projectId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const project = await prisma.iitProject.findUnique({
|
||||
where: { id: projectId },
|
||||
select: { id: true },
|
||||
@@ -87,6 +108,25 @@ export class IitBatchController {
|
||||
projectId, totalRecords, totalEvents, passed, failed, warnings, durationMs,
|
||||
});
|
||||
|
||||
if (requestUserId) {
|
||||
const actor = await prisma.user.findUnique({
|
||||
where: { id: requestUserId },
|
||||
include: { tenants: true },
|
||||
});
|
||||
if (actor) {
|
||||
activityService.log(
|
||||
actor.tenant_id,
|
||||
actor.tenants?.name || null,
|
||||
actor.id,
|
||||
actor.name,
|
||||
'IIT',
|
||||
'CRA质控',
|
||||
'COMPLETE',
|
||||
`projectId:${projectId}, total:${totalEvents}, pass:${passed}, fail:${failed}, warn:${warnings}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
message: '事件级全量质控完成(V3.1 QcExecutor)',
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import * as statsController from '../controllers/statsController.js';
|
||||
import { authenticate, requirePermission } from '../../../common/auth/auth.middleware.js';
|
||||
import { authenticate, requireAnyPermission, requirePermission } from '../../../common/auth/auth.middleware.js';
|
||||
|
||||
export async function statsRoutes(fastify: FastifyInstance) {
|
||||
// ==================== 运营统计 ====================
|
||||
@@ -21,7 +21,7 @@ export async function statsRoutes(fastify: FastifyInstance) {
|
||||
* 权限: SUPER_ADMIN, PROMPT_ENGINEER
|
||||
*/
|
||||
fastify.get('/overview', {
|
||||
preHandler: [authenticate, requirePermission('tenant:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('tenant:view', 'ops:user-ops')],
|
||||
handler: statsController.getOverview,
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ export async function statsRoutes(fastify: FastifyInstance) {
|
||||
* 权限: SUPER_ADMIN, PROMPT_ENGINEER
|
||||
*/
|
||||
fastify.get('/live-feed', {
|
||||
preHandler: [authenticate, requirePermission('tenant:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('tenant:view', 'ops:user-ops')],
|
||||
handler: statsController.getLiveFeed,
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function statsRoutes(fastify: FastifyInstance) {
|
||||
* 权限: SUPER_ADMIN, PROMPT_ENGINEER
|
||||
*/
|
||||
fastify.get('/logs', {
|
||||
preHandler: [authenticate, requirePermission('tenant:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('tenant:view', 'ops:user-ops')],
|
||||
handler: statsController.getActivityLogs,
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@ export async function userOverviewRoute(fastify: FastifyInstance) {
|
||||
* 权限: SUPER_ADMIN
|
||||
*/
|
||||
fastify.get('/:id/overview', {
|
||||
preHandler: [authenticate, requirePermission('user:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('user:view', 'ops:user-ops')],
|
||||
handler: statsController.getUserOverview,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import * as tenantController from '../controllers/tenantController.js';
|
||||
import { authenticate, requirePermission } from '../../../common/auth/auth.middleware.js';
|
||||
import { authenticate, requireAnyPermission, requirePermission } from '../../../common/auth/auth.middleware.js';
|
||||
|
||||
export async function tenantRoutes(fastify: FastifyInstance) {
|
||||
// ==================== 租户管理 ====================
|
||||
@@ -14,14 +14,14 @@ export async function tenantRoutes(fastify: FastifyInstance) {
|
||||
// 获取租户列表
|
||||
// GET /api/admin/tenants?type=&status=&search=&page=1&limit=20
|
||||
fastify.get('/', {
|
||||
preHandler: [authenticate, requirePermission('tenant:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('tenant:view', 'ops:user-ops')],
|
||||
handler: tenantController.listTenants,
|
||||
});
|
||||
|
||||
// 获取租户详情
|
||||
// GET /api/admin/tenants/:id
|
||||
fastify.get('/:id', {
|
||||
preHandler: [authenticate, requirePermission('tenant:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('tenant:view', 'ops:user-ops')],
|
||||
handler: tenantController.getTenant,
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ export async function moduleRoutes(fastify: FastifyInstance) {
|
||||
// 获取所有可用模块列表
|
||||
// GET /api/admin/modules
|
||||
fastify.get('/', {
|
||||
preHandler: [authenticate, requirePermission('tenant:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('tenant:view', 'ops:user-ops')],
|
||||
handler: tenantController.listModules,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import { authenticate, requireRoles, requirePermission } from '../../../common/auth/auth.middleware.js';
|
||||
import { authenticate, requireRoles, requirePermission, requireAnyPermission } from '../../../common/auth/auth.middleware.js';
|
||||
import * as userController from '../controllers/userController.js';
|
||||
|
||||
/**
|
||||
@@ -17,14 +17,14 @@ export async function userRoutes(fastify: FastifyInstance) {
|
||||
// 获取用户列表
|
||||
// GET /api/admin/users?page=1&pageSize=20&search=&role=&tenantId=&status=&departmentId=
|
||||
fastify.get('/', {
|
||||
preHandler: [authenticate, requirePermission('user:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('user:view', 'ops:user-ops')],
|
||||
handler: userController.listUsers,
|
||||
});
|
||||
|
||||
// 获取用户详情
|
||||
// GET /api/admin/users/:id
|
||||
fastify.get('/:id', {
|
||||
preHandler: [authenticate, requirePermission('user:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('user:view', 'ops:user-ops')],
|
||||
handler: userController.getUserById,
|
||||
});
|
||||
|
||||
@@ -95,21 +95,21 @@ export async function userRoutes(fastify: FastifyInstance) {
|
||||
// 获取所有租户列表(用于下拉选择)
|
||||
// GET /api/admin/users/options/tenants
|
||||
fastify.get('/options/tenants', {
|
||||
preHandler: [authenticate, requirePermission('user:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('user:view', 'ops:user-ops')],
|
||||
handler: userController.getAllTenants,
|
||||
});
|
||||
|
||||
// 获取租户的科室列表
|
||||
// GET /api/admin/users/options/tenants/:tenantId/departments
|
||||
fastify.get('/options/tenants/:tenantId/departments', {
|
||||
preHandler: [authenticate, requirePermission('user:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('user:view', 'ops:user-ops')],
|
||||
handler: userController.getDepartmentsByTenant,
|
||||
});
|
||||
|
||||
// 获取租户的模块列表(用于模块配置)
|
||||
// GET /api/admin/users/options/tenants/:tenantId/modules
|
||||
fastify.get('/options/tenants/:tenantId/modules', {
|
||||
preHandler: [authenticate, requirePermission('user:view')],
|
||||
preHandler: [authenticate, requireAnyPermission('user:view', 'ops:user-ops')],
|
||||
handler: userController.getModulesByTenant,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { prisma } from '../../../config/database.js';
|
||||
import { jobQueue } from '../../../common/jobs/index.js';
|
||||
import { logger } from '../../../common/logging/index.js';
|
||||
import { activityService } from '../../../common/services/activity.service.js';
|
||||
import { requirementExpansionService } from '../services/requirementExpansionService.js';
|
||||
import { wordExportService } from '../services/wordExportService.js';
|
||||
import { DEEP_RESEARCH_DATA_SOURCES } from '../config/dataSources.js';
|
||||
@@ -63,6 +64,28 @@ export async function generateRequirement(
|
||||
filters,
|
||||
});
|
||||
|
||||
// 埋点:ASL 意图识别(需求扩写)
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: { tenants: true },
|
||||
});
|
||||
if (user) {
|
||||
activityService.log(
|
||||
user.tenant_id,
|
||||
user.tenants?.name || null,
|
||||
user.id,
|
||||
user.name,
|
||||
'ASL',
|
||||
'意图识别',
|
||||
'USE',
|
||||
`queryLen:${originalQuery.trim().length}`,
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// 埋点失败不影响主流程
|
||||
}
|
||||
|
||||
return reply.send({ success: true, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error('[DeepResearchController] generateRequirement failed', {
|
||||
@@ -120,6 +143,28 @@ export async function executeTask(
|
||||
|
||||
await jobQueue.push('asl_deep_research_v2', { taskId });
|
||||
|
||||
// 埋点:启动 Deep Research
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: { tenants: true },
|
||||
});
|
||||
if (user) {
|
||||
activityService.log(
|
||||
user.tenant_id,
|
||||
user.tenants?.name || null,
|
||||
user.id,
|
||||
user.name,
|
||||
'ASL',
|
||||
'启动Deep Research',
|
||||
'START',
|
||||
`taskId:${taskId}`,
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// 埋点失败不影响主流程
|
||||
}
|
||||
|
||||
logger.info('[DeepResearchController] Task pushed to queue', { taskId });
|
||||
|
||||
return reply.send({ success: true });
|
||||
|
||||
@@ -172,6 +172,25 @@ export class AIController {
|
||||
error: '重试次数必须在1-5之间'
|
||||
});
|
||||
}
|
||||
|
||||
// 埋点:记录 Tool C AI清洗启动
|
||||
try {
|
||||
const user = (request as any).user;
|
||||
if (user) {
|
||||
activityService.log(
|
||||
user.tenantId,
|
||||
user.tenantName || null,
|
||||
user.id || user.userId,
|
||||
user.name || null,
|
||||
'DC',
|
||||
'智能数据清洗',
|
||||
'START',
|
||||
`session:${sessionId}`,
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// 埋点失败不影响主业务
|
||||
}
|
||||
|
||||
// 生成并执行(带重试)
|
||||
const result = await aiCodeService.generateAndExecute(
|
||||
|
||||
@@ -14,6 +14,8 @@ import { logger } from '../../../common/logging/index.js';
|
||||
import { ModelType } from '../../../common/llm/adapters/types.js';
|
||||
import * as reviewService from '../services/reviewService.js';
|
||||
import { AgentType } from '../types/index.js';
|
||||
import { activityService } from '../../../common/services/activity.service.js';
|
||||
import { prisma } from '../../../config/database.js';
|
||||
|
||||
/**
|
||||
* 获取用户ID(从JWT Token中获取)
|
||||
@@ -119,6 +121,28 @@ export async function createTask(
|
||||
// 创建任务
|
||||
const task = await reviewService.createTask(file, filename, userId, tenantId, modelType);
|
||||
|
||||
// 埋点:稿件上传
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: { tenants: true },
|
||||
});
|
||||
if (user) {
|
||||
activityService.log(
|
||||
user.tenant_id,
|
||||
user.tenants?.name || null,
|
||||
user.id,
|
||||
user.name,
|
||||
'RVW',
|
||||
'稿件上传',
|
||||
'CREATE',
|
||||
`taskId:${task.id}, file:${filename}, size:${fileSizeBytes}`,
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// 埋点失败不影响主流程
|
||||
}
|
||||
|
||||
logger.info('[RVW:Controller] 任务已创建', { taskId: task.id });
|
||||
|
||||
return reply.send({
|
||||
|
||||
Reference in New Issue
Block a user