collabdocchat 1.2.13 → 2.0.0

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 (62) hide show
  1. package/README.md +219 -218
  2. package/index.html +2 -0
  3. package/install-and-start.bat +5 -0
  4. package/install-and-start.sh +5 -0
  5. package/package.json +9 -2
  6. package/scripts/pre-publish-check.js +213 -0
  7. package/scripts/start-app.js +15 -15
  8. package/server/index.js +38 -6
  9. package/server/middleware/cache.js +115 -0
  10. package/server/middleware/errorHandler.js +209 -0
  11. package/server/models/Document.js +66 -59
  12. package/server/models/File.js +49 -43
  13. package/server/models/Group.js +6 -0
  14. package/server/models/KnowledgeBase.js +254 -0
  15. package/server/models/Message.js +43 -0
  16. package/server/models/Task.js +87 -55
  17. package/server/models/User.js +67 -60
  18. package/server/models/Workflow.js +249 -0
  19. package/server/routes/ai.js +327 -0
  20. package/server/routes/audit.js +245 -210
  21. package/server/routes/backup.js +108 -0
  22. package/server/routes/chunked-upload.js +343 -0
  23. package/server/routes/export.js +440 -0
  24. package/server/routes/files.js +294 -218
  25. package/server/routes/groups.js +182 -0
  26. package/server/routes/knowledge.js +509 -0
  27. package/server/routes/tasks.js +257 -110
  28. package/server/routes/workflows.js +380 -0
  29. package/server/utils/backup.js +439 -0
  30. package/server/utils/cache.js +223 -0
  31. package/server/utils/workflow-engine.js +479 -0
  32. package/server/websocket/enhanced.js +509 -0
  33. package/server/websocket/index.js +233 -1
  34. package/src/components/knowledge-modal.js +485 -0
  35. package/src/components/optimized-poll-detail.js +724 -0
  36. package/src/main.js +5 -0
  37. package/src/pages/admin-dashboard.js +2248 -44
  38. package/src/pages/optimized-backup-view.js +616 -0
  39. package/src/pages/optimized-knowledge-view.js +803 -0
  40. package/src/pages/optimized-task-detail.js +843 -0
  41. package/src/pages/optimized-workflow-view.js +806 -0
  42. package/src/pages/simplified-workflows.js +651 -0
  43. package/src/pages/user-dashboard.js +677 -58
  44. package/src/services/api.js +65 -1
  45. package/src/services/auth.js +1 -1
  46. package/src/services/websocket.js +124 -16
  47. package/src/styles/collaboration-modern.js +708 -0
  48. package/src/styles/enhancements.css +392 -0
  49. package/src/styles/main.css +620 -1420
  50. package/src/styles/responsive.css +1000 -0
  51. package/src/styles/sidebar-fix.css +60 -0
  52. package/src/utils/ai-assistant.js +1398 -0
  53. package/src/utils/chat-enhancements.js +509 -0
  54. package/src/utils/collaboration-enhancer.js +1151 -0
  55. package/src/utils/feature-integrator.js +1724 -0
  56. package/src/utils/onboarding-guide.js +734 -0
  57. package/src/utils/performance.js +394 -0
  58. package/src/utils/permission-manager.js +890 -0
  59. package/src/utils/responsive-handler.js +491 -0
  60. package/src/utils/theme-manager.js +811 -0
  61. package/src/utils/ui-enhancements-loader.js +329 -0
  62. package/USAGE.md +0 -298
