import { postgrestGet, postgrestPost, type PostgrestParams } from "../postgrest-client"; import dayjs from 'dayjs'; /** * 从不同格式的 API 响应中提取数据 * @param responseData API 响应数据 * @returns 提取后的数据或 null */ function extractApiData(responseData: unknown): T | null { if (!responseData) { console.warn('API响应数据为空'); return null; } try { // 检查是否有错误信息 if (typeof responseData === 'object' && responseData !== null) { // 错误检查: 检查错误码,一般成功的错误码是0或200 if ('code' in responseData) { const code = (responseData as { code: number }).code; // 如果有错误码且不是成功状态 if (code !== 0 && code !== 200) { const errorMsg = 'msg' in responseData ? (responseData as { msg: string }).msg : '未知错误'; console.error(`API响应错误: [${code}] ${errorMsg}`); return null; } } // 错误检查: 检查是否包含错误消息但没有数据 if ('error' in responseData && (responseData as { error: unknown }).error) { const error = (responseData as { error: unknown }).error; console.error(`API响应包含错误: ${typeof error === 'string' ? error : JSON.stringify(error)}`); return null; } // 格式1: { code: number, msg: string, data: T } if ('data' in responseData) { const data = (responseData as { data: unknown }).data; if (!data) { console.warn('API响应中的data字段为空'); return null; } return data as T; } } // 格式2: 直接是数据对象 return responseData as T; } catch (error) { console.error('处理API响应数据时出错:', error); return null; } } /** * 首页数据统计响应类型 */ interface HomeStatistics { todayPendingFiles: number; monthlyReviewedFiles: number; monthlyReviewGrowth: { value: number; isUp: boolean; }; monthlyPassRate: number; passRateGrowth: { value: number; isUp: boolean; }; issuesDetected: number; issuesGrowth: { value: number; isUp: boolean; }; } /** * 通过传入的 reviewType 参数构建类型过滤条件 * @param reviewType 文档类型 * @returns 过滤条件字符串 */ function buildTypeFilter(reviewType: string | null): string { let typeFilter = ''; if (reviewType === 'contract') { typeFilter = 'type_id.eq.1'; } else if (reviewType === 'record') { typeFilter = '(type_id.eq.2,type_id.eq.3)'; } return typeFilter; } /** * 获取主页数据 * @param reviewType 从客户端传入的 reviewType 值 * @param userId 用户ID * @param token JWT token * @returns 主页数据 */ export async function getHomeData(reviewType?: string | null,userId?: string | number, token?: string): Promise { try { // 获取当前日期和时间相关值 const startOfToday = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'); const startOfThisMonth = dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss'); const endOfThisMonth = dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss'); const startOfLastMonth = dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD HH:mm:ss'); const endOfLastMonth = dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD HH:mm:ss'); // console.log('传入的 reviewType', reviewType); // console.log('传入的 userId', userId); // 基于 reviewType 构建类型过滤条件 const typeFilter = buildTypeFilter(reviewType || null); // console.log('构建的 typeFilter', typeFilter); // 通用API响应处理函数 const handleApiResponse = async ( apiCall: Promise<{ data?: unknown; headers?: Record; error?: string; status?: number }>, errorMessage: string, defaultValue: T ): Promise => { try { const response = await apiCall; if (response.error) { console.error(`${errorMessage}: ${response.error}`); return defaultValue; } const data = extractApiData(response.data); if (!data) { console.warn(`${errorMessage}: 无法提取有效数据`); return defaultValue; } return data; } catch (error) { console.error(`${errorMessage}: ${error instanceof Error ? error.message : '未知错误'}`); return defaultValue; } }; // 1. 今日待审核文件 - 获取今天的待审核文件数量 (audit_status = 0 或 2) const todayPendingParams: PostgrestParams = { select: 'count', filter: { or: `(audit_status.eq.0,audit_status.eq.2,audit_status.is.null)`, created_at: `gte.${startOfToday}`, is_test_document: `eq.false`, user_id: `eq.${userId}` } }; // 添加类型过滤条件 if (typeFilter) { if (typeFilter.startsWith('(')) { // 确保 filter 已初始化 if (!todayPendingParams.filter) { todayPendingParams.filter = {}; } todayPendingParams.filter.or = typeFilter + ',' + todayPendingParams.filter.or; } else { const [field, op, value] = typeFilter.split('.'); if (!todayPendingParams.filter) { todayPendingParams.filter = {}; } todayPendingParams.filter[field] = `${op}.${value}`; } } const todayPendingCount = await handleApiResponse<{ count: number }[]>( postgrestGet('documents', { ...todayPendingParams, token }), '获取今日待审核文件数量失败', [] ); const todayPendingFiles = todayPendingCount[0]?.count || 0; // 2. 本月已审核文件 - 获取本月已审核文件数量 (audit_status != 0 且 != 2) const thisMonthReviewedParams: PostgrestParams = { select: 'count', filter: { and: `(audit_status.neq.0,audit_status.neq.2)`, upload_time: `gte.${startOfThisMonth}`, is_test_document: `eq.false`, user_id: `eq.${userId}` } }; // 添加类型过滤条件 if (typeFilter) { if (typeFilter.startsWith('(')) { thisMonthReviewedParams.or = typeFilter; } else { const [field, op, value] = typeFilter.split('.'); if (!thisMonthReviewedParams.filter) { thisMonthReviewedParams.filter = {}; } thisMonthReviewedParams.filter[field] = `${op}.${value}`; } } const thisMonthReviewedCount = await handleApiResponse<{ count: number }[]>( postgrestGet('documents', { ...thisMonthReviewedParams, token }), '获取本月已审核文件数量失败', [] ); // 本月已审核文件数量 const monthlyReviewedFiles = thisMonthReviewedCount[0]?.count || 0; // 上月已审核文件 const lastMonthReviewedParams: PostgrestParams = { select: 'count', filter: { // or: `(audit_status.eq.1,audit_status.eq.-1)`, and: `(upload_time.gte.${startOfLastMonth},upload_time.lte.${endOfLastMonth},audit_status.neq.0,audit_status.neq.2)`, is_test_document: `eq.false`, user_id: `eq.${userId}` } }; // 添加类型过滤条件 if (typeFilter) { if (typeFilter.startsWith('(')) { // 确保 filter 已初始化 if (!lastMonthReviewedParams.filter) { lastMonthReviewedParams.filter = {}; } lastMonthReviewedParams.filter.or = typeFilter; } else { const [field, op, value] = typeFilter.split('.'); if (!lastMonthReviewedParams.filter) { lastMonthReviewedParams.filter = {}; } lastMonthReviewedParams.filter[field] = `${op}.${value}`; } } const lastMonthReviewedCount = await handleApiResponse<{ count: number }[]>( postgrestGet('documents', { ...lastMonthReviewedParams, token }), '获取上月已审核文件数量失败', [] ); // 上月已审核文件数量 const lastMonthReviewed = lastMonthReviewedCount[0]?.count || 0; // console.log('上月已审核文件查询参数', lastMonthReviewedParams); // console.log('上月已审核文件数量', lastMonthReviewed); // 计算同比增长 let reviewGrowthValue = 0; let reviewGrowthIsUp = true; if (lastMonthReviewed > 0) { const growthRate = ((monthlyReviewedFiles - lastMonthReviewed) / lastMonthReviewed) * 100; reviewGrowthValue = Math.abs(parseFloat(growthRate.toFixed(1))); reviewGrowthIsUp = growthRate >= 0; } else if (lastMonthReviewed == 0 && monthlyReviewedFiles > 0) { reviewGrowthValue = 100; reviewGrowthIsUp = true; } // 3. 审核通过率 - 本月审核通过率 const thisMonthTotalParams: PostgrestParams = { select: 'count', filter: { audit_status: `eq.1`, created_at: `gte.${startOfThisMonth}`, is_test_document: `eq.false`, user_id: `eq.${userId}` } }; // 添加类型过滤条件 if (typeFilter) { if (typeFilter.startsWith('(')) { thisMonthTotalParams.or = typeFilter; } else { const [field, op, value] = typeFilter.split('.'); if (!thisMonthTotalParams.filter) { thisMonthTotalParams.filter = {}; } thisMonthTotalParams.filter[field] = `${op}.${value}`; } } const thisMonthTotalCount = await handleApiResponse<{ count: number }[]>( postgrestGet('documents', { ...thisMonthTotalParams, token }), '获取本月审核通过数量失败', [] ); // console.log('本月审核通过数量查询参数', thisMonthTotalParams); // 本月审核通过数量 const thisMonthPassTotal = thisMonthTotalCount[0]?.count || 0; // console.log('本月审核通过数量', thisMonthPassTotal); // console.log('本月已审核文件数量', monthlyReviewedFiles); // 本月审核通过率 const monthlyPassRate = (thisMonthPassTotal > 0 && monthlyReviewedFiles > 0) ? parseFloat(((thisMonthPassTotal / monthlyReviewedFiles) * 100).toFixed(1)) : 0; // 上月审核通过率 const lastMonthTotalParams: PostgrestParams = { select: 'count', filter: { audit_status: `eq.1`, and: `(upload_time.gte.${startOfLastMonth},upload_time.lte.${endOfLastMonth})`, is_test_document: `eq.false`, user_id: `eq.${userId}` } }; // 添加类型过滤条件 if (typeFilter) { if (typeFilter.startsWith('(')) { lastMonthTotalParams.or = typeFilter; } else { const [field, op, value] = typeFilter.split('.'); if (!lastMonthTotalParams.filter) { lastMonthTotalParams.filter = {}; } lastMonthTotalParams.filter[field] = `${op}.${value}`; } } const lastMonthTotalCount = await handleApiResponse<{ count: number }[]>( postgrestGet('documents', { ...lastMonthTotalParams, token }), '获取上月审核通过数量失败', [] ); // 上月审核通过数量 const lastMonthTotal = lastMonthTotalCount[0]?.count || 0; // 上月审核通过率 const lastMonthPassRate = (lastMonthTotal > 0 && lastMonthReviewed > 0) ? parseFloat(((lastMonthTotal / lastMonthReviewed) * 100).toFixed(1)) : 0; // console.log('上个月-------', lastMonthPassRate); // 计算通过率同比增长 let passRateGrowthValue = 0; let passRateGrowthIsUp = true; if (lastMonthPassRate > 0) { const passRateGrowth = ((monthlyPassRate - lastMonthPassRate) / lastMonthPassRate) * 100; passRateGrowthValue = Math.abs(parseFloat(passRateGrowth.toFixed(1))); passRateGrowthIsUp = passRateGrowth >= 0; } else if (lastMonthPassRate == 0 && monthlyPassRate > 0) { passRateGrowthValue = 100; passRateGrowthIsUp = true; } // console.log('上月通过率-------', lastMonthPassRate); // console.log('本月通过率-------', monthlyPassRate); // 4. 检查出的问题总数(从评估结果表中统计) // 使用新的数据库函数 count_evaluation_results_by_type 获取指定类型文档的问题数量 let thisMonthIssuesCount = 0; let lastMonthIssuesCount = 0; // 根据 reviewType 设置要查询的文档类型 if (reviewType === 'contract') { // 合同类型 - 直接查询类型 1 const typeToQuery = [1]; // 调用数据库函数获取本月指定类型的问题数量 const thisMonthIssuesResponse = await handleApiResponse<{ count: number }[]>( postgrestPost('rpc/count_evaluation_results_by_type', { start_time: startOfThisMonth, end_time: endOfThisMonth, type_val: typeToQuery, userid: parseInt(userId as string) }, token), '获取合同本月问题数据失败', [] ); // 本月问题数量 thisMonthIssuesCount = thisMonthIssuesResponse[0]?.count || 0; // 调用数据库函数获取上月指定类型的问题数量 const lastMonthIssuesResponse = await handleApiResponse<{ count: number }[]>( postgrestPost('rpc/count_evaluation_results_by_type', { start_time: startOfLastMonth, end_time: endOfLastMonth, type_val: typeToQuery, userid: parseInt(userId as string) }, token), '获取上月问题数据失败', [] ); // 上月问题数量 lastMonthIssuesCount = lastMonthIssuesResponse[0]?.count || 0; } else if (reviewType === 'record') { // 记录类型 - 需要查询类型 2 和类型 3,并合并结果 const typeToQuery = [2,3]; const thisMonthType2Response = await handleApiResponse<{ count: number }[]>( postgrestPost('rpc/count_evaluation_results_by_type', { start_time: startOfThisMonth, end_time: endOfThisMonth, type_val: typeToQuery, userid: parseInt(userId as string) }, token), '获取本月许可卷宗类型2问题数据失败', [] ); // 本月两种类型的问题数量 const thisMonthType2Count = thisMonthType2Response[0]?.count || 0; thisMonthIssuesCount = thisMonthType2Count // 上月两种类型的问题数量 const lastMonthType2Response = await handleApiResponse<{ count: number }[]>( postgrestPost('rpc/count_evaluation_results_by_type', { start_time: startOfLastMonth, end_time: endOfLastMonth, type_val: typeToQuery, userid: parseInt(userId as string) }, token), '获取上月许可卷宗类型2问题数据失败', [] ); // 上月两种类型的问题数量 const lastMonthType2Count = lastMonthType2Response[0]?.count || 0; lastMonthIssuesCount = lastMonthType2Count } // 计算问题数量同比增长 let issuesGrowthValue = 0; let issuesGrowthIsUp = true; if (lastMonthIssuesCount > 0) { const issuesGrowth = ((thisMonthIssuesCount - lastMonthIssuesCount) / lastMonthIssuesCount) * 100; issuesGrowthValue = Math.abs(parseFloat(issuesGrowth.toFixed(1))); issuesGrowthIsUp = issuesGrowth >= 0; }else if(lastMonthIssuesCount == 0 && thisMonthIssuesCount > 0){ issuesGrowthValue = 100; issuesGrowthIsUp = true; } // 返回统计结果 return { todayPendingFiles, monthlyReviewedFiles, monthlyReviewGrowth: { value: reviewGrowthValue, isUp: reviewGrowthIsUp }, monthlyPassRate, passRateGrowth: { value: passRateGrowthValue, isUp: passRateGrowthIsUp }, issuesDetected: thisMonthIssuesCount, issuesGrowth: { value: issuesGrowthValue, isUp: issuesGrowthIsUp } }; } catch (error) { console.error('获取首页数据失败:', error instanceof Error ? error.message : String(error)); // 返回默认值以防止页面崩溃 return { todayPendingFiles: 0, monthlyReviewedFiles: 0, monthlyReviewGrowth: { value: 0, isUp: true }, monthlyPassRate: 0, passRateGrowth: { value: 0, isUp: true }, issuesDetected: 0, issuesGrowth: { value: 0, isUp: true } }; } } /** * 入口模块类型定义 */ export interface EntryModule { id: number; name: string; description: string | null; path: string | null; areas: string[]; created_at: string; updated_at: string; document_types?: Array<{ id: number; name: string; code: string | null; }>; } /** * 获取用户可访问的入口模块 * @param userArea 用户所属地区 * @param token JWT token * @returns 入口模块列表 */ export async function getEntryModules(userRole: string | null | undefined, userArea: string | null | undefined, token?: string): Promise { try { if (!userRole || !userArea) { console.warn('⚠️ [getEntryModules] 用户角色或地区为空,返回空模块列表'); return []; } // console.log('🔍 [getEntryModules] 查询地区:', userArea); // 查询 entry_modules 表,筛选 areas 数组中包含用户地区的模块 // 使用 PostgreSQL JSONB 操作符 @> 检查数组是否包含值 const params: PostgrestParams = { select: 'id,name,description,path,areas,created_at,updated_at', filter: { // areas 数组中包含用户的 area // areas: `cs.["${userArea}"]` // cs = contains (PostgreSQL @> 操作符) } }; if (userRole != 'provincial_admin'){ params.filter = { areas: `cs.["${userArea}"]` } } const modulesResponse = await postgrestGet('entry_modules', { ...params, token }); if (modulesResponse.error) { console.error('❌ [getEntryModules] 查询入口模块失败:', modulesResponse.error); return []; } const modules = extractApiData(modulesResponse.data); if (!modules || modules.length === 0) { console.warn('⚠️ [getEntryModules] 未找到匹配的入口模块'); return []; } console.log(`✅ [getEntryModules] 找到 ${modules.length} 个入口模块`); // 为每个模块查询关联的 document_types const modulesWithTypes = await Promise.all( modules.map(async (module) => { try { const typesParams: PostgrestParams = { select: 'id,name,code', filter: { entry_module_id: `eq.${module.id}` } }; const typesResponse = await postgrestGet('document_types', { ...typesParams, token }); if (typesResponse.error) { console.error(`❌ [getEntryModules] 查询模块 ${module.id} 的文档类型失败:`, typesResponse.error); return { ...module, document_types: [] }; } const documentTypes = extractApiData>(typesResponse.data); return { ...module, document_types: documentTypes || [] }; } catch (error) { console.error(`❌ [getEntryModules] 处理模块 ${module.id} 时出错:`, error); return { ...module, document_types: [] }; } }) ); console.log('✅ [getEntryModules] 入口模块数据(含文档类型):', JSON.stringify(modulesWithTypes)); // 默认会多加一个 智慧法务大模型 入口 默认所有人都可以用,看到 modulesWithTypes.push({ "id": 0, "name": "智慧法务大模型", "description": "智慧法务大模型", "path": "entryModule/assistant", "areas": [], "created_at": "2025-11-18T21:33:33.857417+08:00", "updated_at": "2025-11-18T22:28:51.819722+08:00", "document_types": [ { "id": 0, "name": "空", "code": "空" } ] }) return modulesWithTypes; } catch (error) { console.error('❌ [getEntryModules] 获取入口模块失败:', error instanceof Error ? error.message : String(error)); return []; } }