galaxy-opc-plugin 0.1.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.
- package/README.md +166 -0
- package/index.ts +145 -0
- package/openclaw.plugin.json +17 -0
- package/package.json +47 -0
- package/skills/basic-crm/SKILL.md +81 -0
- package/skills/basic-finance/SKILL.md +100 -0
- package/skills/business-monitoring/SKILL.md +120 -0
- package/skills/company-lifecycle/SKILL.md +99 -0
- package/skills/company-registration/SKILL.md +80 -0
- package/skills/finance-tax/SKILL.md +150 -0
- package/skills/hr-assistant/SKILL.md +127 -0
- package/skills/investment-management/SKILL.md +101 -0
- package/skills/legal-assistant/SKILL.md +113 -0
- package/skills/media-ops/SKILL.md +101 -0
- package/skills/procurement-management/SKILL.md +91 -0
- package/skills/project-management/SKILL.md +125 -0
- package/src/api/companies.ts +193 -0
- package/src/api/dashboard.ts +25 -0
- package/src/api/routes.ts +14 -0
- package/src/db/index.ts +63 -0
- package/src/db/migrations.ts +67 -0
- package/src/db/schema.ts +518 -0
- package/src/db/sqlite-adapter.ts +366 -0
- package/src/opc/company-manager.ts +82 -0
- package/src/opc/context-injector.ts +186 -0
- package/src/opc/reminder-service.ts +289 -0
- package/src/opc/types.ts +330 -0
- package/src/opc/workspace-factory.ts +189 -0
- package/src/tools/acquisition-tool.ts +150 -0
- package/src/tools/asset-package-tool.ts +283 -0
- package/src/tools/finance-tool.ts +244 -0
- package/src/tools/hr-tool.ts +211 -0
- package/src/tools/investment-tool.ts +201 -0
- package/src/tools/legal-tool.ts +191 -0
- package/src/tools/lifecycle-tool.ts +251 -0
- package/src/tools/media-tool.ts +174 -0
- package/src/tools/monitoring-tool.ts +207 -0
- package/src/tools/opb-tool.ts +193 -0
- package/src/tools/opc-tool.ts +206 -0
- package/src/tools/procurement-tool.ts +191 -0
- package/src/tools/project-tool.ts +203 -0
- package/src/tools/schemas.ts +163 -0
- package/src/tools/staff-tool.ts +211 -0
- package/src/utils/tool-helper.ts +16 -0
- package/src/web/config-ui.ts +3501 -0
- package/src/web/landing-page.ts +269 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 星环OPC中心 — opc_procurement 服务采购工具
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Type, type Static } from "@sinclair/typebox";
|
|
6
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
7
|
+
import type { OpcDatabase } from "../db/index.js";
|
|
8
|
+
import { json } from "../utils/tool-helper.js";
|
|
9
|
+
|
|
10
|
+
const ProcurementSchema = Type.Union([
|
|
11
|
+
Type.Object({
|
|
12
|
+
action: Type.Literal("create_service"),
|
|
13
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
14
|
+
name: Type.String({ description: "服务名称" }),
|
|
15
|
+
category: Type.Optional(Type.String({ description: "服务类别: saas/outsource/subscription/consulting/other" })),
|
|
16
|
+
provider: Type.Optional(Type.String({ description: "服务商名称" })),
|
|
17
|
+
unit_price: Type.Number({ description: "单价(元)" }),
|
|
18
|
+
billing_cycle: Type.Optional(Type.String({ description: "计费周期: monthly/quarterly/yearly/one_time" })),
|
|
19
|
+
description: Type.Optional(Type.String({ description: "描述" })),
|
|
20
|
+
}),
|
|
21
|
+
Type.Object({
|
|
22
|
+
action: Type.Literal("list_services"),
|
|
23
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
24
|
+
category: Type.Optional(Type.String({ description: "按类别筛选" })),
|
|
25
|
+
status: Type.Optional(Type.String({ description: "按状态筛选: active/suspended/terminated" })),
|
|
26
|
+
}),
|
|
27
|
+
Type.Object({
|
|
28
|
+
action: Type.Literal("create_order"),
|
|
29
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
30
|
+
service_id: Type.Optional(Type.String({ description: "关联服务 ID" })),
|
|
31
|
+
title: Type.String({ description: "订单标题" }),
|
|
32
|
+
amount: Type.Number({ description: "订单金额(元)" }),
|
|
33
|
+
order_date: Type.Optional(Type.String({ description: "订单日期 (YYYY-MM-DD)" })),
|
|
34
|
+
delivery_date: Type.Optional(Type.String({ description: "交付日期 (YYYY-MM-DD)" })),
|
|
35
|
+
notes: Type.Optional(Type.String({ description: "备注" })),
|
|
36
|
+
}),
|
|
37
|
+
Type.Object({
|
|
38
|
+
action: Type.Literal("list_orders"),
|
|
39
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
40
|
+
status: Type.Optional(Type.String({ description: "按状态筛选: pending/approved/paid/cancelled" })),
|
|
41
|
+
}),
|
|
42
|
+
Type.Object({
|
|
43
|
+
action: Type.Literal("update_order"),
|
|
44
|
+
order_id: Type.String({ description: "订单 ID" }),
|
|
45
|
+
status: Type.Optional(Type.String({ description: "新状态: pending/approved/paid/cancelled" })),
|
|
46
|
+
delivery_date: Type.Optional(Type.String({ description: "交付日期 (YYYY-MM-DD)" })),
|
|
47
|
+
notes: Type.Optional(Type.String({ description: "备注" })),
|
|
48
|
+
}),
|
|
49
|
+
Type.Object({
|
|
50
|
+
action: Type.Literal("order_summary"),
|
|
51
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
52
|
+
}),
|
|
53
|
+
Type.Object({
|
|
54
|
+
action: Type.Literal("delete_service"),
|
|
55
|
+
service_id: Type.String({ description: "服务项目 ID" }),
|
|
56
|
+
}),
|
|
57
|
+
Type.Object({
|
|
58
|
+
action: Type.Literal("delete_order"),
|
|
59
|
+
order_id: Type.String({ description: "采购订单 ID" }),
|
|
60
|
+
}),
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
type ProcurementParams = Static<typeof ProcurementSchema>;
|
|
64
|
+
|
|
65
|
+
export function registerProcurementTool(api: OpenClawPluginApi, db: OpcDatabase): void {
|
|
66
|
+
api.registerTool(
|
|
67
|
+
{
|
|
68
|
+
name: "opc_procurement",
|
|
69
|
+
label: "OPC 服务采购",
|
|
70
|
+
description:
|
|
71
|
+
"服务采购管理工具。操作: create_service(添加服务项目), list_services(服务列表), " +
|
|
72
|
+
"create_order(创建采购订单), update_order(更新订单状态/审批/付款), list_orders(订单列表), order_summary(采购汇总), delete_service(删除服务项目), delete_order(删除采购订单)",
|
|
73
|
+
parameters: ProcurementSchema,
|
|
74
|
+
async execute(_toolCallId, params) {
|
|
75
|
+
const p = params as ProcurementParams;
|
|
76
|
+
try {
|
|
77
|
+
switch (p.action) {
|
|
78
|
+
case "create_service": {
|
|
79
|
+
const id = db.genId();
|
|
80
|
+
const now = new Date().toISOString();
|
|
81
|
+
db.execute(
|
|
82
|
+
`INSERT INTO opc_services (id, company_id, name, category, provider, unit_price, billing_cycle, status, description, created_at, updated_at)
|
|
83
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?)`,
|
|
84
|
+
id, p.company_id, p.name,
|
|
85
|
+
p.category ?? "", p.provider ?? "", p.unit_price,
|
|
86
|
+
p.billing_cycle ?? "monthly", p.description ?? "", now, now,
|
|
87
|
+
);
|
|
88
|
+
return json(db.queryOne("SELECT * FROM opc_services WHERE id = ?", id));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case "list_services": {
|
|
92
|
+
let sql = "SELECT * FROM opc_services WHERE company_id = ?";
|
|
93
|
+
const params2: unknown[] = [p.company_id];
|
|
94
|
+
if (p.category) { sql += " AND category = ?"; params2.push(p.category); }
|
|
95
|
+
if (p.status) { sql += " AND status = ?"; params2.push(p.status); }
|
|
96
|
+
sql += " ORDER BY created_at DESC";
|
|
97
|
+
return json(db.query(sql, ...params2));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case "create_order": {
|
|
101
|
+
const id = db.genId();
|
|
102
|
+
const now = new Date().toISOString();
|
|
103
|
+
db.execute(
|
|
104
|
+
`INSERT INTO opc_procurement_orders (id, service_id, company_id, title, amount, status, order_date, delivery_date, notes, created_at)
|
|
105
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)`,
|
|
106
|
+
id, p.service_id ?? "", p.company_id, p.title, p.amount,
|
|
107
|
+
p.order_date ?? now.slice(0, 10), p.delivery_date ?? "", p.notes ?? "", now,
|
|
108
|
+
);
|
|
109
|
+
return json(db.queryOne("SELECT * FROM opc_procurement_orders WHERE id = ?", id));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case "list_orders": {
|
|
113
|
+
let sql = "SELECT * FROM opc_procurement_orders WHERE company_id = ?";
|
|
114
|
+
const params2: unknown[] = [p.company_id];
|
|
115
|
+
if (p.status) { sql += " AND status = ?"; params2.push(p.status); }
|
|
116
|
+
sql += " ORDER BY order_date DESC";
|
|
117
|
+
return json(db.query(sql, ...params2));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
case "update_order": {
|
|
121
|
+
const sets: string[] = [];
|
|
122
|
+
const vals: unknown[] = [];
|
|
123
|
+
if (p.status !== undefined) { sets.push("status = ?"); vals.push(p.status); }
|
|
124
|
+
if (p.delivery_date !== undefined) { sets.push("delivery_date = ?"); vals.push(p.delivery_date); }
|
|
125
|
+
if (p.notes !== undefined) { sets.push("notes = ?"); vals.push(p.notes); }
|
|
126
|
+
if (sets.length === 0) return json({ error: "未提供任何更新字段" });
|
|
127
|
+
vals.push(p.order_id);
|
|
128
|
+
db.execute(`UPDATE opc_procurement_orders SET ${sets.join(", ")} WHERE id = ?`, ...vals);
|
|
129
|
+
return json(db.queryOne("SELECT * FROM opc_procurement_orders WHERE id = ?", p.order_id));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
case "order_summary": {
|
|
133
|
+
const total = db.queryOne(
|
|
134
|
+
`SELECT COUNT(*) as order_count, COALESCE(SUM(amount), 0) as total_amount
|
|
135
|
+
FROM opc_procurement_orders WHERE company_id = ?`,
|
|
136
|
+
p.company_id,
|
|
137
|
+
) as { order_count: number; total_amount: number };
|
|
138
|
+
|
|
139
|
+
const byStatus = db.query(
|
|
140
|
+
`SELECT status, COUNT(*) as count, COALESCE(SUM(amount), 0) as amount
|
|
141
|
+
FROM opc_procurement_orders WHERE company_id = ?
|
|
142
|
+
GROUP BY status`,
|
|
143
|
+
p.company_id,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const byCategory = db.query(
|
|
147
|
+
`SELECT s.category, COUNT(o.id) as order_count, COALESCE(SUM(o.amount), 0) as total_amount
|
|
148
|
+
FROM opc_procurement_orders o
|
|
149
|
+
LEFT JOIN opc_services s ON o.service_id = s.id
|
|
150
|
+
WHERE o.company_id = ?
|
|
151
|
+
GROUP BY s.category`,
|
|
152
|
+
p.company_id,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const activeServices = db.queryOne(
|
|
156
|
+
`SELECT COUNT(*) as count, COALESCE(SUM(unit_price), 0) as monthly_cost
|
|
157
|
+
FROM opc_services WHERE company_id = ? AND status = 'active'`,
|
|
158
|
+
p.company_id,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return json({
|
|
162
|
+
...total,
|
|
163
|
+
by_status: byStatus,
|
|
164
|
+
by_category: byCategory,
|
|
165
|
+
active_services: activeServices,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
case "delete_service": {
|
|
170
|
+
db.execute("DELETE FROM opc_services WHERE id = ?", p.service_id);
|
|
171
|
+
return json({ ok: true });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case "delete_order": {
|
|
175
|
+
db.execute("DELETE FROM opc_procurement_orders WHERE id = ?", p.order_id);
|
|
176
|
+
return json({ ok: true });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
default:
|
|
180
|
+
return json({ error: `未知操作: ${(p as { action: string }).action}` });
|
|
181
|
+
}
|
|
182
|
+
} catch (err) {
|
|
183
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{ name: "opc_procurement" },
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
api.logger.info("opc: 已注册 opc_procurement 工具");
|
|
191
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 星环OPC中心 — opc_project 项目管理工具
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Type, type Static } from "@sinclair/typebox";
|
|
6
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
7
|
+
import type { OpcDatabase } from "../db/index.js";
|
|
8
|
+
import { json } from "../utils/tool-helper.js";
|
|
9
|
+
|
|
10
|
+
const ProjectSchema = Type.Union([
|
|
11
|
+
Type.Object({
|
|
12
|
+
action: Type.Literal("create_project"),
|
|
13
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
14
|
+
name: Type.String({ description: "项目名称" }),
|
|
15
|
+
description: Type.Optional(Type.String({ description: "项目描述" })),
|
|
16
|
+
start_date: Type.Optional(Type.String({ description: "开始日期 (YYYY-MM-DD)" })),
|
|
17
|
+
end_date: Type.Optional(Type.String({ description: "截止日期 (YYYY-MM-DD)" })),
|
|
18
|
+
budget: Type.Optional(Type.Number({ description: "预算(元)" })),
|
|
19
|
+
}),
|
|
20
|
+
Type.Object({
|
|
21
|
+
action: Type.Literal("list_projects"),
|
|
22
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
23
|
+
status: Type.Optional(Type.String({ description: "按状态筛选: planning/active/paused/completed/cancelled" })),
|
|
24
|
+
}),
|
|
25
|
+
Type.Object({
|
|
26
|
+
action: Type.Literal("update_project"),
|
|
27
|
+
project_id: Type.String({ description: "项目 ID" }),
|
|
28
|
+
status: Type.Optional(Type.String({ description: "新状态" })),
|
|
29
|
+
spent: Type.Optional(Type.Number({ description: "已花费金额" })),
|
|
30
|
+
end_date: Type.Optional(Type.String({ description: "新截止日期" })),
|
|
31
|
+
description: Type.Optional(Type.String({ description: "新描述" })),
|
|
32
|
+
}),
|
|
33
|
+
Type.Object({
|
|
34
|
+
action: Type.Literal("add_task"),
|
|
35
|
+
project_id: Type.String({ description: "项目 ID" }),
|
|
36
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
37
|
+
title: Type.String({ description: "任务标题" }),
|
|
38
|
+
description: Type.Optional(Type.String({ description: "任务描述" })),
|
|
39
|
+
assignee: Type.Optional(Type.String({ description: "负责人" })),
|
|
40
|
+
priority: Type.Optional(Type.String({ description: "优先级: low/medium/high/urgent" })),
|
|
41
|
+
due_date: Type.Optional(Type.String({ description: "截止日期 (YYYY-MM-DD)" })),
|
|
42
|
+
hours_estimated: Type.Optional(Type.Number({ description: "预估工时(小时)" })),
|
|
43
|
+
}),
|
|
44
|
+
Type.Object({
|
|
45
|
+
action: Type.Literal("list_tasks"),
|
|
46
|
+
project_id: Type.String({ description: "项目 ID" }),
|
|
47
|
+
status: Type.Optional(Type.String({ description: "按状态筛选: todo/in_progress/review/done" })),
|
|
48
|
+
}),
|
|
49
|
+
Type.Object({
|
|
50
|
+
action: Type.Literal("update_task"),
|
|
51
|
+
task_id: Type.String({ description: "任务 ID" }),
|
|
52
|
+
status: Type.Optional(Type.String({ description: "新状态: todo/in_progress/review/done" })),
|
|
53
|
+
hours_actual: Type.Optional(Type.Number({ description: "实际工时(小时)" })),
|
|
54
|
+
assignee: Type.Optional(Type.String({ description: "新负责人" })),
|
|
55
|
+
priority: Type.Optional(Type.String({ description: "新优先级" })),
|
|
56
|
+
due_date: Type.Optional(Type.String({ description: "新截止日期" })),
|
|
57
|
+
}),
|
|
58
|
+
Type.Object({
|
|
59
|
+
action: Type.Literal("project_summary"),
|
|
60
|
+
project_id: Type.String({ description: "项目 ID" }),
|
|
61
|
+
}),
|
|
62
|
+
Type.Object({
|
|
63
|
+
action: Type.Literal("kanban"),
|
|
64
|
+
project_id: Type.String({ description: "项目 ID" }),
|
|
65
|
+
}),
|
|
66
|
+
Type.Object({
|
|
67
|
+
action: Type.Literal("delete_project"),
|
|
68
|
+
project_id: Type.String({ description: "项目 ID" }),
|
|
69
|
+
}),
|
|
70
|
+
Type.Object({
|
|
71
|
+
action: Type.Literal("delete_task"),
|
|
72
|
+
task_id: Type.String({ description: "任务 ID" }),
|
|
73
|
+
}),
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
type ProjectParams = Static<typeof ProjectSchema>;
|
|
77
|
+
|
|
78
|
+
export function registerProjectTool(api: OpenClawPluginApi, db: OpcDatabase): void {
|
|
79
|
+
api.registerTool(
|
|
80
|
+
{
|
|
81
|
+
name: "opc_project",
|
|
82
|
+
label: "OPC 项目管理",
|
|
83
|
+
description:
|
|
84
|
+
"项目管理工具。操作: create_project(创建项目), list_projects(项目列表), " +
|
|
85
|
+
"update_project(更新项目), add_task(添加任务), list_tasks(任务列表), " +
|
|
86
|
+
"update_task(更新任务), project_summary(项目概况), kanban(看板视图), delete_project(删除项目及其任务), delete_task(删除任务)",
|
|
87
|
+
parameters: ProjectSchema,
|
|
88
|
+
async execute(_toolCallId, params) {
|
|
89
|
+
const p = params as ProjectParams;
|
|
90
|
+
try {
|
|
91
|
+
switch (p.action) {
|
|
92
|
+
case "create_project": {
|
|
93
|
+
const id = db.genId();
|
|
94
|
+
const now = new Date().toISOString();
|
|
95
|
+
db.execute(
|
|
96
|
+
`INSERT INTO opc_projects (id, company_id, name, description, status, start_date, end_date, budget, spent, created_at, updated_at)
|
|
97
|
+
VALUES (?, ?, ?, ?, 'planning', ?, ?, ?, 0, ?, ?)`,
|
|
98
|
+
id, p.company_id, p.name, p.description ?? "",
|
|
99
|
+
p.start_date ?? now.slice(0, 10), p.end_date ?? "",
|
|
100
|
+
p.budget ?? 0, now, now,
|
|
101
|
+
);
|
|
102
|
+
return json(db.queryOne("SELECT * FROM opc_projects WHERE id = ?", id));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
case "list_projects": {
|
|
106
|
+
let sql = "SELECT * FROM opc_projects WHERE company_id = ?";
|
|
107
|
+
const params2: unknown[] = [p.company_id];
|
|
108
|
+
if (p.status) { sql += " AND status = ?"; params2.push(p.status); }
|
|
109
|
+
sql += " ORDER BY created_at DESC";
|
|
110
|
+
return json(db.query(sql, ...params2));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
case "update_project": {
|
|
114
|
+
const fields: string[] = [];
|
|
115
|
+
const values: unknown[] = [];
|
|
116
|
+
if (p.status) { fields.push("status = ?"); values.push(p.status); }
|
|
117
|
+
if (p.spent !== undefined) { fields.push("spent = ?"); values.push(p.spent); }
|
|
118
|
+
if (p.end_date) { fields.push("end_date = ?"); values.push(p.end_date); }
|
|
119
|
+
if (p.description) { fields.push("description = ?"); values.push(p.description); }
|
|
120
|
+
fields.push("updated_at = ?"); values.push(new Date().toISOString());
|
|
121
|
+
values.push(p.project_id);
|
|
122
|
+
db.execute(`UPDATE opc_projects SET ${fields.join(", ")} WHERE id = ?`, ...values);
|
|
123
|
+
return json(db.queryOne("SELECT * FROM opc_projects WHERE id = ?", p.project_id) ?? { error: "项目不存在" });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case "add_task": {
|
|
127
|
+
const id = db.genId();
|
|
128
|
+
const now = new Date().toISOString();
|
|
129
|
+
db.execute(
|
|
130
|
+
`INSERT INTO opc_tasks (id, project_id, company_id, title, description, assignee, priority, status, due_date, hours_estimated, hours_actual, created_at, updated_at)
|
|
131
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'todo', ?, ?, 0, ?, ?)`,
|
|
132
|
+
id, p.project_id, p.company_id, p.title, p.description ?? "",
|
|
133
|
+
p.assignee ?? "", p.priority ?? "medium",
|
|
134
|
+
p.due_date ?? "", p.hours_estimated ?? 0, now, now,
|
|
135
|
+
);
|
|
136
|
+
return json(db.queryOne("SELECT * FROM opc_tasks WHERE id = ?", id));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
case "list_tasks": {
|
|
140
|
+
let sql = "SELECT * FROM opc_tasks WHERE project_id = ?";
|
|
141
|
+
const params2: unknown[] = [p.project_id];
|
|
142
|
+
if (p.status) { sql += " AND status = ?"; params2.push(p.status); }
|
|
143
|
+
sql += " ORDER BY CASE priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END, due_date";
|
|
144
|
+
return json(db.query(sql, ...params2));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
case "update_task": {
|
|
148
|
+
const fields: string[] = [];
|
|
149
|
+
const values: unknown[] = [];
|
|
150
|
+
if (p.status) { fields.push("status = ?"); values.push(p.status); }
|
|
151
|
+
if (p.hours_actual !== undefined) { fields.push("hours_actual = ?"); values.push(p.hours_actual); }
|
|
152
|
+
if (p.assignee) { fields.push("assignee = ?"); values.push(p.assignee); }
|
|
153
|
+
if (p.priority) { fields.push("priority = ?"); values.push(p.priority); }
|
|
154
|
+
if (p.due_date) { fields.push("due_date = ?"); values.push(p.due_date); }
|
|
155
|
+
fields.push("updated_at = ?"); values.push(new Date().toISOString());
|
|
156
|
+
values.push(p.task_id);
|
|
157
|
+
db.execute(`UPDATE opc_tasks SET ${fields.join(", ")} WHERE id = ?`, ...values);
|
|
158
|
+
return json(db.queryOne("SELECT * FROM opc_tasks WHERE id = ?", p.task_id) ?? { error: "任务不存在" });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
case "project_summary": {
|
|
162
|
+
const project = db.queryOne("SELECT * FROM opc_projects WHERE id = ?", p.project_id);
|
|
163
|
+
if (!project) return json({ error: "项目不存在" });
|
|
164
|
+
const tasks = db.query("SELECT status, COUNT(*) as count, SUM(hours_estimated) as est, SUM(hours_actual) as actual FROM opc_tasks WHERE project_id = ? GROUP BY status", p.project_id);
|
|
165
|
+
const overdue = db.query(
|
|
166
|
+
"SELECT * FROM opc_tasks WHERE project_id = ? AND status != 'done' AND due_date != '' AND due_date < date('now')",
|
|
167
|
+
p.project_id,
|
|
168
|
+
);
|
|
169
|
+
return json({ project, task_stats: tasks, overdue_tasks: overdue });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
case "kanban": {
|
|
173
|
+
const todo = db.query("SELECT * FROM opc_tasks WHERE project_id = ? AND status = 'todo' ORDER BY priority", p.project_id);
|
|
174
|
+
const inProgress = db.query("SELECT * FROM opc_tasks WHERE project_id = ? AND status = 'in_progress' ORDER BY priority", p.project_id);
|
|
175
|
+
const review = db.query("SELECT * FROM opc_tasks WHERE project_id = ? AND status = 'review' ORDER BY priority", p.project_id);
|
|
176
|
+
const done = db.query("SELECT * FROM opc_tasks WHERE project_id = ? AND status = 'done' ORDER BY updated_at DESC LIMIT 10", p.project_id);
|
|
177
|
+
return json({ todo, in_progress: inProgress, review, done });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
case "delete_project": {
|
|
181
|
+
db.execute("DELETE FROM opc_tasks WHERE project_id = ?", p.project_id);
|
|
182
|
+
db.execute("DELETE FROM opc_projects WHERE id = ?", p.project_id);
|
|
183
|
+
return json({ ok: true });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
case "delete_task": {
|
|
187
|
+
db.execute("DELETE FROM opc_tasks WHERE id = ?", p.task_id);
|
|
188
|
+
return json({ ok: true });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
default:
|
|
192
|
+
return json({ error: `未知操作: ${(p as { action: string }).action}` });
|
|
193
|
+
}
|
|
194
|
+
} catch (err) {
|
|
195
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
{ name: "opc_project" },
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
api.logger.info("opc: 已注册 opc_project 工具");
|
|
203
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 星环OPC中心 — TypeBox 工具参数 Schema
|
|
3
|
+
*
|
|
4
|
+
* 使用 Type.Union + action 字符串字段,兼容所有 LLM Provider。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Type, type Static } from "@sinclair/typebox";
|
|
8
|
+
|
|
9
|
+
// ── 公司管理 Schema ──────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const RegisterCompany = Type.Object({
|
|
12
|
+
action: Type.Literal("register_company"),
|
|
13
|
+
name: Type.String({ description: "公司名称" }),
|
|
14
|
+
industry: Type.String({ description: "所属行业" }),
|
|
15
|
+
owner_name: Type.String({ description: "创办人姓名" }),
|
|
16
|
+
owner_contact: Type.Optional(Type.String({ description: "创办人联系方式(手机/邮箱)" })),
|
|
17
|
+
registered_capital: Type.Optional(Type.Number({ description: "注册资本(元)" })),
|
|
18
|
+
description: Type.Optional(Type.String({ description: "公司简介" })),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const GetCompany = Type.Object({
|
|
22
|
+
action: Type.Literal("get_company"),
|
|
23
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const ListCompanies = Type.Object({
|
|
27
|
+
action: Type.Literal("list_companies"),
|
|
28
|
+
status: Type.Optional(
|
|
29
|
+
Type.String({ description: "按状态筛选: pending/active/suspended/acquired/packaged/terminated" }),
|
|
30
|
+
),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const UpdateCompany = Type.Object({
|
|
34
|
+
action: Type.Literal("update_company"),
|
|
35
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
36
|
+
name: Type.Optional(Type.String({ description: "新公司名称" })),
|
|
37
|
+
industry: Type.Optional(Type.String({ description: "新行业" })),
|
|
38
|
+
description: Type.Optional(Type.String({ description: "新简介" })),
|
|
39
|
+
owner_contact: Type.Optional(Type.String({ description: "新联系方式" })),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const ActivateCompany = Type.Object({
|
|
43
|
+
action: Type.Literal("activate_company"),
|
|
44
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const ChangeCompanyStatus = Type.Object({
|
|
48
|
+
action: Type.Literal("change_company_status"),
|
|
49
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
50
|
+
new_status: Type.String({ description: "目标状态: active/suspended/acquired/packaged/terminated" }),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ── 交易记录 Schema ──────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
const AddTransaction = Type.Object({
|
|
56
|
+
action: Type.Literal("add_transaction"),
|
|
57
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
58
|
+
type: Type.String({ description: "交易类型: income(收入) 或 expense(支出)" }),
|
|
59
|
+
category: Type.Optional(
|
|
60
|
+
Type.String({
|
|
61
|
+
description:
|
|
62
|
+
"分类: service_income/product_income/investment_income/salary/rent/utilities/marketing/tax/supplies/other",
|
|
63
|
+
}),
|
|
64
|
+
),
|
|
65
|
+
amount: Type.Number({ description: "金额(元)" }),
|
|
66
|
+
description: Type.Optional(Type.String({ description: "交易描述" })),
|
|
67
|
+
counterparty: Type.Optional(Type.String({ description: "交易对方" })),
|
|
68
|
+
transaction_date: Type.Optional(Type.String({ description: "交易日期 (YYYY-MM-DD),默认今天" })),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const ListTransactions = Type.Object({
|
|
72
|
+
action: Type.Literal("list_transactions"),
|
|
73
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
74
|
+
type: Type.Optional(Type.String({ description: "按类型筛选: income/expense" })),
|
|
75
|
+
start_date: Type.Optional(Type.String({ description: "起始日期 (YYYY-MM-DD)" })),
|
|
76
|
+
end_date: Type.Optional(Type.String({ description: "截止日期 (YYYY-MM-DD)" })),
|
|
77
|
+
limit: Type.Optional(Type.Number({ description: "返回条数,默认 50" })),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const GetFinanceSummary = Type.Object({
|
|
81
|
+
action: Type.Literal("finance_summary"),
|
|
82
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
83
|
+
start_date: Type.Optional(Type.String({ description: "起始日期 (YYYY-MM-DD)" })),
|
|
84
|
+
end_date: Type.Optional(Type.String({ description: "截止日期 (YYYY-MM-DD)" })),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ── 客户管理 Schema ──────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
const AddContact = Type.Object({
|
|
90
|
+
action: Type.Literal("add_contact"),
|
|
91
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
92
|
+
name: Type.String({ description: "联系人姓名" }),
|
|
93
|
+
phone: Type.Optional(Type.String({ description: "手机号" })),
|
|
94
|
+
email: Type.Optional(Type.String({ description: "邮箱" })),
|
|
95
|
+
company_name: Type.Optional(Type.String({ description: "联系人所在公司" })),
|
|
96
|
+
tags: Type.Optional(Type.String({ description: "标签,JSON 数组格式,如 [\"VIP\",\"供应商\"]" })),
|
|
97
|
+
notes: Type.Optional(Type.String({ description: "备注" })),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const ListContacts = Type.Object({
|
|
101
|
+
action: Type.Literal("list_contacts"),
|
|
102
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
103
|
+
tag: Type.Optional(Type.String({ description: "按标签筛选" })),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const UpdateContact = Type.Object({
|
|
107
|
+
action: Type.Literal("update_contact"),
|
|
108
|
+
contact_id: Type.String({ description: "联系人 ID" }),
|
|
109
|
+
name: Type.Optional(Type.String({ description: "新姓名" })),
|
|
110
|
+
phone: Type.Optional(Type.String({ description: "新手机号" })),
|
|
111
|
+
email: Type.Optional(Type.String({ description: "新邮箱" })),
|
|
112
|
+
company_name: Type.Optional(Type.String({ description: "新公司名" })),
|
|
113
|
+
tags: Type.Optional(Type.String({ description: "新标签" })),
|
|
114
|
+
notes: Type.Optional(Type.String({ description: "新备注" })),
|
|
115
|
+
last_contact_date: Type.Optional(Type.String({ description: "最近联系日期 (YYYY-MM-DD)" })),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const DeleteContact = Type.Object({
|
|
119
|
+
action: Type.Literal("delete_contact"),
|
|
120
|
+
contact_id: Type.String({ description: "联系人 ID" }),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ── Dashboard ────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
const GetDashboard = Type.Object({
|
|
126
|
+
action: Type.Literal("dashboard"),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ── Company Skills (OpenClaw agent-level skills) ──────────────
|
|
130
|
+
|
|
131
|
+
const SetCompanySkills = Type.Object({
|
|
132
|
+
action: Type.Literal("set_company_skills"),
|
|
133
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
134
|
+
skills: Type.Array(Type.String(), { description: "OpenClaw 内置 skill 列表,如 [\"company-registration\", \"basic-finance\"]" }),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const GetCompanySkills = Type.Object({
|
|
138
|
+
action: Type.Literal("get_company_skills"),
|
|
139
|
+
company_id: Type.String({ description: "公司 ID" }),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// ── Union Schema ─────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
export const OpcManageSchema = Type.Union([
|
|
145
|
+
RegisterCompany,
|
|
146
|
+
GetCompany,
|
|
147
|
+
ListCompanies,
|
|
148
|
+
UpdateCompany,
|
|
149
|
+
ActivateCompany,
|
|
150
|
+
ChangeCompanyStatus,
|
|
151
|
+
AddTransaction,
|
|
152
|
+
ListTransactions,
|
|
153
|
+
GetFinanceSummary,
|
|
154
|
+
AddContact,
|
|
155
|
+
ListContacts,
|
|
156
|
+
UpdateContact,
|
|
157
|
+
DeleteContact,
|
|
158
|
+
GetDashboard,
|
|
159
|
+
SetCompanySkills,
|
|
160
|
+
GetCompanySkills,
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
export type OpcManageParams = Static<typeof OpcManageSchema>;
|