@@ -1,60 +1,67 @@
1
- import mongoose from 'mongoose';
2
- import bcrypt from 'bcryptjs';
3
-
4
- const userSchema = new mongoose.Schema({
5
- username: {
6
- type: String,
7
- required: true,
8
- unique: true,
9
- trim: true
10
- },
11
- email: {
12
- type: String,
13
- required: true,
14
- unique: true,
15
- lowercase: true
16
- },
17
- password: {
18
- type: String,
19
- required: true
20
- },
21
- role: {
22
- type: String,
23
- enum: ['admin', 'user'],
24
- default: 'user'
25
- },
26
- avatar: {
27
- type: String,
28
- default: ''
29
- },
30
- groups: [{
31
- type: mongoose.Schema.Types.ObjectId,
32
- ref: 'Group'
33
- }],
34
- isOnline: {
35
- type: Boolean,
36
- default: false
37
- },
38
- lastSeen: {
39
- type: Date,
40
- default: Date.now
41
- }
42
- }, {
43
- timestamps: true
44
- });
45
-
46
- // 密码加密
47
- userSchema.pre('save', async function(next) {
48
- if (!this.isModified('password')) return next();
49
- this.password = await bcrypt.hash(this.password, 10);
50
- next();
51
- });
52
-
53
- // 密码验证
54
- userSchema.methods.comparePassword = async function(candidatePassword) {
55
- return await bcrypt.compare(candidatePassword, this.password);
56
- };
57
-
58
- export default mongoose.model('User', userSchema);
59
-
60
-
1
+ import mongoose from 'mongoose';
2
+ import bcrypt from 'bcryptjs';
3
+
4
+ const userSchema = new mongoose.Schema({
5
+ username: {
6
+ type: String,
7
+ required: true,
8
+ unique: true,
9
+ trim: true
10
+ },
11
+ email: {
12
+ type: String,
13
+ required: true,
14
+ unique: true,
15
+ lowercase: true
16
+ },
17
+ password: {
18
+ type: String,
19
+ required: true
20
+ },
21
+ role: {
22
+ type: String,
23
+ enum: ['admin', 'user'],
24
+ default: 'user'
25
+ },
26
+ avatar: {
27
+ type: String,
28
+ default: ''
29
+ },
30
+ groups: [{
31
+ type: mongoose.Schema.Types.ObjectId,
32
+ ref: 'Group'
33
+ }],
34
+ isOnline: {
35
+ type: Boolean,
36
+ default: false
37
+ },
38
+ lastSeen: {
39
+ type: Date,
40
+ default: Date.now
41
+ }
42
+ }, {
43
+ timestamps: true
44
+ });
45
+
46
+ // 密码加密
47
+ userSchema.pre('save', async function(next) {
48
+ if (!this.isModified('password')) return next();
49
+ this.password = await bcrypt.hash(this.password, 10);
50
+ next();
51
+ });
52
+
53
+ // 密码验证
54
+ userSchema.methods.comparePassword = async function(candidatePassword) {
55
+ return await bcrypt.compare(candidatePassword, this.password);
56
+ };
57
+
58
+ // 索引优化
59
+ userSchema.index({ username: 1 }, { unique: true });
60
+ userSchema.index({ email: 1 }, { unique: true });
61
+ userSchema.index({ createdAt: -1 });
62
+ userSchema.index({ isOnline: 1, lastSeen: -1 });
63
+ userSchema.index({ role: 1 });
64
+
65
+ export default mongoose.model('User', userSchema);
66
+
67
+
@@ -0,0 +1,249 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const workflowSchema = new mongoose.Schema({
4
+ name: {
5
+ type: String,
6
+ required: true,
7
+ trim: true
8
+ },
9
+ description: {
10
+ type: String,
11
+ default: ''
12
+ },
13
+ group: {
14
+ type: mongoose.Schema.Types.ObjectId,
15
+ ref: 'Group',
16
+ required: true
17
+ },
18
+ creator: {
19
+ type: mongoose.Schema.Types.ObjectId,
20
+ ref: 'User',
21
+ required: true
22
+ },
23
+ // 触发器配置
24
+ trigger: {
25
+ type: {
26
+ type: String,
27
+ enum: ['manual', 'scheduled', 'event'],
28
+ default: 'manual'
29
+ },
30
+ // 事件类型:task_created, file_uploaded, message_sent 等
31
+ event: String,
32
+ // cron 表达式(用于定时触发)
33
+ schedule: String,
34
+ // 触发条件
35
+ conditions: [{
36
+ field: String,
37
+ operator: {
38
+ type: String,
39
+ enum: ['equals', 'not_equals', 'contains', 'greater_than', 'less_than', 'in', 'not_in']
40
+ },
41
+ value: mongoose.Schema.Types.Mixed
42
+ }]
43
+ },
44
+ // 工作流步骤
45
+ steps: [{
46
+ name: String,
47
+ type: {
48
+ type: String,
49
+ enum: ['approval', 'notification', 'assignment', 'webhook', 'delay', 'condition', 'script'],
50
+ required: true
51
+ },
52
+ order: Number,
53
+ // 步骤配置
54
+ config: {
55
+ // 审批配置
56
+ approvers: [{
57
+ type: mongoose.Schema.Types.ObjectId,
58
+ ref: 'User'
59
+ }],
60
+ approvalType: {
61
+ type: String,
62
+ enum: ['any', 'all', 'sequential']
63
+ },
64
+ // 通知配置
65
+ notificationType: {
66
+ type: String,
67
+ enum: ['email', 'system', 'webhook']
68
+ },
69
+ recipients: [{
70
+ type: mongoose.Schema.Types.ObjectId,
71
+ ref: 'User'
72
+ }],
73
+ message: String,
74
+ // 分配配置
75
+ assignee: {
76
+ type: mongoose.Schema.Types.ObjectId,
77
+ ref: 'User'
78
+ },
79
+ // Webhook 配置
80
+ url: String,
81
+ method: {
82
+ type: String,
83
+ enum: ['GET', 'POST', 'PUT', 'DELETE']
84
+ },
85
+ headers: mongoose.Schema.Types.Mixed,
86
+ body: mongoose.Schema.Types.Mixed,
87
+ // 延迟配置
88
+ delayMinutes: Number,
89
+ // 条件配置
90
+ conditions: [{
91
+ field: String,
92
+ operator: String,
93
+ value: mongoose.Schema.Types.Mixed
94
+ }],
95
+ // 脚本配置
96
+ script: String
97
+ },
98
+ // 步骤执行条件
99
+ executeIf: [{
100
+ field: String,
101
+ operator: String,
102
+ value: mongoose.Schema.Types.Mixed
103
+ }],
104
+ // 失败时的操作
105
+ onFailure: {
106
+ type: String,
107
+ enum: ['stop', 'continue', 'retry'],
108
+ default: 'stop'
109
+ },
110
+ retryCount: {
111
+ type: Number,
112
+ default: 0
113
+ }
114
+ }],
115
+ // 工作流状态
116
+ status: {
117
+ type: String,
118
+ enum: ['active', 'inactive', 'draft'],
119
+ default: 'draft'
120
+ },
121
+ // 执行统计
122
+ stats: {
123
+ totalExecutions: {
124
+ type: Number,
125
+ default: 0
126
+ },
127
+ successfulExecutions: {
128
+ type: Number,
129
+ default: 0
130
+ },
131
+ failedExecutions: {
132
+ type: Number,
133
+ default: 0
134
+ },
135
+ lastExecutedAt: Date
136
+ }
137
+ }, {
138
+ timestamps: true
139
+ });
140
+
141
+ // 索引
142
+ workflowSchema.index({ group: 1, status: 1 });
143
+ workflowSchema.index({ creator: 1 });
144
+ workflowSchema.index({ 'trigger.type': 1 });
145
+ workflowSchema.index({ 'trigger.event': 1 });
146
+
147
+ // 工作流执行记录
148
+ const workflowExecutionSchema = new mongoose.Schema({
149
+ workflow: {
150
+ type: mongoose.Schema.Types.ObjectId,
151
+ ref: 'Workflow',
152
+ required: true
153
+ },
154
+ triggeredBy: {
155
+ type: mongoose.Schema.Types.ObjectId,
156
+ ref: 'User'
157
+ },
158
+ triggerType: {
159
+ type: String,
160
+ enum: ['manual', 'scheduled', 'event']
161
+ },
162
+ triggerData: mongoose.Schema.Types.Mixed,
163
+ status: {
164
+ type: String,
165
+ enum: ['pending', 'running', 'completed', 'failed', 'cancelled'],
166
+ default: 'pending'
167
+ },
168
+ currentStep: {
169
+ type: Number,
170
+ default: 0
171
+ },
172
+ steps: [{
173
+ stepIndex: Number,
174
+ stepName: String,
175
+ status: {
176
+ type: String,
177
+ enum: ['pending', 'running', 'completed', 'failed', 'skipped']
178
+ },
179
+ startedAt: Date,
180
+ completedAt: Date,
181
+ result: mongoose.Schema.Types.Mixed,
182
+ error: String
183
+ }],
184
+ startedAt: Date,
185
+ completedAt: Date,
186
+ error: String,
187
+ logs: [{
188
+ timestamp: {
189
+ type: Date,
190
+ default: Date.now
191
+ },
192
+ level: {
193
+ type: String,
194
+ enum: ['info', 'warning', 'error']
195
+ },
196
+ message: String,
197
+ data: mongoose.Schema.Types.Mixed
198
+ }]
199
+ }, {
200
+ timestamps: true
201
+ });
202
+
203
+ // 索引
204
+ workflowExecutionSchema.index({ workflow: 1, createdAt: -1 });
205
+ workflowExecutionSchema.index({ status: 1 });
206
+ workflowExecutionSchema.index({ triggeredBy: 1 });
207
+
208
+ // 审批记录
209
+ const approvalSchema = new mongoose.Schema({
210
+ execution: {
211
+ type: mongoose.Schema.Types.ObjectId,
212
+ ref: 'WorkflowExecution',
213
+ required: true
214
+ },
215
+ workflow: {
216
+ type: mongoose.Schema.Types.ObjectId,
217
+ ref: 'Workflow',
218
+ required: true
219
+ },
220
+ stepIndex: {
221
+ type: Number,
222
+ required: true
223
+ },
224
+ approver: {
225
+ type: mongoose.Schema.Types.ObjectId,
226
+ ref: 'User',
227
+ required: true
228
+ },
229
+ status: {
230
+ type: String,
231
+ enum: ['pending', 'approved', 'rejected'],
232
+ default: 'pending'
233
+ },
234
+ comment: String,
235
+ decidedAt: Date
236
+ }, {
237
+ timestamps: true
238
+ });
239
+
240
+ // 索引
241
+ approvalSchema.index({ execution: 1, approver: 1 });
242
+ approvalSchema.index({ approver: 1, status: 1 });
243
+
244
+ export const Workflow = mongoose.model('Workflow', workflowSchema);
245
+ export const WorkflowExecution = mongoose.model('WorkflowExecution', workflowExecutionSchema);
246
+ export const Approval = mongoose.model('Approval', approvalSchema);
247
+
248
+ export default Workflow;
249
+
@@ -0,0 +1,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
+ // 智能回复
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
+