galaxy-opc-plugin 0.2.1 → 0.2.3
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.
- package/README.md +207 -10
- package/index.ts +350 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +17 -3
- package/src/__tests__/e2e/company-lifecycle.test.ts +399 -0
- package/src/__tests__/integration/business-workflows.test.ts +366 -0
- package/src/__tests__/test-utils.ts +316 -0
- package/src/api/companies.ts +4 -0
- package/src/api/dashboard.ts +368 -16
- package/src/api/routes.ts +2 -2
- package/src/commands/opc-command.ts +422 -0
- package/src/db/index.ts +3 -0
- package/src/db/migrations.test.ts +324 -0
- package/src/db/migrations.ts +277 -0
- package/src/db/schema.ts +312 -0
- package/src/db/sqlite-adapter.ts +44 -2
- package/src/opc/accounting-parser.ts +178 -0
- package/src/opc/autonomy-rules.ts +132 -0
- package/src/opc/briefing-builder.ts +1331 -0
- package/src/opc/business-workflows.test.ts +535 -0
- package/src/opc/business-workflows.ts +325 -0
- package/src/opc/context-injector.ts +366 -28
- package/src/opc/daily-brief.ts +529 -0
- package/src/opc/event-triggers.ts +472 -0
- package/src/opc/intelligence-engine.ts +702 -0
- package/src/opc/milestone-detector.ts +251 -0
- package/src/opc/onboarding-flow.ts +332 -0
- package/src/opc/proactive-service.ts +466 -0
- package/src/opc/reminder-service.ts +4 -43
- package/src/opc/session-task-tracker.ts +60 -0
- package/src/opc/stage-detector.ts +168 -0
- package/src/opc/task-executor.ts +332 -0
- package/src/opc/task-templates.ts +179 -0
- package/src/tools/document-tool.ts +1176 -0
- package/src/tools/finance-tool.test-payment.ts +326 -0
- package/src/tools/finance-tool.test.ts +238 -0
- package/src/tools/finance-tool.ts +1574 -14
- package/src/tools/hr-tool.ts +10 -1
- package/src/tools/legal-tool.test.ts +251 -0
- package/src/tools/legal-tool.ts +26 -4
- package/src/tools/lifecycle-tool.test.ts +231 -0
- package/src/tools/media-tool.ts +156 -1
- package/src/tools/monitoring-tool.ts +134 -1
- package/src/tools/onboarding-tool.ts +233 -0
- package/src/tools/opc-tool.test.ts +250 -0
- package/src/tools/opc-tool.ts +251 -28
- package/src/tools/order-tool.ts +481 -0
- package/src/tools/project-tool.test.ts +218 -0
- package/src/tools/schemas.ts +80 -0
- package/src/tools/search-tool.ts +227 -0
- package/src/tools/smart-accounting-tool.ts +144 -0
- package/src/tools/staff-tool.ts +395 -2
- package/src/web/DASHBOARD_INTEGRATION_GUIDE.md +478 -0
- package/src/web/config-ui-patches.ts +389 -0
- package/src/web/config-ui.ts +4162 -3555
- package/src/web/dashboard-ui.ts +582 -0
- package/src/web/landing-page.ts +56 -6
package/src/api/dashboard.ts
CHANGED
|
@@ -1,32 +1,110 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 星环OPC中心 — Dashboard
|
|
2
|
+
* 星环OPC中心 — Dashboard 监控中心 API
|
|
3
3
|
*
|
|
4
4
|
* 路由:
|
|
5
|
-
* GET /opc/api/dashboard
|
|
5
|
+
* GET /opc/admin/api/dashboard — 获取监控中心数据
|
|
6
|
+
* POST /opc/admin/api/alerts/:id/dismiss — 忽略预警
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
9
10
|
import type { OpcDatabase } from "../db/index.js";
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
generateDashboardHtml,
|
|
13
|
+
type DashboardData,
|
|
14
|
+
type DashboardMetrics,
|
|
15
|
+
type DashboardAlert,
|
|
16
|
+
type DashboardTodo,
|
|
17
|
+
type DashboardSuggestion,
|
|
18
|
+
} from "../web/dashboard-ui.js";
|
|
11
19
|
|
|
12
|
-
|
|
20
|
+
interface DashboardRow {
|
|
21
|
+
total_income: number;
|
|
22
|
+
total_expense: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface PaymentRow {
|
|
26
|
+
id: string;
|
|
27
|
+
company_id: string;
|
|
28
|
+
direction: string;
|
|
29
|
+
counterparty: string;
|
|
30
|
+
amount: number;
|
|
31
|
+
paid_amount: number;
|
|
32
|
+
status: string;
|
|
33
|
+
due_date: string;
|
|
34
|
+
category: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface AlertRow {
|
|
38
|
+
id: string;
|
|
39
|
+
company_id: string;
|
|
40
|
+
title: string;
|
|
41
|
+
severity: string;
|
|
42
|
+
category: string;
|
|
43
|
+
status: string;
|
|
44
|
+
message: string;
|
|
45
|
+
created_at: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface TodoRow {
|
|
49
|
+
id: string;
|
|
50
|
+
company_id: string;
|
|
51
|
+
title: string;
|
|
52
|
+
priority: string;
|
|
53
|
+
category: string;
|
|
54
|
+
status: string;
|
|
55
|
+
due_date: string;
|
|
56
|
+
description: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 注册 Dashboard API 路由
|
|
61
|
+
*/
|
|
62
|
+
export function registerDashboardApiRoutes(api: OpenClawPluginApi, db: OpcDatabase): void {
|
|
63
|
+
// GET /opc/admin/api/dashboard
|
|
13
64
|
api.registerHttpRoute({
|
|
14
|
-
path: "/opc/api/dashboard
|
|
65
|
+
path: "/opc/admin/api/dashboard",
|
|
15
66
|
handler: (req, res) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
67
|
+
try {
|
|
68
|
+
const data = getDashboardData(db);
|
|
69
|
+
const html = generateDashboardHtml(data);
|
|
70
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
71
|
+
res.end(JSON.stringify({ ...data, html }));
|
|
72
|
+
} catch (err) {
|
|
73
|
+
res.writeHead(500, { "Content-Type": "application/json; charset=utf-8" });
|
|
74
|
+
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
24
75
|
}
|
|
76
|
+
},
|
|
77
|
+
});
|
|
25
78
|
|
|
79
|
+
// POST /opc/admin/api/alerts/:id/dismiss
|
|
80
|
+
api.registerHttpRoute({
|
|
81
|
+
path: "/opc/admin/api/alerts/:id/dismiss",
|
|
82
|
+
handler: async (req, res) => {
|
|
26
83
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
84
|
+
const url = new URL(req.url || "", `http://${req.headers.host}`);
|
|
85
|
+
const pathParts = url.pathname.split("/");
|
|
86
|
+
const alertId = pathParts[pathParts.length - 2]; // .../alerts/{id}/dismiss
|
|
87
|
+
|
|
88
|
+
if (!alertId) {
|
|
89
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
90
|
+
res.end(JSON.stringify({ error: "Missing alert ID" }));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const now = new Date().toISOString();
|
|
95
|
+
const result = db.execute(
|
|
96
|
+
"UPDATE opc_alerts SET status = 'resolved', resolved_at = ? WHERE id = ? AND status = 'active'",
|
|
97
|
+
now,
|
|
98
|
+
alertId,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (result.changes > 0) {
|
|
102
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
103
|
+
res.end(JSON.stringify({ ok: true }));
|
|
104
|
+
} else {
|
|
105
|
+
res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
|
|
106
|
+
res.end(JSON.stringify({ error: "Alert not found or already resolved" }));
|
|
107
|
+
}
|
|
30
108
|
} catch (err) {
|
|
31
109
|
res.writeHead(500, { "Content-Type": "application/json; charset=utf-8" });
|
|
32
110
|
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
@@ -34,3 +112,277 @@ export function registerDashboardRoutes(api: OpenClawPluginApi, db: OpcDatabase,
|
|
|
34
112
|
},
|
|
35
113
|
});
|
|
36
114
|
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 获取 Dashboard 数据
|
|
118
|
+
*/
|
|
119
|
+
function getDashboardData(db: OpcDatabase): DashboardData {
|
|
120
|
+
const metrics = calculateMetrics(db);
|
|
121
|
+
const alerts = getActiveAlerts(db);
|
|
122
|
+
const todos = getTodayTodos(db);
|
|
123
|
+
const suggestions = generateSuggestions(db, metrics, alerts, todos);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
metrics,
|
|
127
|
+
alerts,
|
|
128
|
+
todos,
|
|
129
|
+
suggestions,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 计算关键指标
|
|
135
|
+
*/
|
|
136
|
+
function calculateMetrics(db: OpcDatabase): DashboardMetrics {
|
|
137
|
+
// 本月收入和支出
|
|
138
|
+
const now = new Date();
|
|
139
|
+
const currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10);
|
|
140
|
+
const currentMonthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString().slice(0, 10);
|
|
141
|
+
|
|
142
|
+
const currentMonth = db.queryOne(
|
|
143
|
+
`SELECT
|
|
144
|
+
COALESCE(SUM(CASE WHEN type='income' THEN amount ELSE 0 END), 0) as total_income,
|
|
145
|
+
COALESCE(SUM(CASE WHEN type='expense' THEN amount ELSE 0 END), 0) as total_expense
|
|
146
|
+
FROM opc_transactions
|
|
147
|
+
WHERE transaction_date >= ? AND transaction_date <= ?`,
|
|
148
|
+
currentMonthStart,
|
|
149
|
+
currentMonthEnd,
|
|
150
|
+
) as DashboardRow;
|
|
151
|
+
|
|
152
|
+
// 上月收入和支出(用于计算同比)
|
|
153
|
+
const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString().slice(0, 10);
|
|
154
|
+
const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0).toISOString().slice(0, 10);
|
|
155
|
+
|
|
156
|
+
const lastMonth = db.queryOne(
|
|
157
|
+
`SELECT
|
|
158
|
+
COALESCE(SUM(CASE WHEN type='income' THEN amount ELSE 0 END), 0) as total_income,
|
|
159
|
+
COALESCE(SUM(CASE WHEN type='expense' THEN amount ELSE 0 END), 0) as total_expense
|
|
160
|
+
FROM opc_transactions
|
|
161
|
+
WHERE transaction_date >= ? AND transaction_date <= ?`,
|
|
162
|
+
lastMonthStart,
|
|
163
|
+
lastMonthEnd,
|
|
164
|
+
) as DashboardRow;
|
|
165
|
+
|
|
166
|
+
const monthlyIncome = currentMonth.total_income;
|
|
167
|
+
const monthlyExpense = currentMonth.total_expense;
|
|
168
|
+
const monthlyProfit = monthlyIncome - monthlyExpense;
|
|
169
|
+
|
|
170
|
+
// 计算同比变化(避免除零)
|
|
171
|
+
const monthlyIncomeChange = lastMonth.total_income > 0
|
|
172
|
+
? ((monthlyIncome - lastMonth.total_income) / lastMonth.total_income) * 100
|
|
173
|
+
: 0;
|
|
174
|
+
|
|
175
|
+
const lastMonthProfit = lastMonth.total_income - lastMonth.total_expense;
|
|
176
|
+
const monthlyProfitChange = lastMonthProfit !== 0
|
|
177
|
+
? ((monthlyProfit - lastMonthProfit) / Math.abs(lastMonthProfit)) * 100
|
|
178
|
+
: 0;
|
|
179
|
+
|
|
180
|
+
// 现金余额(所有收入 - 所有支出)
|
|
181
|
+
const cashBalance = db.queryOne(
|
|
182
|
+
`SELECT
|
|
183
|
+
COALESCE(SUM(CASE WHEN type='income' THEN amount ELSE 0 END), 0) -
|
|
184
|
+
COALESCE(SUM(CASE WHEN type='expense' THEN amount ELSE 0 END), 0) as balance
|
|
185
|
+
FROM opc_transactions`,
|
|
186
|
+
) as { balance: number };
|
|
187
|
+
|
|
188
|
+
// 可撑月数(基于最近3个月平均支出)
|
|
189
|
+
const threeMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 3, 1).toISOString().slice(0, 10);
|
|
190
|
+
const avgExpense = db.queryOne(
|
|
191
|
+
`SELECT COALESCE(AVG(monthly_expense), 0) as avg_expense FROM (
|
|
192
|
+
SELECT SUM(amount) as monthly_expense
|
|
193
|
+
FROM opc_transactions
|
|
194
|
+
WHERE type='expense' AND transaction_date >= ?
|
|
195
|
+
GROUP BY strftime('%Y-%m', transaction_date)
|
|
196
|
+
)`,
|
|
197
|
+
threeMonthsAgo,
|
|
198
|
+
) as { avg_expense: number };
|
|
199
|
+
|
|
200
|
+
const monthsOfRunway = avgExpense.avg_expense > 0
|
|
201
|
+
? cashBalance.balance / avgExpense.avg_expense
|
|
202
|
+
: 999;
|
|
203
|
+
|
|
204
|
+
// 应收账款(未收和部分已收的应收款)
|
|
205
|
+
const receivables = db.queryOne(
|
|
206
|
+
`SELECT COALESCE(SUM(amount - paid_amount), 0) as total
|
|
207
|
+
FROM opc_payments
|
|
208
|
+
WHERE direction = 'receivable' AND status IN ('pending', 'partial')`,
|
|
209
|
+
) as { total: number };
|
|
210
|
+
|
|
211
|
+
// 逾期应收账款
|
|
212
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
213
|
+
const overdueReceivables = db.queryOne(
|
|
214
|
+
`SELECT COALESCE(SUM(amount - paid_amount), 0) as total
|
|
215
|
+
FROM opc_payments
|
|
216
|
+
WHERE direction = 'receivable'
|
|
217
|
+
AND status IN ('pending', 'partial', 'overdue')
|
|
218
|
+
AND due_date < ?`,
|
|
219
|
+
today,
|
|
220
|
+
) as { total: number };
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
monthlyIncome,
|
|
224
|
+
monthlyIncomeChange,
|
|
225
|
+
monthlyProfit,
|
|
226
|
+
monthlyProfitChange,
|
|
227
|
+
cashBalance: cashBalance.balance,
|
|
228
|
+
monthsOfRunway,
|
|
229
|
+
receivables: receivables.total,
|
|
230
|
+
overdueReceivables: overdueReceivables.total,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* 获取活跃预警
|
|
236
|
+
*/
|
|
237
|
+
function getActiveAlerts(db: OpcDatabase): DashboardAlert[] {
|
|
238
|
+
const alerts = db.query(
|
|
239
|
+
`SELECT id, company_id, title, severity, category, message, created_at
|
|
240
|
+
FROM opc_alerts
|
|
241
|
+
WHERE status = 'active'
|
|
242
|
+
ORDER BY
|
|
243
|
+
CASE severity
|
|
244
|
+
WHEN 'critical' THEN 0
|
|
245
|
+
WHEN 'warning' THEN 1
|
|
246
|
+
ELSE 2
|
|
247
|
+
END,
|
|
248
|
+
created_at DESC
|
|
249
|
+
LIMIT 10`,
|
|
250
|
+
) as AlertRow[];
|
|
251
|
+
|
|
252
|
+
return alerts.map(alert => ({
|
|
253
|
+
id: alert.id,
|
|
254
|
+
title: alert.title,
|
|
255
|
+
severity: alert.severity as 'critical' | 'warning' | 'info',
|
|
256
|
+
category: alert.category,
|
|
257
|
+
message: alert.message,
|
|
258
|
+
created_at: alert.created_at,
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 获取今日待办
|
|
264
|
+
*/
|
|
265
|
+
function getTodayTodos(db: OpcDatabase): DashboardTodo[] {
|
|
266
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
267
|
+
|
|
268
|
+
const todos = db.query(
|
|
269
|
+
`SELECT id, company_id, title, priority, related_type as category, due_date, description
|
|
270
|
+
FROM opc_todos
|
|
271
|
+
WHERE status = 'pending'
|
|
272
|
+
AND (due_date = ? OR due_date < ?)
|
|
273
|
+
ORDER BY
|
|
274
|
+
CASE priority
|
|
275
|
+
WHEN 'urgent' THEN 0
|
|
276
|
+
WHEN 'high' THEN 1
|
|
277
|
+
ELSE 2
|
|
278
|
+
END,
|
|
279
|
+
due_date ASC
|
|
280
|
+
LIMIT 20`,
|
|
281
|
+
today,
|
|
282
|
+
today,
|
|
283
|
+
) as TodoRow[];
|
|
284
|
+
|
|
285
|
+
return todos.map(todo => ({
|
|
286
|
+
id: todo.id,
|
|
287
|
+
title: todo.title,
|
|
288
|
+
priority: todo.priority as 'urgent' | 'high' | 'normal',
|
|
289
|
+
category: todo.category || '',
|
|
290
|
+
due_date: todo.due_date || undefined,
|
|
291
|
+
description: todo.description || undefined,
|
|
292
|
+
}));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 生成 AI 建议
|
|
297
|
+
*/
|
|
298
|
+
function generateSuggestions(
|
|
299
|
+
db: OpcDatabase,
|
|
300
|
+
metrics: DashboardMetrics,
|
|
301
|
+
alerts: DashboardAlert[],
|
|
302
|
+
todos: DashboardTodo[],
|
|
303
|
+
): DashboardSuggestion[] {
|
|
304
|
+
const suggestions: DashboardSuggestion[] = [];
|
|
305
|
+
|
|
306
|
+
// 建议1: 现金流预警
|
|
307
|
+
if (metrics.monthsOfRunway < 2) {
|
|
308
|
+
suggestions.push({
|
|
309
|
+
id: "cash-runway-low",
|
|
310
|
+
title: "现金流预警:可撑月数不足",
|
|
311
|
+
description: `当前现金余额 ¥${metrics.cashBalance.toFixed(0)} 仅能维持 ${metrics.monthsOfRunway.toFixed(1)} 个月运营。建议尽快催收应收账款或拓展收入来源。`,
|
|
312
|
+
action: {
|
|
313
|
+
label: "查看应收账款",
|
|
314
|
+
onclick: "loadView('finance')",
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// 建议2: 逾期催收
|
|
320
|
+
if (metrics.overdueReceivables > 0) {
|
|
321
|
+
suggestions.push({
|
|
322
|
+
id: "overdue-receivables",
|
|
323
|
+
title: "逾期账款催收提醒",
|
|
324
|
+
description: `有 ¥${metrics.overdueReceivables.toFixed(0)} 的应收账款已逾期,建议立即联系客户催收。`,
|
|
325
|
+
action: {
|
|
326
|
+
label: "查看逾期清单",
|
|
327
|
+
onclick: "loadView('finance')",
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// 建议3: 利润优化
|
|
333
|
+
if (metrics.monthlyProfit < 0) {
|
|
334
|
+
suggestions.push({
|
|
335
|
+
id: "negative-profit",
|
|
336
|
+
title: "本月利润为负,需优化成本",
|
|
337
|
+
description: `本月支出超过收入 ¥${Math.abs(metrics.monthlyProfit).toFixed(0)},建议检查支出明细,削减非必要成本。`,
|
|
338
|
+
action: {
|
|
339
|
+
label: "查看支出分析",
|
|
340
|
+
onclick: "loadView('finance')",
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 建议4: 收入增长策略
|
|
346
|
+
if (metrics.monthlyIncomeChange < -10) {
|
|
347
|
+
suggestions.push({
|
|
348
|
+
id: "income-decline",
|
|
349
|
+
title: "收入下滑,需拓展业务",
|
|
350
|
+
description: `本月收入较上月下降 ${Math.abs(metrics.monthlyIncomeChange).toFixed(1)}%,建议加强客户开发和老客户维护。`,
|
|
351
|
+
action: {
|
|
352
|
+
label: "查看客户管理",
|
|
353
|
+
onclick: "loadView('companies')",
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 建议5: 合同到期提醒
|
|
359
|
+
const expiringContracts = db.query(
|
|
360
|
+
`SELECT COUNT(*) as cnt FROM opc_contracts
|
|
361
|
+
WHERE status = 'active'
|
|
362
|
+
AND end_date <= date('now', '+30 days')
|
|
363
|
+
AND end_date >= date('now')`,
|
|
364
|
+
) as { cnt: number }[];
|
|
365
|
+
|
|
366
|
+
if (expiringContracts.length > 0 && expiringContracts[0].cnt > 0) {
|
|
367
|
+
suggestions.push({
|
|
368
|
+
id: "contracts-expiring",
|
|
369
|
+
title: "合同即将到期",
|
|
370
|
+
description: `有 ${expiringContracts[0].cnt} 份合同将在 30 天内到期,建议提前联系续约。`,
|
|
371
|
+
action: {
|
|
372
|
+
label: "查看合同列表",
|
|
373
|
+
onclick: "loadView('companies')",
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 默认建议:一切正常
|
|
379
|
+
if (suggestions.length === 0) {
|
|
380
|
+
suggestions.push({
|
|
381
|
+
id: "all-good",
|
|
382
|
+
title: "经营状况良好",
|
|
383
|
+
description: "当前各项指标正常,继续保持!建议定期回顾财务数据,优化运营效率。",
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return suggestions.slice(0, 5); // 最多返回5条建议
|
|
388
|
+
}
|
package/src/api/routes.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
6
6
|
import type { OpcDatabase } from "../db/index.js";
|
|
7
7
|
import { registerCompanyRoutes } from "./companies.js";
|
|
8
|
-
import {
|
|
8
|
+
import { registerDashboardApiRoutes } from "./dashboard.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* 从 OpenClaw 配置中读取 gateway auth token。
|
|
@@ -26,6 +26,6 @@ export function registerHttpRoutes(api: OpenClawPluginApi, db: OpcDatabase): voi
|
|
|
26
26
|
const gatewayToken = getGatewayToken(api);
|
|
27
27
|
|
|
28
28
|
registerCompanyRoutes(api, db, gatewayToken);
|
|
29
|
-
|
|
29
|
+
registerDashboardApiRoutes(api, db);
|
|
30
30
|
api.logger.info("opc: 已注册 HTTP API 路由");
|
|
31
31
|
}
|