collabdocchat 2.4.4 → 2.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/bin/cli.js +1 -1
  2. package/package.json +4 -2
  3. package/scripts/cleanup-scripts.js +140 -0
  4. package/scripts/fix-startup-issues.js +136 -0
  5. package/scripts/start-app.js +11 -5
  6. package/scripts/start-simple.js +96 -0
  7. package/server/index.js +4 -0
  8. package/server/index.js.bak +97 -0
  9. package/server/models/Document.js +5 -0
  10. package/server/models/KnowledgeBase.js +259 -254
  11. package/server/models/Poll.js +97 -0
  12. package/server/routes/ai.js +391 -327
  13. package/server/routes/audit.js +61 -0
  14. package/server/routes/documents.js +74 -5
  15. package/server/routes/export.js +171 -10
  16. package/server/routes/files.js +27 -4
  17. package/server/routes/knowledge.js +31 -22
  18. package/server/routes/messages.js +142 -0
  19. package/server/routes/polls.js +241 -0
  20. package/server/routes/tasks.js +1 -0
  21. package/server/routes/workflows.js +27 -0
  22. package/server/utils/auditLogger.js +268 -238
  23. package/src/pages/admin-dashboard.js +1431 -335
  24. package/src/pages/admin-dashboard.js.audit-optimize.bak +4134 -0
  25. package/src/pages/admin-dashboard.js.bak +4041 -0
  26. package/src/pages/admin-dashboard.js.broken.bak +4099 -0
  27. package/src/pages/admin-dashboard.js.comprehensive.bak +4099 -0
  28. package/src/pages/admin-dashboard.js.escape.bak +4099 -0
  29. package/src/pages/admin-dashboard.js.final-final-fix.bak +4099 -0
  30. package/src/pages/admin-dashboard.js.final-fix.bak +4099 -0
  31. package/src/pages/admin-dashboard.js.final.bak +4099 -0
  32. package/src/pages/admin-dashboard.js.indent-fix.bak +4099 -0
  33. package/src/pages/admin-dashboard.js.last-fix.bak +4099 -0
  34. package/src/pages/admin-dashboard.js.line595-fix.bak +4099 -0
  35. package/src/pages/admin-dashboard.js.pre-manual-fix.bak +4099 -0
  36. package/src/pages/admin-dashboard.js.syntax.bak +4099 -0
  37. package/src/pages/admin-dashboard.js.test.bak +4099 -0
  38. package/src/pages/optimized-task-detail-original.js +838 -0
  39. package/src/pages/optimized-task-detail.js +324 -22
  40. package/src/pages/optimized-task-detail.js.bak +1162 -0
  41. package/src/pages/poll-detail-enhanced.js +394 -0
  42. package/src/pages/update-poll-display.js +380 -0
  43. package/src/pages/user-dashboard.js +1860 -1006
  44. package/src/services/api.js +326 -265
  45. package/src/services/auth.js +54 -54
  46. package/src/services/websocket.js +88 -80
  47. package/scripts/add-button-hover.js +0 -56
  48. package/scripts/add-missing-functions.js +0 -66
  49. package/scripts/add-more-features.js +0 -427
  50. package/scripts/add-user-functions.js +0 -201
  51. package/scripts/auto-publish.js +0 -63
  52. package/scripts/beautify-buttons.js +0 -45
  53. package/scripts/beautify-ui.js +0 -267
  54. package/scripts/check-encoding.js +0 -41
  55. package/scripts/check-syntax.js +0 -54
  56. package/scripts/find-buttons.js +0 -20
  57. package/scripts/find-duplicate.js +0 -35
  58. package/scripts/find-sidebar-buttons.js +0 -21
  59. package/scripts/fix-help.js +0 -274
  60. package/scripts/fix-issues-step1.js +0 -73
  61. package/scripts/fix-issues-step2.js +0 -93
  62. package/scripts/fix-issues-step3.js +0 -155
  63. package/scripts/fix-issues-step4.js +0 -150
  64. package/scripts/fix-optimized-views.js +0 -37
  65. package/scripts/fix-ports.js +0 -77
  66. package/scripts/fix-settings.js +0 -258
  67. package/scripts/fix-user-dashboard.js +0 -62
  68. package/scripts/fix-workflow.js +0 -110
  69. package/scripts/refactor-step1.js +0 -32
  70. package/scripts/refactor-step2.js +0 -255
  71. package/scripts/refactor-step3.js +0 -137
  72. package/scripts/refactor-step4.js +0 -183
  73. package/scripts/refactor-step5.js +0 -181
  74. package/scripts/refactor-step6.js +0 -254
  75. package/scripts/refactor-step7.js +0 -291
  76. package/scripts/remove-bom.js +0 -69
  77. package/scripts/remove-quill-from-user-dashboard.js +0 -49
  78. package/scripts/remove-quill-imports-only.js +0 -32
  79. package/scripts/update-port-user.js +0 -21
  80. package/scripts/update-port.js +0 -22
  81. package/src/pages/simplified-workflows.js +0 -652
  82. package/src/utils/ai-assistant.js +0 -1384
