// client/ai/pub/review.js // Markdown 转 HTML function mdToHtml(md) { if (!md) return ''; let html = md; // 标题 html = html.replace(/^####\s+(.+)$/gm, '
$1
'); html = html.replace(/^###\s+(.+)$/gm, '

$1

'); html = html.replace(/^##\s+(.+)$/gm, '

$1

'); html = html.replace(/^#\s+(.+)$/gm, '

$1

'); // 加粗 html = html.replace(/\*\*(.*?)\*\*/g, '$1'); // 链接 html = html.replace(/(https?:\/\/[^\s\)\]\"\<]+)/g, '$1'); // 代码块 html = html.replace(/```[\w]*\n([\s\S]*?)```/g, '
$1
'); // 行内代码 html = html.replace(/`([^`]+)`/g, '$1'); // 列表 html = html.replace(/^[\-\•]\s+(.+)$/gm, '
  • $1
  • '); html = html.replace(/^(\d+)\.\s+(.+)$/gm, '
  • $1. $2
  • '); // 换行 html = html.replace(/\n\n/g, '

    '); html = html.replace(/\n/g, '
    '); html = '

    ' + html + '

    '; // 清理空段落 html = html.replace(/

    \s*<\/p>/g, ''); return html; } // 生成精美 HTML 报告 function generateHtmlReport(markdown, fileName, reportDate, info) { const riskLevel = info.risk_level || '待评估'; const riskSummary = info.risk_summary || ''; const riskCount = info.risk_count || { high: 0, medium: 0, low: 0 }; const contractName = info.contract_name || fileName || '合同审查报告'; const contractType = info.contract_type || ''; // 风险等级颜色 let riskColor = '#67c23a'; if (riskLevel.includes('高')) riskColor = '#f56c6c'; else if (riskLevel.includes('中高')) riskColor = '#e6a23c'; else if (riskLevel.includes('中')) riskColor = '#e6a23c'; else if (riskLevel.includes('中低')) riskColor = '#409eff'; return ` ${contractName} - 审查报告

    📋 合同审查报告

    ${contractName} | ${reportDate}
    综合风险等级
    ${riskLevel}
    风险统计
    ${riskCount.high||0} / ${riskCount.medium||0} / ${riskCount.low||0}
    合同类型
    ${contractType || '待识别'}

    总体评估

    ${riskSummary || '暂无总体评估'}

    详细审查意见

    ${mdToHtml(markdown.replace(/```json[\s\S]*?```/, ''))}
    ⚠️ 免责声明:本报告由 AI 系统自动生成,仅供参考,不构成正式法律意见。涉及重大金额或复杂交易的合同,建议咨询执业律师。
    `; } module.exports = { main: async (event) => { let { data = {}, userInfo } = event; const COZE_TOKEN = 'sat_OiXlI4Da5Q99fxmul6pNfExmA6hR898UbxBDwrvjZ638APxRSlFnXbz6zyFedKNG'; const BOT_ID = '7689821072762011698'; const db = uniCloud.database(); const _ = db.command; let fileId = data.file_id; // ===== 模式1:上传文件 + 创建待审查记录 ===== if (data.upload_only && data.file_base64) { const fileBuffer = Buffer.from(data.file_base64, 'base64'); const boundary = '----FormBoundary' + Date.now(); const header = Buffer.from( '--' + boundary + '\r\n' + 'Content-Disposition: form-data; name="file"; filename="' + (data.file_name || 'file') + '"\r\n' + 'Content-Type: application/octet-stream\r\n\r\n' ); const footer = Buffer.from('\r\n--' + boundary + '--\r\n'); const uploadBody = Buffer.concat([header, fileBuffer, footer]); try { const uploadRes = await uniCloud.httpclient.request('https://api.coze.cn/v1/files/upload', { method: 'POST', dataType: 'json', headers: { 'Authorization': 'Bearer ' + COZE_TOKEN, 'Content-Type': 'multipart/form-data; boundary=' + boundary, 'Content-Length': uploadBody.length.toString() }, content: uploadBody, timeout: 600000 }); if (uploadRes.data.code !== 0) { return { code: -1, msg: '文件上传失败: ' + uploadRes.data.msg }; } fileId = uploadRes.data.data.id; // 同时上传到 uniCloud 云存储(用于查看/下载) let fileUrl = ''; try { const cloudPath = 'review-files/' + Date.now() + '_' + (data.file_name || 'file'); const uploadFileRes = await uniCloud.uploadFile({ cloudPath: cloudPath, fileContent: fileBuffer }); if (uploadFileRes.fileID) { const urlRes = await uniCloud.getTempFileURL({ fileList: [uploadFileRes.fileID] }); if (urlRes.fileList && urlRes.fileList[0]) { fileUrl = urlRes.fileList[0].tempFileURL; } } } catch (e) { console.log('云存储上传失败(不影响审查):', e.message); } // 创建待审查记录 const recordRes = await db.collection('jjx-review-records').add({ user_id: (data && data.uid) || 'anonymous', file_name: data.file_name || '', file_size: data.file_size || 0, file_id: fileId, file_url: fileUrl, chat_id: '', conversation_id: '', status: 'uploaded', result: '', points_used: 0, _add_time: new Date(), update_time: new Date() }); return { code: 0, msg: 'ok', data: { file_id: fileId, record_id: recordRes.id, file_url: fileUrl } }; } catch (e) { return { code: -1, msg: '上传异常: ' + e.message }; } } // ===== 模式2:发起审查(只更新状态,立即返回)===== if (!fileId) return { code: -1, msg: '缺少 file_id' }; const recordId = data.record_id; if (!recordId) return { code: -1, msg: '缺少 record_id' }; // 检查当前状态 const record = await db.collection('jjx-review-records').doc(recordId).get(); if (record.data.length) { const rec = record.data[0]; // 已完成,直接返回结果 if (rec.status === 'completed') { return { code: 0, msg: 'ok', data: { record_id: recordId, status: 'completed', result: rec.result, points_used: rec.points_used, html_url: rec.html_url || '', contract_info: rec.contract_info || {} } }; } // 正在审查中,直接返回 if (rec.status === 'reviewing') { return { code: 0, msg: 'ok', data: { record_id: recordId, status: 'reviewing' } }; } // 失败,返回错误 if (rec.status === 'failed') { return { code: 0, msg: 'ok', data: { record_id: recordId, status: 'failed', result: rec.result || '审查失败' } }; } } // 更新状态为审查中 await db.collection('jjx-review-records').doc(recordId).update({ file_id: fileId, file_name: data.file_name || '', file_size: data.file_size || 0, status: 'reviewing', review_started_at: new Date(), update_time: new Date() }); // ===== 立即返回,审查由定时任务触发 ===== return { code: 0, msg: 'ok', data: { record_id: recordId, status: 'reviewing' } }; } };