@@ -1,327 +1,391 @@
1
- // AI 助手路由(示例实现)
2
- import express from 'express';
3
- import { authenticate } from '../middleware/auth.js';
4
- import { asyncHandler, ValidationError } from '../middleware/errorHandler.js';
5
-
6
- const router = express.Router();
7
-
8
- // 注意:这是一个示例实现
9
- // 生产环境需要接入真实的AI服务(OpenAI、百度文心一言、阿里通义千问等)
10
-
11
- // 模拟AI响应延迟
12
- const simulateAIDelay = () => new Promise(resolve => setTimeout(resolve, 500 + Math.random() * 1000));
13
-
14
- // 智能回复
15
- router.post('/reply', authenticate, asyncHandler(async (req, res) => {
16
- const { context, message } = req.body;
17
-
18
- if (!message) {
19
- throw new ValidationError('消息内容不能为空');
20
- }
21
-
22
- await simulateAIDelay();
23
-
24
- // TODO: 接入真实AI服务
25
- // 示例:使用 OpenAI
26
- /*
27
- import OpenAI from 'openai';
28
- const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
29
-
30
- const completion = await openai.chat.completions.create({
31
- model: "gpt-3.5-turbo",
32
- messages: [
33
- { role: "system", content: "你是一个协作助手,帮助用户更好地沟通" },
34
- { role: "user", content: `上下文: ${context}\n消息: ${message}` }
35
- ]
36
- });
37
-
38
- const reply = completion.choices[0].message.content;
39
- */
40
-
41
- // 模拟回复
42
- const replies = [
43
- '感谢您的反馈,我会认真考虑您的建议。',
44
- '这是一个很好的想法,让我们一起讨论一下具体实施方案。',
45
- '我理解您的观点,不过我们可能需要从另一个角度来看待这个问题。',
46
- '收到,我会尽快处理这个事项并及时反馈进展。',
47
- '好的,让我们安排一个时间详细讨论这个话题。'
48
- ];
49
-
50
- const reply = replies[Math.floor(Math.random() * replies.length)];
51
-
52
- res.json({
53
- success: true,
54
- data: {
55
- reply,
56
- confidence: 0.85,
57
- suggestions: [reply]
58
- }
59
- });
60
- }));
61
-
62
- // 内容摘要
63
- router.post('/summarize', authenticate, asyncHandler(async (req, res) => {
64
- const { content } = req.body;
65
-
66
- if (!content || content.length < 50) {
67
- throw new ValidationError('内容太短,无法生成摘要');
68
- }
69
-
70
- await simulateAIDelay();
71
-
72
- // TODO: 接入真实AI服务
73
- /*
74
- const completion = await openai.chat.completions.create({
75
- model: "gpt-3.5-turbo",
76
- messages: [
77
- { role: "system", content: "请总结以下内容的关键要点,用3-5个要点列出" },
78
- { role: "user", content }
79
- ]
80
- });
81
-
82
- const summary = completion.choices[0].message.content;
83
- */
84
-
85
- // 模拟摘要
86
- const sentences = content.split(/[。!?.!?]+/).filter(s => s.trim());
87
- const keyPoints = sentences.slice(0, Math.min(3, sentences.length)).map((s, i) => `${i + 1}. ${s.trim()}`);
88
-
89
- res.json({
90
- success: true,
91
- data: {
92
- summary: keyPoints.join('\n'),
93
- keyPoints,
94
- wordCount: content.length
95
- }
96
- });
97
- }));
98
-
99
- // 语法检查
100
- router.post('/grammar-check', authenticate, asyncHandler(async (req, res) => {
101
- const { text } = req.body;
102
-
103
- if (!text) {
104
- throw new ValidationError('文本内容不能为空');
105
- }
106
-
107
- await simulateAIDelay();
108
-
109
- // 简单的语法检查规则
110
- const errors = [];
111
-
112
- // 检查重复词
113
- const words = text.split(/\s+/);
114
- for (let i = 0; i < words.length - 1; i++) {
115
- if (words[i] === words[i + 1] && words[i].length > 1) {
116
- errors.push({
117
- type: 'repetition',
118
- message: `重复的词: "${words[i]}"`,
119
- position: i,
120
- suggestion: words[i]
121
- });
122
- }
123
- }
124
-
125
- // 检查标点符号
126
- if (!/[。!?.!?]$/.test(text.trim())) {
127
- errors.push({
128
- type: 'punctuation',
129
- message: '句子末尾缺少标点符号',
130
- position: text.length - 1,
131
- suggestion: '添加句号、问号或感叹号'
132
- });
133
- }
134
-
135
- // 检查空格
136
- if (/\s{2,}/.test(text)) {
137
- errors.push({
138
- type: 'spacing',
139
- message: '存在多余的空格',
140
- suggestion: '删除多余空格'
141
- });
142
- }
143
-
144
- res.json({
145
- success: true,
146
- data: {
147
- hasErrors: errors.length > 0,
148
- errors,
149
- correctedText: text.replace(/\s{2,}/g, ' ').trim()
150
- }
151
- });
152
- }));
153
-
154
- // 翻译
155
- router.post('/translate', authenticate, asyncHandler(async (req, res) => {
156
- const { text, targetLang = 'en' } = req.body;
157
-
158
- if (!text) {
159
- throw new ValidationError('文本内容不能为空');
160
- }
161
-
162
- await simulateAIDelay();
163
-
164
- // TODO: 接入真实翻译服务
165
- /*
166
- // 使用 OpenAI
167
- const completion = await openai.chat.completions.create({
168
- model: "gpt-3.5-turbo",
169
- messages: [
170
- { role: "system", content: `请将以下文本翻译为${targetLang === 'en' ? '英语' : '中文'}` },
171
- { role: "user", content: text }
172
- ]
173
- });
174
-
175
- const translation = completion.choices[0].message.content;
176
-
177
- // 或使用专业翻译API(百度翻译、有道翻译等)
178
- */
179
-
180
- // 模拟翻译
181
- const translations = {
182
- 'zh': {
183
- 'Hello': '你好',
184
- 'Thank you': '谢谢',
185
- 'Good morning': '早上好'
186
- },
187
- 'en': {
188
- '你好': 'Hello',
189
- '谢谢': 'Thank you',
190
- '早上好': 'Good morning'
191
- }
192
- };
193
-
194
- const translation = translations[targetLang]?.[text] || `[${targetLang}] ${text}`;
195
-
196
- res.json({
197
- success: true,
198
- data: {
199
- originalText: text,
200
- translatedText: translation,
201
- sourceLang: targetLang === 'en' ? 'zh' : 'en',
202
- targetLang
203
- }
204
- });
205
- }));
206
-
207
- // 情感分析
208
- router.post('/sentiment', authenticate, asyncHandler(async (req, res) => {
209
- const { text } = req.body;
210
-
211
- if (!text) {
212
- throw new ValidationError('文本内容不能为空');
213
- }
214
-
215
- await simulateAIDelay();
216
-
217
- // 简单的情感分析
218
- const positiveWords = ['好', '棒', '优秀', '喜欢', '开心', '高兴', '满意', '赞', 'good', 'great', 'excellent', 'happy', 'love'];
219
- const negativeWords = ['', '糟', '不好', '讨厌', '难过', '失望', '生气', 'bad', 'terrible', 'hate', 'sad', 'angry'];
220
-
221
- let positiveCount = 0;
222
- let negativeCount = 0;
223
-
224
- const lowerText = text.toLowerCase();
225
- positiveWords.forEach(word => {
226
- if (lowerText.includes(word)) positiveCount++;
227
- });
228
- negativeWords.forEach(word => {
229
- if (lowerText.includes(word)) negativeCount++;
230
- });
231
-
232
- let sentiment = 'neutral';
233
- let score = 0;
234
-
235
- if (positiveCount > negativeCount) {
236
- sentiment = 'positive';
237
- score = Math.min(0.9, 0.5 + (positiveCount - negativeCount) * 0.1);
238
- } else if (negativeCount > positiveCount) {
239
- sentiment = 'negative';
240
- score = Math.max(-0.9, -0.5 - (negativeCount - positiveCount) * 0.1);
241
- }
242
-
243
- res.json({
244
- success: true,
245
- data: {
246
- sentiment,
247
- score,
248
- confidence: 0.75,
249
- details: {
250
- positive: positiveCount,
251
- negative: negativeCount,
252
- neutral: positiveCount === negativeCount
253
- }
254
- }
255
- });
256
- }));
257
-
258
- // 关键词提取
259
- router.post('/keywords', authenticate, asyncHandler(async (req, res) => {
260
- const { text, maxKeywords = 5 } = req.body;
261
-
262
- if (!text) {
263
- throw new ValidationError('文本内容不能为空');
264
- }
265
-
266
- await simulateAIDelay();
267
-
268
- // 简单的关键词提取(基于词频)
269
- const stopWords = new Set(['的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', 'the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'or', 'but']);
270
-
271
- const words = text.toLowerCase()
272
- .replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s]/g, ' ')
273
- .split(/\s+/)
274
- .filter(word => word.length > 1 && !stopWords.has(word));
275
-
276
- const wordFreq = {};
277
- words.forEach(word => {
278
- wordFreq[word] = (wordFreq[word] || 0) + 1;
279
- });
280
-
281
- const keywords = Object.entries(wordFreq)
282
- .sort((a, b) => b[1] - a[1])
283
- .slice(0, maxKeywords)
284
- .map(([word, freq]) => ({
285
- word,
286
- frequency: freq,
287
- relevance: Math.min(1, freq / words.length * 10)
288
- }));
289
-
290
- res.json({
291
- success: true,
292
- data: {
293
- keywords,
294
- totalWords: words.length,
295
- uniqueWords: Object.keys(wordFreq).length
296
- }
297
- });
298
- }));
299
-
300
- // 文本补全
301
- router.post('/complete', authenticate, asyncHandler(async (req, res) => {
302
- const { text, maxSuggestions = 3 } = req.body;
303
-
304
- if (!text) {
305
- throw new ValidationError('文本内容不能为空');
306
- }
307
-
308
- await simulateAIDelay();
309
-
310
- // 模拟文本补全
311
- const suggestions = [
312
- text + ',让我们一起努力完成这个目标。',
313
- text + ',期待您的反馈和建议。',
314
- text + ',如有任何问题请随时联系我。'
315
- ].slice(0, maxSuggestions);
316
-
317
- res.json({
318
- success: true,
319
- data: {
320
- suggestions,
321
- originalText: text
322
- }
323
- });
324
- }));
325
-
326
- export default router;
327
-
1
+ // AI 助手路由(示例实现)
2
+ import express from 'express';
3
+ import { authenticate } from '../middleware/auth.js';
4
+ import { asyncHandler, ValidationError } from '../middleware/errorHandler.js';
5
+
6
+ const router = express.Router();
7
+
8
+ // 注意:这是一个示例实现
9
+ // 生产环境需要接入真实的AI服务(OpenAI、百度文心一言、阿里通义千问等)
10
+
11
+ // 模拟AI响应延迟
12
+ const simulateAIDelay = () => new Promise(resolve => setTimeout(resolve, 500 + Math.random() * 1000));
13
+
14
+ // AI问答(通用问答接口)
15
+ router.post('/ask', authenticate, asyncHandler(async (req, res) => {
16
+ const { question, groupId } = req.body;
17
+
18
+ if (!question) {
19
+ throw new ValidationError('问题内容不能为空');
20
+ }
21
+
22
+ await simulateAIDelay();
23
+
24
+ // TODO: 接入真实AI服务
25
+ // 示例:使用 OpenAI
26
+ /*
27
+ import OpenAI from 'openai';
28
+ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
29
+
30
+ const completion = await openai.chat.completions.create({
31
+ model: "gpt-3.5-turbo",
32
+ messages: [
33
+ { role: "system", content: "你是一个协作助手,帮助用户解答问题" },
34
+ { role: "user", content: question }
35
+ ]
36
+ });
37
+
38
+ const answer = completion.choices[0].message.content;
39
+ */
40
+
41
+ // 模拟智能回答
42
+ const answers = {
43
+ '如何创建文档': '您可以点击"共享文档"页面的"创建文档"按钮,填写文档标题和内容,选择可编辑的成员,然后点击"创建"即可。',
44
+ '如何邀请成员': '在群组管理页面,点击"添加成员"按钮,选择要邀请的用户,然后点击"确定"即可将成员添加到群组。',
45
+ '如何使用工作流': '进入"工作流"页面,点击"创建工作流"按钮,选择触发条件和执行动作,配置相关参数后保存即可。工作流会自动在满足条件时执行。',
46
+ '如何备份数据': '进入"数据导出"页面,选择要导出的数据类型(文档、任务、消息等),点击"导出"按钮即可下载备份文件。系统也支持自动备份功能。'
47
+ };
48
+
49
+ // 简单的关键词匹配
50
+ let answer = null;
51
+ for (const [key, value] of Object.entries(answers)) {
52
+ if (question.includes(key) || question.includes(key.replace('如何', ''))) {
53
+ answer = value;
54
+ break;
55
+ }
56
+ }
57
+
58
+ // 如果没有匹配到,返回通用回答
59
+ if (!answer) {
60
+ const genericAnswers = [
61
+ '这是一个很好的问题。根据您的描述,我建议您查看相关文档或联系管理员获取更详细的帮助。',
62
+ '感谢您的提问。您可以在帮助中心查找相关信息,或者在群组中向其他成员请教。',
63
+ '关于这个问题,建议您先查看系统的使用指南。如果还有疑问,可以在群聊中提问,会有人帮助您。',
64
+ '我理解您的需求。您可以尝试在搜索功能中查找相关内容,或者查看操作记录了解其他用户是如何操作的。',
65
+ '这个问题涉及到具体的使用场景。建议您先尝试相关功能,如果遇到困难,可以随时在群组中寻求帮助。'
66
+ ];
67
+ answer = genericAnswers[Math.floor(Math.random() * genericAnswers.length)];
68
+ }
69
+
70
+ res.json({
71
+ success: true,
72
+ answer,
73
+ confidence: answer === null ? 0.5 : 0.85,
74
+ relatedTopics: ['文档管理', '群组协作', '工作流', '数据备份']
75
+ });
76
+ }));
77
+
78
+ // 智能回复
79
+ router.post('/reply', authenticate, asyncHandler(async (req, res) => {
80
+ const { context, message } = req.body;
81
+
82
+ if (!message) {
83
+ throw new ValidationError('消息内容不能为空');
84
+ }
85
+
86
+ await simulateAIDelay();
87
+
88
+ // TODO: 接入真实AI服务
89
+ // 示例:使用 OpenAI
90
+ /*
91
+ import OpenAI from 'openai';
92
+ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
93
+
94
+ const completion = await openai.chat.completions.create({
95
+ model: "gpt-3.5-turbo",
96
+ messages: [
97
+ { role: "system", content: "你是一个协作助手,帮助用户更好地沟通" },
98
+ { role: "user", content: `上下文: ${context}\n消息: ${message}` }
99
+ ]
100
+ });
101
+
102
+ const reply = completion.choices[0].message.content;
103
+ */
104
+
105
+ // 模拟回复
106
+ const replies = [
107
+ '感谢您的反馈,我会认真考虑您的建议。',
108
+ '这是一个很好的想法,让我们一起讨论一下具体实施方案。',
109
+ '我理解您的观点,不过我们可能需要从另一个角度来看待这个问题。',
110
+ '收到,我会尽快处理这个事项并及时反馈进展。',
111
+ '好的,让我们安排一个时间详细讨论这个话题。'
112
+ ];
113
+
114
+ const reply = replies[Math.floor(Math.random() * replies.length)];
115
+
116
+ res.json({
117
+ success: true,
118
+ data: {
119
+ reply,
120
+ confidence: 0.85,
121
+ suggestions: [reply]
122
+ }
123
+ });
124
+ }));
125
+
126
+ // 内容摘要
127
+ router.post('/summarize', authenticate, asyncHandler(async (req, res) => {
128
+ const { content } = req.body;
129
+
130
+ if (!content || content.length < 50) {
131
+ throw new ValidationError('内容太短,无法生成摘要');
132
+ }
133
+
134
+ await simulateAIDelay();
135
+
136
+ // TODO: 接入真实AI服务
137
+ /*
138
+ const completion = await openai.chat.completions.create({
139
+ model: "gpt-3.5-turbo",
140
+ messages: [
141
+ { role: "system", content: "请总结以下内容的关键要点,用3-5个要点列出" },
142
+ { role: "user", content }
143
+ ]
144
+ });
145
+
146
+ const summary = completion.choices[0].message.content;
147
+ */
148
+
149
+ // 模拟摘要
150
+ const sentences = content.split(/[。!?.!?]+/).filter(s => s.trim());
151
+ const keyPoints = sentences.slice(0, Math.min(3, sentences.length)).map((s, i) => `${i + 1}. ${s.trim()}`);
152
+
153
+ res.json({
154
+ success: true,
155
+ data: {
156
+ summary: keyPoints.join('\n'),
157
+ keyPoints,
158
+ wordCount: content.length
159
+ }
160
+ });
161
+ }));
162
+
163
+ // 语法检查
164
+ router.post('/grammar-check', authenticate, asyncHandler(async (req, res) => {
165
+ const { text } = req.body;
166
+
167
+ if (!text) {
168
+ throw new ValidationError('文本内容不能为空');
169
+ }
170
+
171
+ await simulateAIDelay();
172
+
173
+ // 简单的语法检查规则
174
+ const errors = [];
175
+
176
+ // 检查重复词
177
+ const words = text.split(/\s+/);
178
+ for (let i = 0; i < words.length - 1; i++) {
179
+ if (words[i] === words[i + 1] && words[i].length > 1) {
180
+ errors.push({
181
+ type: 'repetition',
182
+ message: `重复的词: "${words[i]}"`,
183
+ position: i,
184
+ suggestion: words[i]
185
+ });
186
+ }
187
+ }
188
+
189
+ // 检查标点符号
190
+ if (!/[。!?.!?]$/.test(text.trim())) {
191
+ errors.push({
192
+ type: 'punctuation',
193
+ message: '句子末尾缺少标点符号',
194
+ position: text.length - 1,
195
+ suggestion: '添加句号、问号或感叹号'
196
+ });
197
+ }
198
+
199
+ // 检查空格
200
+ if (/\s{2,}/.test(text)) {
201
+ errors.push({
202
+ type: 'spacing',
203
+ message: '存在多余的空格',
204
+ suggestion: '删除多余空格'
205
+ });
206
+ }
207
+
208
+ res.json({
209
+ success: true,
210
+ data: {
211
+ hasErrors: errors.length > 0,
212
+ errors,
213
+ correctedText: text.replace(/\s{2,}/g, ' ').trim()
214
+ }
215
+ });
216
+ }));
217
+
218
+ // 翻译
219
+ router.post('/translate', authenticate, asyncHandler(async (req, res) => {
220
+ const { text, targetLang = 'en' } = req.body;
221
+
222
+ if (!text) {
223
+ throw new ValidationError('文本内容不能为空');
224
+ }
225
+
226
+ await simulateAIDelay();
227
+
228
+ // TODO: 接入真实翻译服务
229
+ /*
230
+ // 使用 OpenAI
231
+ const completion = await openai.chat.completions.create({
232
+ model: "gpt-3.5-turbo",
233
+ messages: [
234
+ { role: "system", content: `请将以下文本翻译为${targetLang === 'en' ? '英语' : '中文'}` },
235
+ { role: "user", content: text }
236
+ ]
237
+ });
238
+
239
+ const translation = completion.choices[0].message.content;
240
+
241
+ // 或使用专业翻译API(百度翻译、有道翻译等)
242
+ */
243
+
244
+ // 模拟翻译
245
+ const translations = {
246
+ 'zh': {
247
+ 'Hello': '你好',
248
+ 'Thank you': '谢谢',
249
+ 'Good morning': '早上好'
250
+ },
251
+ 'en': {
252
+ '你好': 'Hello',
253
+ '谢谢': 'Thank you',
254
+ '早上好': 'Good morning'
255
+ }
256
+ };
257
+
258
+ const translation = translations[targetLang]?.[text] || `[${targetLang}] ${text}`;
259
+
260
+ res.json({
261
+ success: true,
262
+ data: {
263
+ originalText: text,
264
+ translatedText: translation,
265
+ sourceLang: targetLang === 'en' ? 'zh' : 'en',
266
+ targetLang
267
+ }
268
+ });
269
+ }));
270
+
271
+ // 情感分析
272
+ router.post('/sentiment', authenticate, asyncHandler(async (req, res) => {
273
+ const { text } = req.body;
274
+
275
+ if (!text) {
276
+ throw new ValidationError('文本内容不能为空');
277
+ }
278
+
279
+ await simulateAIDelay();
280
+
281
+ // 简单的情感分析
282
+ const positiveWords = ['好', '棒', '优秀', '喜欢', '开心', '高兴', '满意', '赞', 'good', 'great', 'excellent', 'happy', 'love'];
283
+ const negativeWords = ['差', '糟', '不好', '讨厌', '难过', '失望', '生气', 'bad', 'terrible', 'hate', 'sad', 'angry'];
284
+
285
+ let positiveCount = 0;
286
+ let negativeCount = 0;
287
+
288
+ const lowerText = text.toLowerCase();
289
+ positiveWords.forEach(word => {
290
+ if (lowerText.includes(word)) positiveCount++;
291
+ });
292
+ negativeWords.forEach(word => {
293
+ if (lowerText.includes(word)) negativeCount++;
294
+ });
295
+
296
+ let sentiment = 'neutral';
297
+ let score = 0;
298
+
299
+ if (positiveCount > negativeCount) {
300
+ sentiment = 'positive';
301
+ score = Math.min(0.9, 0.5 + (positiveCount - negativeCount) * 0.1);
302
+ } else if (negativeCount > positiveCount) {
303
+ sentiment = 'negative';
304
+ score = Math.max(-0.9, -0.5 - (negativeCount - positiveCount) * 0.1);
305
+ }
306
+
307
+ res.json({
308
+ success: true,
309
+ data: {
310
+ sentiment,
311
+ score,
312
+ confidence: 0.75,
313
+ details: {
314
+ positive: positiveCount,
315
+ negative: negativeCount,
316
+ neutral: positiveCount === negativeCount
317
+ }
318
+ }
319
+ });
320
+ }));
321
+
322
+ // 关键词提取
323
+ router.post('/keywords', authenticate, asyncHandler(async (req, res) => {
324
+ const { text, maxKeywords = 5 } = req.body;
325
+
326
+ if (!text) {
327
+ throw new ValidationError('文本内容不能为空');
328
+ }
329
+
330
+ await simulateAIDelay();
331
+
332
+ // 简单的关键词提取(基于词频)
333
+ const stopWords = new Set(['的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', 'the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'or', 'but']);
334
+
335
+ const words = text.toLowerCase()
336
+ .replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s]/g, ' ')
337
+ .split(/\s+/)
338
+ .filter(word => word.length > 1 && !stopWords.has(word));
339
+
340
+ const wordFreq = {};
341
+ words.forEach(word => {
342
+ wordFreq[word] = (wordFreq[word] || 0) + 1;
343
+ });
344
+
345
+ const keywords = Object.entries(wordFreq)
346
+ .sort((a, b) => b[1] - a[1])
347
+ .slice(0, maxKeywords)
348
+ .map(([word, freq]) => ({
349
+ word,
350
+ frequency: freq,
351
+ relevance: Math.min(1, freq / words.length * 10)
352
+ }));
353
+
354
+ res.json({
355
+ success: true,
356
+ data: {
357
+ keywords,
358
+ totalWords: words.length,
359
+ uniqueWords: Object.keys(wordFreq).length
360
+ }
361
+ });
362
+ }));
363
+
364
+ // 文本补全
365
+ router.post('/complete', authenticate, asyncHandler(async (req, res) => {
366
+ const { text, maxSuggestions = 3 } = req.body;
367
+
368
+ if (!text) {
369
+ throw new ValidationError('文本内容不能为空');
370
+ }
371
+
372
+ await simulateAIDelay();
373
+
374
+ // 模拟文本补全
375
+ const suggestions = [
376
+ text + ',让我们一起努力完成这个目标。',
377
+ text + ',期待您的反馈和建议。',
378
+ text + ',如有任何问题请随时联系我。'
379
+ ].slice(0, maxSuggestions);
380
+
381
+ res.json({
382
+ success: true,
383
+ data: {
384
+ suggestions,
385
+ originalText: text
386
+ }
387
+ });
388
+ }));
389
+
390
+ export default router;
391
+