dialog-survey 1.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/CHANGELOG.md +95 -0
- package/README.md +242 -0
- package/VERSION +1 -0
- package/dist/api/admin-templates.d.ts +15 -0
- package/dist/api/admin-templates.d.ts.map +1 -0
- package/dist/api/admin-templates.js +611 -0
- package/dist/api/admin-templates.js.map +1 -0
- package/dist/api/analysis.d.ts +8 -0
- package/dist/api/analysis.d.ts.map +1 -0
- package/dist/api/analysis.js +106 -0
- package/dist/api/analysis.js.map +1 -0
- package/dist/api/health.d.ts +6 -0
- package/dist/api/health.d.ts.map +1 -0
- package/dist/api/health.js +116 -0
- package/dist/api/health.js.map +1 -0
- package/dist/api/plans.d.ts +3 -0
- package/dist/api/plans.d.ts.map +1 -0
- package/dist/api/plans.js +184 -0
- package/dist/api/plans.js.map +1 -0
- package/dist/api/templates.d.ts +9 -0
- package/dist/api/templates.d.ts.map +1 -0
- package/dist/api/templates.js +211 -0
- package/dist/api/templates.js.map +1 -0
- package/dist/core/graph.d.ts +7 -0
- package/dist/core/graph.d.ts.map +1 -0
- package/dist/core/graph.js +31 -0
- package/dist/core/graph.js.map +1 -0
- package/dist/core/nodes/analyzing.d.ts +3 -0
- package/dist/core/nodes/analyzing.d.ts.map +1 -0
- package/dist/core/nodes/analyzing.js +38 -0
- package/dist/core/nodes/analyzing.js.map +1 -0
- package/dist/core/nodes/completed.d.ts +3 -0
- package/dist/core/nodes/completed.d.ts.map +1 -0
- package/dist/core/nodes/completed.js +8 -0
- package/dist/core/nodes/completed.js.map +1 -0
- package/dist/core/nodes/interviewing.d.ts +13 -0
- package/dist/core/nodes/interviewing.d.ts.map +1 -0
- package/dist/core/nodes/interviewing.js +130 -0
- package/dist/core/nodes/interviewing.js.map +1 -0
- package/dist/core/nodes/planning.d.ts +15 -0
- package/dist/core/nodes/planning.d.ts.map +1 -0
- package/dist/core/nodes/planning.js +40 -0
- package/dist/core/nodes/planning.js.map +1 -0
- package/dist/core/types/index.d.ts +69 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +49 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/domains/interview/entities/interview.entity.d.ts +21 -0
- package/dist/domains/interview/entities/interview.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/interview.entity.js +20 -0
- package/dist/domains/interview/entities/interview.entity.js.map +1 -0
- package/dist/domains/interview/entities/message.entity.d.ts +15 -0
- package/dist/domains/interview/entities/message.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/message.entity.js +11 -0
- package/dist/domains/interview/entities/message.entity.js.map +1 -0
- package/dist/domains/interview/entities/response.entity.d.ts +17 -0
- package/dist/domains/interview/entities/response.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/response.entity.js +12 -0
- package/dist/domains/interview/entities/response.entity.js.map +1 -0
- package/dist/domains/interview/services/interview.domain.d.ts +8 -0
- package/dist/domains/interview/services/interview.domain.d.ts.map +1 -0
- package/dist/domains/interview/services/interview.domain.js +30 -0
- package/dist/domains/interview/services/interview.domain.js.map +1 -0
- package/dist/integrations/dingtalk/client.d.ts +32 -0
- package/dist/integrations/dingtalk/client.d.ts.map +1 -0
- package/dist/integrations/dingtalk/client.js +89 -0
- package/dist/integrations/dingtalk/client.js.map +1 -0
- package/dist/integrations/dingtalk/message-sender.d.ts +24 -0
- package/dist/integrations/dingtalk/message-sender.d.ts.map +1 -0
- package/dist/integrations/dingtalk/message-sender.js +81 -0
- package/dist/integrations/dingtalk/message-sender.js.map +1 -0
- package/dist/integrations/dingtalk/middleware.d.ts +4 -0
- package/dist/integrations/dingtalk/middleware.d.ts.map +1 -0
- package/dist/integrations/dingtalk/middleware.js +29 -0
- package/dist/integrations/dingtalk/middleware.js.map +1 -0
- package/dist/integrations/dingtalk/stream-client.d.ts +112 -0
- package/dist/integrations/dingtalk/stream-client.d.ts.map +1 -0
- package/dist/integrations/dingtalk/stream-client.js +305 -0
- package/dist/integrations/dingtalk/stream-client.js.map +1 -0
- package/dist/integrations/dingtalk/token-manager.d.ts +22 -0
- package/dist/integrations/dingtalk/token-manager.d.ts.map +1 -0
- package/dist/integrations/dingtalk/token-manager.js +109 -0
- package/dist/integrations/dingtalk/token-manager.js.map +1 -0
- package/dist/integrations/llm/alibaba.d.ts +10 -0
- package/dist/integrations/llm/alibaba.d.ts.map +1 -0
- package/dist/integrations/llm/alibaba.js +60 -0
- package/dist/integrations/llm/alibaba.js.map +1 -0
- package/dist/integrations/llm/base.d.ts +28 -0
- package/dist/integrations/llm/base.d.ts.map +1 -0
- package/dist/integrations/llm/base.js +2 -0
- package/dist/integrations/llm/base.js.map +1 -0
- package/dist/integrations/llm/volcengine.d.ts +12 -0
- package/dist/integrations/llm/volcengine.d.ts.map +1 -0
- package/dist/integrations/llm/volcengine.js +80 -0
- package/dist/integrations/llm/volcengine.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +3 -0
- package/dist/middleware/admin-auth.d.ts.map +1 -0
- package/dist/middleware/admin-auth.js +37 -0
- package/dist/middleware/admin-auth.js.map +1 -0
- package/dist/middleware/api-auth.d.ts +10 -0
- package/dist/middleware/api-auth.d.ts.map +1 -0
- package/dist/middleware/api-auth.js +30 -0
- package/dist/middleware/api-auth.js.map +1 -0
- package/dist/repositories/interview-state-mapper.d.ts +28 -0
- package/dist/repositories/interview-state-mapper.d.ts.map +1 -0
- package/dist/repositories/interview-state-mapper.js +32 -0
- package/dist/repositories/interview-state-mapper.js.map +1 -0
- package/dist/repositories/interview-state.repository.d.ts +34 -0
- package/dist/repositories/interview-state.repository.d.ts.map +1 -0
- package/dist/repositories/interview-state.repository.js +220 -0
- package/dist/repositories/interview-state.repository.js.map +1 -0
- package/dist/repositories/interview.repository.d.ts +29 -0
- package/dist/repositories/interview.repository.d.ts.map +1 -0
- package/dist/repositories/interview.repository.js +36 -0
- package/dist/repositories/interview.repository.js.map +1 -0
- package/dist/repositories/message.repository.d.ts +17 -0
- package/dist/repositories/message.repository.d.ts.map +1 -0
- package/dist/repositories/message.repository.js +37 -0
- package/dist/repositories/message.repository.js.map +1 -0
- package/dist/repositories/template.repository.d.ts +117 -0
- package/dist/repositories/template.repository.d.ts.map +1 -0
- package/dist/repositories/template.repository.js +280 -0
- package/dist/repositories/template.repository.js.map +1 -0
- package/dist/schemas/dimensions.d.ts +61 -0
- package/dist/schemas/dimensions.d.ts.map +1 -0
- package/dist/schemas/dimensions.js +44 -0
- package/dist/schemas/dimensions.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +217 -0
- package/dist/server.js.map +1 -0
- package/dist/services/analysis.service.d.ts +81 -0
- package/dist/services/analysis.service.d.ts.map +1 -0
- package/dist/services/analysis.service.js +229 -0
- package/dist/services/analysis.service.js.map +1 -0
- package/dist/services/analytics.service.d.ts +44 -0
- package/dist/services/analytics.service.d.ts.map +1 -0
- package/dist/services/analytics.service.js +222 -0
- package/dist/services/analytics.service.js.map +1 -0
- package/dist/services/asr.service.d.ts +9 -0
- package/dist/services/asr.service.d.ts.map +1 -0
- package/dist/services/asr.service.js +41 -0
- package/dist/services/asr.service.js.map +1 -0
- package/dist/services/audit-cleanup.service.d.ts +9 -0
- package/dist/services/audit-cleanup.service.d.ts.map +1 -0
- package/dist/services/audit-cleanup.service.js +27 -0
- package/dist/services/audit-cleanup.service.js.map +1 -0
- package/dist/services/batch-aggregation.service.d.ts +34 -0
- package/dist/services/batch-aggregation.service.d.ts.map +1 -0
- package/dist/services/batch-aggregation.service.js +91 -0
- package/dist/services/batch-aggregation.service.js.map +1 -0
- package/dist/services/conversation-engine.d.ts +16 -0
- package/dist/services/conversation-engine.d.ts.map +1 -0
- package/dist/services/conversation-engine.js +49 -0
- package/dist/services/conversation-engine.js.map +1 -0
- package/dist/services/dead-letter.service.d.ts +12 -0
- package/dist/services/dead-letter.service.d.ts.map +1 -0
- package/dist/services/dead-letter.service.js +24 -0
- package/dist/services/dead-letter.service.js.map +1 -0
- package/dist/services/followup.service.d.ts +18 -0
- package/dist/services/followup.service.d.ts.map +1 -0
- package/dist/services/followup.service.js +146 -0
- package/dist/services/followup.service.js.map +1 -0
- package/dist/services/interview-plan-base.service.d.ts +226 -0
- package/dist/services/interview-plan-base.service.d.ts.map +1 -0
- package/dist/services/interview-plan-base.service.js +217 -0
- package/dist/services/interview-plan-base.service.js.map +1 -0
- package/dist/services/interview-plan-members.service.d.ts +45 -0
- package/dist/services/interview-plan-members.service.d.ts.map +1 -0
- package/dist/services/interview-plan-members.service.js +237 -0
- package/dist/services/interview-plan-members.service.js.map +1 -0
- package/dist/services/interview-plan-send.service.d.ts +27 -0
- package/dist/services/interview-plan-send.service.d.ts.map +1 -0
- package/dist/services/interview-plan-send.service.js +250 -0
- package/dist/services/interview-plan-send.service.js.map +1 -0
- package/dist/services/interview-plan.service.d.ts +8 -0
- package/dist/services/interview-plan.service.d.ts.map +1 -0
- package/dist/services/interview-plan.service.js +8 -0
- package/dist/services/interview-plan.service.js.map +1 -0
- package/dist/services/llm-clustering.d.ts +16 -0
- package/dist/services/llm-clustering.d.ts.map +1 -0
- package/dist/services/llm-clustering.js +91 -0
- package/dist/services/llm-clustering.js.map +1 -0
- package/dist/services/prompt.service.d.ts +13 -0
- package/dist/services/prompt.service.d.ts.map +1 -0
- package/dist/services/prompt.service.js +151 -0
- package/dist/services/prompt.service.js.map +1 -0
- package/dist/services/report.service.d.ts +28 -0
- package/dist/services/report.service.d.ts.map +1 -0
- package/dist/services/report.service.js +169 -0
- package/dist/services/report.service.js.map +1 -0
- package/dist/services/stream-message-utils.d.ts +31 -0
- package/dist/services/stream-message-utils.d.ts.map +1 -0
- package/dist/services/stream-message-utils.js +80 -0
- package/dist/services/stream-message-utils.js.map +1 -0
- package/dist/services/stream-message.service.d.ts +29 -0
- package/dist/services/stream-message.service.d.ts.map +1 -0
- package/dist/services/stream-message.service.js +184 -0
- package/dist/services/stream-message.service.js.map +1 -0
- package/dist/services/template-dimension.service.d.ts +21 -0
- package/dist/services/template-dimension.service.d.ts.map +1 -0
- package/dist/services/template-dimension.service.js +9 -0
- package/dist/services/template-dimension.service.js.map +1 -0
- package/dist/utils/db.d.ts +10 -0
- package/dist/utils/db.d.ts.map +1 -0
- package/dist/utils/db.js +21 -0
- package/dist/utils/db.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/markdown.d.ts +2 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +39 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/pii-anonymizer.d.ts +2 -0
- package/dist/utils/pii-anonymizer.d.ts.map +1 -0
- package/dist/utils/pii-anonymizer.js +13 -0
- package/dist/utils/pii-anonymizer.js.map +1 -0
- package/dist/utils/retry.d.ts +8 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +28 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/security.d.ts +26 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +99 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +80 -0
- package/prisma/schema.prisma +228 -0
- package/public/css/admin.css +73 -0
- package/public/js/alpine.min.js +2670 -0
- package/public/js/htmx.min.js +3422 -0
- package/scripts/cli.mjs +794 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { SendStatus } from '@prisma/client';
|
|
2
|
+
import { DingTalkClient } from '../integrations/dingtalk/client.js';
|
|
3
|
+
import { messageSender } from '../integrations/dingtalk/message-sender.js';
|
|
4
|
+
import { error, info } from '../utils/logger.js';
|
|
5
|
+
import { InterviewPlanSendService } from './interview-plan-send.service.js';
|
|
6
|
+
export class PlanNotFoundError extends Error {
|
|
7
|
+
code = 'PLAN_NOT_FOUND';
|
|
8
|
+
constructor(planId) {
|
|
9
|
+
super(`Plan not found: ${planId}`);
|
|
10
|
+
this.name = 'PlanNotFoundError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class InterviewNotFoundError extends Error {
|
|
14
|
+
code = 'INTERVIEW_NOT_FOUND';
|
|
15
|
+
constructor(interviewId) {
|
|
16
|
+
super(`Interview not found: ${interviewId}`);
|
|
17
|
+
this.name = 'InterviewNotFoundError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class MemberConflictError extends Error {
|
|
21
|
+
code = 'MEMBER_CONFLICT';
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'MemberConflictError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class MemberNotFoundError extends Error {
|
|
28
|
+
code = 'MEMBER_NOT_FOUND';
|
|
29
|
+
constructor(message) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'MemberNotFoundError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class InvalidMemberInputError extends Error {
|
|
35
|
+
code = 'INVALID_MEMBER_INPUT';
|
|
36
|
+
constructor(message) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'InvalidMemberInputError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class InvalidStateError extends Error {
|
|
42
|
+
code = 'INVALID_STATE';
|
|
43
|
+
constructor(message) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = 'InvalidStateError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function maskPhone(phone) {
|
|
49
|
+
if (phone.length < 7)
|
|
50
|
+
return '****';
|
|
51
|
+
return `${phone.slice(0, 3)}****${phone.slice(-4)}`;
|
|
52
|
+
}
|
|
53
|
+
function normalizePhone(phone) {
|
|
54
|
+
return phone.replace(/\s+/g, '').replace(/^\+86/, '');
|
|
55
|
+
}
|
|
56
|
+
export class InterviewPlanService extends InterviewPlanSendService {
|
|
57
|
+
dingTalkClient;
|
|
58
|
+
constructor(prisma, dingTalkClient) {
|
|
59
|
+
super(prisma);
|
|
60
|
+
this.dingTalkClient = dingTalkClient ?? DingTalkClient.fromEnv();
|
|
61
|
+
}
|
|
62
|
+
async addMember(planId, input) {
|
|
63
|
+
let { userId, name } = input;
|
|
64
|
+
const { phone } = input;
|
|
65
|
+
// Validate: at least one of userId or phone must be provided
|
|
66
|
+
if (!userId && !phone) {
|
|
67
|
+
throw new InvalidMemberInputError('Either userId or phone is required');
|
|
68
|
+
}
|
|
69
|
+
// If userId provided, ignore phone entirely - skip DingTalk call
|
|
70
|
+
if (!userId && phone) {
|
|
71
|
+
// Resolve userId from phone via DingTalk API
|
|
72
|
+
const normalizedPhone = normalizePhone(phone);
|
|
73
|
+
const lookupResult = await this.dingTalkClient.getUserIdByMobile(normalizedPhone);
|
|
74
|
+
if (!lookupResult.found) {
|
|
75
|
+
throw new MemberNotFoundError(`Phone number ${maskPhone(phone)} not found in DingTalk`);
|
|
76
|
+
}
|
|
77
|
+
userId = lookupResult.userId;
|
|
78
|
+
// If name not provided by client, use name from DingTalk API response
|
|
79
|
+
if (!name) {
|
|
80
|
+
name = lookupResult.name;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!userId) {
|
|
84
|
+
throw new InvalidMemberInputError('userId is required after phone resolution');
|
|
85
|
+
}
|
|
86
|
+
const newInterviewId = await this.prisma.$transaction(async (tx) => {
|
|
87
|
+
const plan = await tx.interviewPlan.findUnique({ where: { id: planId } });
|
|
88
|
+
if (!plan) {
|
|
89
|
+
throw new PlanNotFoundError(planId);
|
|
90
|
+
}
|
|
91
|
+
// Check userId conflict first
|
|
92
|
+
const existingByUserId = await tx.interview.findFirst({
|
|
93
|
+
where: { planId, userId },
|
|
94
|
+
});
|
|
95
|
+
if (existingByUserId) {
|
|
96
|
+
throw new MemberConflictError(`Member already exists in plan: ${userId}`);
|
|
97
|
+
}
|
|
98
|
+
// Phone mode: check phone conflict in inviteeData JSON
|
|
99
|
+
if (phone) {
|
|
100
|
+
const normalizedPhone = normalizePhone(phone);
|
|
101
|
+
const existingInvitees = Array.isArray(plan.inviteeData)
|
|
102
|
+
? plan.inviteeData
|
|
103
|
+
: [];
|
|
104
|
+
const existingByPhone = existingInvitees.find((inv) => inv.phone && normalizePhone(inv.phone) === normalizedPhone);
|
|
105
|
+
if (existingByPhone) {
|
|
106
|
+
throw new MemberConflictError(`Member with phone ${maskPhone(phone)} already exists in plan`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const interview = await tx.interview.create({
|
|
110
|
+
data: {
|
|
111
|
+
userId,
|
|
112
|
+
templateId: plan.templateId,
|
|
113
|
+
planId,
|
|
114
|
+
status: 'PENDING',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
const existingInvitees = Array.isArray(plan.inviteeData)
|
|
118
|
+
? plan.inviteeData
|
|
119
|
+
: [];
|
|
120
|
+
// Only save phone to inviteeData if we're in phone mode (not userId-only mode)
|
|
121
|
+
const updatedInvitees = phone
|
|
122
|
+
? [...existingInvitees, { userId, name, phone: normalizePhone(phone) }]
|
|
123
|
+
: [...existingInvitees, { userId, name }];
|
|
124
|
+
await tx.interviewPlan.update({
|
|
125
|
+
where: { id: planId },
|
|
126
|
+
data: {
|
|
127
|
+
inviteeData: updatedInvitees,
|
|
128
|
+
updatedBy: 'admin',
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
return interview.id;
|
|
132
|
+
});
|
|
133
|
+
info('Member added to plan', { planId, userId, interviewId: newInterviewId });
|
|
134
|
+
return { interviewId: newInterviewId };
|
|
135
|
+
}
|
|
136
|
+
async removeMember(planId, interviewId) {
|
|
137
|
+
await this.prisma.$transaction(async (tx) => {
|
|
138
|
+
const interview = await tx.interview.findUnique({ where: { id: interviewId } });
|
|
139
|
+
if (!interview) {
|
|
140
|
+
throw new InterviewNotFoundError(interviewId);
|
|
141
|
+
}
|
|
142
|
+
if (interview.planId !== planId) {
|
|
143
|
+
throw new InvalidStateError(`Interview does not belong to plan: ${interviewId}`);
|
|
144
|
+
}
|
|
145
|
+
if (interview.status === 'COMPLETED') {
|
|
146
|
+
throw new InvalidStateError(`Cannot remove completed interview: ${interviewId}`);
|
|
147
|
+
}
|
|
148
|
+
const plan = await tx.interviewPlan.findUnique({ where: { id: planId } });
|
|
149
|
+
if (!plan) {
|
|
150
|
+
throw new PlanNotFoundError(planId);
|
|
151
|
+
}
|
|
152
|
+
const wasCounted = interview.sendStatus === SendStatus.SENT || interview.sendStatus === SendStatus.FAILED;
|
|
153
|
+
const wasFailed = interview.sendStatus === SendStatus.FAILED;
|
|
154
|
+
await tx.interview.delete({ where: { id: interviewId } });
|
|
155
|
+
const existingInvitees = Array.isArray(plan.inviteeData)
|
|
156
|
+
? plan.inviteeData
|
|
157
|
+
: [];
|
|
158
|
+
const updatedInvitees = existingInvitees.filter((inv) => inv.userId !== interview.userId);
|
|
159
|
+
const planUpdate = {
|
|
160
|
+
inviteeData: updatedInvitees,
|
|
161
|
+
updatedBy: 'admin',
|
|
162
|
+
};
|
|
163
|
+
if (wasCounted) {
|
|
164
|
+
planUpdate.sentCount = { decrement: 1 };
|
|
165
|
+
}
|
|
166
|
+
if (wasFailed) {
|
|
167
|
+
planUpdate.failedCount = { decrement: 1 };
|
|
168
|
+
}
|
|
169
|
+
await tx.interviewPlan.update({
|
|
170
|
+
where: { id: planId },
|
|
171
|
+
data: planUpdate,
|
|
172
|
+
});
|
|
173
|
+
info('Member removed from plan', {
|
|
174
|
+
planId,
|
|
175
|
+
interviewId,
|
|
176
|
+
userId: interview.userId,
|
|
177
|
+
wasCounted,
|
|
178
|
+
wasFailed,
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
async sendReminder(planId, interviewId) {
|
|
183
|
+
const plan = await this.prisma.interviewPlan.findUnique({ where: { id: planId } });
|
|
184
|
+
if (!plan) {
|
|
185
|
+
throw new PlanNotFoundError(planId);
|
|
186
|
+
}
|
|
187
|
+
let targets;
|
|
188
|
+
if (interviewId) {
|
|
189
|
+
const interview = await this.prisma.interview.findUnique({ where: { id: interviewId } });
|
|
190
|
+
if (!interview || interview.planId !== planId) {
|
|
191
|
+
throw new InterviewNotFoundError(interviewId);
|
|
192
|
+
}
|
|
193
|
+
if (interview.status === 'COMPLETED' || interview.status === 'CANCELLED') {
|
|
194
|
+
return { reminded: 0, failed: 0 };
|
|
195
|
+
}
|
|
196
|
+
targets = [{ id: interview.id, userId: interview.userId }];
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
targets = await this.prisma.interview.findMany({
|
|
200
|
+
where: {
|
|
201
|
+
planId,
|
|
202
|
+
status: { notIn: ['COMPLETED', 'CANCELLED'] },
|
|
203
|
+
},
|
|
204
|
+
select: { id: true, userId: true },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (targets.length === 0) {
|
|
208
|
+
return { reminded: 0, failed: 0 };
|
|
209
|
+
}
|
|
210
|
+
const message = `【访谈提醒】您参与的访谈 "${plan.name}" 尚未完成,请抽空回复访谈邀约消息继续完成。`;
|
|
211
|
+
const concurrency = 10;
|
|
212
|
+
let reminded = 0;
|
|
213
|
+
let failed = 0;
|
|
214
|
+
for (let i = 0; i < targets.length; i += concurrency) {
|
|
215
|
+
const batch = targets.slice(i, i + concurrency);
|
|
216
|
+
const results = await Promise.allSettled(batch.map((target) => messageSender.sendTextMessage([target.userId], message)));
|
|
217
|
+
for (let j = 0; j < results.length; j++) {
|
|
218
|
+
const result = results[j];
|
|
219
|
+
if (result.status === 'fulfilled') {
|
|
220
|
+
reminded++;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
failed++;
|
|
224
|
+
const errMsg = result.reason instanceof Error ? result.reason.message : 'send failed';
|
|
225
|
+
error('Reminder send failed', {
|
|
226
|
+
planId,
|
|
227
|
+
userId: batch[j].userId,
|
|
228
|
+
error: errMsg,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
info('Reminders sent', { planId, reminded, failed });
|
|
234
|
+
return { reminded, failed };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=interview-plan-members.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interview-plan-members.service.js","sourceRoot":"","sources":["../../src/services/interview-plan-members.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PlanStatus } from '@prisma/client';
|
|
2
|
+
import { InterviewPlanServiceBase } from './interview-plan-base.service.js';
|
|
3
|
+
import type { CreateAndPublishInput, ImportResult, InviteeData } from './interview-plan-base.service.js';
|
|
4
|
+
export declare class InterviewPlanSendService extends InterviewPlanServiceBase {
|
|
5
|
+
importInvitees(planId: string, invitees: InviteeData[]): Promise<ImportResult>;
|
|
6
|
+
sendInvitations(planId: string): Promise<{
|
|
7
|
+
sent: number;
|
|
8
|
+
failed: number;
|
|
9
|
+
}>;
|
|
10
|
+
resendToInterview(planId: string, interviewId: string): Promise<{
|
|
11
|
+
success: boolean;
|
|
12
|
+
error?: string;
|
|
13
|
+
}>;
|
|
14
|
+
private sendInvitation;
|
|
15
|
+
updatePlanStatus(planId: string, status: PlanStatus): Promise<void>;
|
|
16
|
+
pausePlan(planId: string): Promise<void>;
|
|
17
|
+
resumePlan(planId: string): Promise<void>;
|
|
18
|
+
cancelPlan(planId: string): Promise<void>;
|
|
19
|
+
createAndPublish(input: CreateAndPublishInput): Promise<{
|
|
20
|
+
planId: string;
|
|
21
|
+
imported: number;
|
|
22
|
+
sent: number;
|
|
23
|
+
failed: number;
|
|
24
|
+
errors: string[];
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=interview-plan-send.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interview-plan-send.service.d.ts","sourceRoot":"","sources":["../../src/services/interview-plan-send.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,gBAAgB,CAAC;AAGhE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EACZ,WAAW,EACZ,MAAM,kCAAkC,CAAC;AAG1C,qBAAa,wBAAyB,SAAQ,wBAAwB;IAC9D,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAyC9E,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAmF1E,iBAAiB,CACrB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;YA+ElC,cAAc;IAWtB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC;QAC5D,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CAgCH"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { PlanStatus, SendStatus } from '@prisma/client';
|
|
2
|
+
import { messageSender } from '../integrations/dingtalk/message-sender.js';
|
|
3
|
+
import { error, info, warn } from '../utils/logger.js';
|
|
4
|
+
import { InterviewPlanServiceBase } from './interview-plan-base.service.js';
|
|
5
|
+
import { parseInviteeText } from './interview-plan-base.service.js';
|
|
6
|
+
export class InterviewPlanSendService extends InterviewPlanServiceBase {
|
|
7
|
+
async importInvitees(planId, invitees) {
|
|
8
|
+
const result = { success: 0, failed: 0, errors: [] };
|
|
9
|
+
const plan = await this.prisma.interviewPlan.findUnique({ where: { id: planId } });
|
|
10
|
+
if (!plan)
|
|
11
|
+
throw new Error(`Plan not found: ${planId}`);
|
|
12
|
+
const existingUserIds = new Set((await this.prisma.interview.findMany({ where: { planId }, select: { userId: true } })).map((i) => i.userId));
|
|
13
|
+
const uniqueInvitees = new Map();
|
|
14
|
+
for (const inv of invitees)
|
|
15
|
+
uniqueInvitees.set(inv.userId, inv);
|
|
16
|
+
const interviews = [];
|
|
17
|
+
for (const invitee of uniqueInvitees.values()) {
|
|
18
|
+
if (existingUserIds.has(invitee.userId)) {
|
|
19
|
+
result.failed++;
|
|
20
|
+
result.errors.push(`User ${invitee.userId} already exists in plan`);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
interviews.push({
|
|
24
|
+
userId: invitee.userId,
|
|
25
|
+
templateId: plan.templateId,
|
|
26
|
+
planId: planId,
|
|
27
|
+
status: 'PENDING',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (interviews.length > 0) {
|
|
31
|
+
await this.prisma.interview.createMany({ data: interviews });
|
|
32
|
+
result.success = interviews.length;
|
|
33
|
+
}
|
|
34
|
+
await this.prisma.interviewPlan.update({
|
|
35
|
+
where: { id: planId },
|
|
36
|
+
data: {
|
|
37
|
+
inviteeData: Array.from(uniqueInvitees.values()),
|
|
38
|
+
status: PlanStatus.READY,
|
|
39
|
+
updatedBy: 'admin',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
info('Invitees imported', { planId, success: result.success, failed: result.failed });
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
async sendInvitations(planId) {
|
|
46
|
+
const plan = await this.prisma.interviewPlan.findUnique({
|
|
47
|
+
where: { id: planId },
|
|
48
|
+
include: {
|
|
49
|
+
template: { select: { content: true } },
|
|
50
|
+
interviews: {
|
|
51
|
+
where: { status: 'PENDING' },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
if (!plan) {
|
|
56
|
+
throw new Error(`Plan not found: ${planId}`);
|
|
57
|
+
}
|
|
58
|
+
let invitationPrompt = '';
|
|
59
|
+
if (plan.template?.content) {
|
|
60
|
+
try {
|
|
61
|
+
const content = JSON.parse(plan.template.content);
|
|
62
|
+
invitationPrompt = content.invitationPrompt || '';
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
warn('Failed to parse template content', {
|
|
66
|
+
templateId: plan.templateId,
|
|
67
|
+
error: err instanceof Error ? err.message : String(err),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let sent = 0;
|
|
72
|
+
let failed = 0;
|
|
73
|
+
for (const interview of plan.interviews) {
|
|
74
|
+
try {
|
|
75
|
+
await this.sendInvitation(interview.userId, plan.name, invitationPrompt);
|
|
76
|
+
sent++;
|
|
77
|
+
await this.prisma.interview.update({
|
|
78
|
+
where: { id: interview.id },
|
|
79
|
+
data: { sendStatus: SendStatus.SENT, sentAt: new Date() },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
failed++;
|
|
84
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
85
|
+
error('Failed to send invitation', {
|
|
86
|
+
planId,
|
|
87
|
+
userId: interview.userId,
|
|
88
|
+
error: errorMsg,
|
|
89
|
+
});
|
|
90
|
+
await this.prisma.interview.update({
|
|
91
|
+
where: { id: interview.id },
|
|
92
|
+
data: { sendStatus: SendStatus.FAILED, sendError: errorMsg },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
let planSendStatus;
|
|
97
|
+
if (failed > 0 && sent > 0) {
|
|
98
|
+
planSendStatus = SendStatus.SENT;
|
|
99
|
+
}
|
|
100
|
+
else if (failed > 0 && sent === 0) {
|
|
101
|
+
planSendStatus = SendStatus.FAILED;
|
|
102
|
+
}
|
|
103
|
+
else if (failed === 0 && sent > 0) {
|
|
104
|
+
planSendStatus = SendStatus.SENT;
|
|
105
|
+
}
|
|
106
|
+
const planUpdateData = {
|
|
107
|
+
status: PlanStatus.RUNNING,
|
|
108
|
+
sentCount: { increment: sent },
|
|
109
|
+
failedCount: { increment: failed },
|
|
110
|
+
startedAt: plan.startedAt || new Date(),
|
|
111
|
+
updatedBy: 'admin',
|
|
112
|
+
};
|
|
113
|
+
if (planSendStatus !== undefined) {
|
|
114
|
+
planUpdateData.sendStatus = planSendStatus;
|
|
115
|
+
}
|
|
116
|
+
await this.prisma.interviewPlan.update({
|
|
117
|
+
where: { id: planId },
|
|
118
|
+
data: planUpdateData,
|
|
119
|
+
});
|
|
120
|
+
info('Invitations sent', { planId, sent, failed });
|
|
121
|
+
return { sent, failed };
|
|
122
|
+
}
|
|
123
|
+
async resendToInterview(planId, interviewId) {
|
|
124
|
+
const interview = await this.prisma.interview.findUnique({
|
|
125
|
+
where: { id: interviewId },
|
|
126
|
+
select: {
|
|
127
|
+
id: true,
|
|
128
|
+
userId: true,
|
|
129
|
+
sendStatus: true,
|
|
130
|
+
plan: { select: { id: true, name: true, templateId: true } },
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
if (!interview) {
|
|
134
|
+
return { success: false, error: `Interview not found: ${interviewId}` };
|
|
135
|
+
}
|
|
136
|
+
if (interview.plan?.id !== planId) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
error: `Interview ${interviewId} does not belong to plan ${planId}`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const template = await this.prisma.template.findUnique({
|
|
143
|
+
where: { id: interview.plan.templateId },
|
|
144
|
+
select: { content: true },
|
|
145
|
+
});
|
|
146
|
+
let invitationPrompt = '';
|
|
147
|
+
if (template?.content) {
|
|
148
|
+
try {
|
|
149
|
+
const content = JSON.parse(template.content);
|
|
150
|
+
invitationPrompt = content.invitationPrompt || '';
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
warn('Failed to parse template content', {
|
|
154
|
+
templateId: interview.plan.templateId,
|
|
155
|
+
error: err instanceof Error ? err.message : String(err),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const wasAlreadyCounted = interview.sendStatus === SendStatus.SENT || interview.sendStatus === SendStatus.FAILED;
|
|
160
|
+
try {
|
|
161
|
+
await this.sendInvitation(interview.userId, interview.plan.name, invitationPrompt);
|
|
162
|
+
await this.prisma.interview.update({
|
|
163
|
+
where: { id: interviewId },
|
|
164
|
+
data: { sendStatus: SendStatus.SENT, sentAt: new Date(), sendError: null },
|
|
165
|
+
});
|
|
166
|
+
if (!wasAlreadyCounted) {
|
|
167
|
+
await this.prisma.interviewPlan.update({
|
|
168
|
+
where: { id: planId },
|
|
169
|
+
data: { sentCount: { increment: 1 } },
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
info('Resent invitation', { planId, interviewId, userId: interview.userId });
|
|
173
|
+
return { success: true };
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
177
|
+
await this.prisma.interview.update({
|
|
178
|
+
where: { id: interviewId },
|
|
179
|
+
data: { sendStatus: SendStatus.FAILED, sendError: errorMsg },
|
|
180
|
+
});
|
|
181
|
+
if (!wasAlreadyCounted) {
|
|
182
|
+
await this.prisma.interviewPlan.update({
|
|
183
|
+
where: { id: planId },
|
|
184
|
+
data: { failedCount: { increment: 1 } },
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
error('Failed to resend invitation', {
|
|
188
|
+
planId,
|
|
189
|
+
interviewId,
|
|
190
|
+
userId: interview.userId,
|
|
191
|
+
error: errorMsg,
|
|
192
|
+
});
|
|
193
|
+
return { success: false, error: errorMsg };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async sendInvitation(userId, planName, invitationPrompt) {
|
|
197
|
+
const message = invitationPrompt
|
|
198
|
+
? `${invitationPrompt}\n\n(来自「${planName}」访谈。请直接在钉钉中回复 OpenClaw小钉 任意消息即可开始访谈。)`
|
|
199
|
+
: `您被邀请参与「${planName}」访谈。请直接在钉钉中回复 OpenClaw小钉 任意消息(如"你好"或"开始")即可开始访谈。`;
|
|
200
|
+
await messageSender.sendTextMessage([userId], message);
|
|
201
|
+
}
|
|
202
|
+
async updatePlanStatus(planId, status) {
|
|
203
|
+
const updateData = { status, updatedBy: 'admin' };
|
|
204
|
+
if (status === PlanStatus.COMPLETED) {
|
|
205
|
+
updateData.completedAt = new Date();
|
|
206
|
+
}
|
|
207
|
+
await this.prisma.interviewPlan.update({
|
|
208
|
+
where: { id: planId },
|
|
209
|
+
data: updateData,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async pausePlan(planId) {
|
|
213
|
+
await this.updatePlanStatus(planId, PlanStatus.PAUSED);
|
|
214
|
+
}
|
|
215
|
+
async resumePlan(planId) {
|
|
216
|
+
await this.updatePlanStatus(planId, PlanStatus.RUNNING);
|
|
217
|
+
}
|
|
218
|
+
async cancelPlan(planId) {
|
|
219
|
+
await this.updatePlanStatus(planId, PlanStatus.CANCELLED);
|
|
220
|
+
}
|
|
221
|
+
async createAndPublish(input) {
|
|
222
|
+
const planId = await this.createPlan({
|
|
223
|
+
name: input.name,
|
|
224
|
+
description: input.description,
|
|
225
|
+
templateId: input.templateId,
|
|
226
|
+
targetDate: input.targetDate ? new Date(input.targetDate) : undefined,
|
|
227
|
+
schedule: input.schedule,
|
|
228
|
+
});
|
|
229
|
+
const invitees = parseInviteeText(input.invitees);
|
|
230
|
+
if (invitees.length === 0) {
|
|
231
|
+
return { planId, imported: 0, sent: 0, failed: 0, errors: ['未提供有效的受访人员'] };
|
|
232
|
+
}
|
|
233
|
+
const importResult = await this.importInvitees(planId, invitees);
|
|
234
|
+
let sent = 0;
|
|
235
|
+
let failed = 0;
|
|
236
|
+
if (input.publish === true) {
|
|
237
|
+
const sendResult = await this.sendInvitations(planId);
|
|
238
|
+
sent = sendResult.sent;
|
|
239
|
+
failed = sendResult.failed;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
planId,
|
|
243
|
+
imported: importResult.success,
|
|
244
|
+
sent,
|
|
245
|
+
failed,
|
|
246
|
+
errors: importResult.errors,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=interview-plan-send.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interview-plan-send.service.js","sourceRoot":"","sources":["../../src/services/interview-plan-send.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { parseInviteeText } from './interview-plan-base.service.js';
|
|
2
|
+
export type { CreatePlanInput, CreateAndPublishInput, InviteeData, ImportResult, } from './interview-plan-base.service.js';
|
|
3
|
+
export { InterviewPlanServiceBase } from './interview-plan-base.service.js';
|
|
4
|
+
export { InterviewPlanSendService } from './interview-plan-send.service.js';
|
|
5
|
+
export { InterviewPlanService } from './interview-plan-members.service.js';
|
|
6
|
+
export { PlanNotFoundError, InterviewNotFoundError, MemberConflictError, MemberNotFoundError, InvalidMemberInputError, InvalidStateError, } from './interview-plan-members.service.js';
|
|
7
|
+
export type { AddMemberInput } from './interview-plan-members.service.js';
|
|
8
|
+
//# sourceMappingURL=interview-plan.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interview-plan.service.d.ts","sourceRoot":"","sources":["../../src/services/interview-plan.service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,WAAW,EACX,YAAY,GACb,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qCAAqC,CAAC;AAE7C,YAAY,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Barrel file: re-exports from split modules for backwards compatibility.
|
|
2
|
+
// All existing imports from './interview-plan.service.js' continue to work.
|
|
3
|
+
export { parseInviteeText } from './interview-plan-base.service.js';
|
|
4
|
+
export { InterviewPlanServiceBase } from './interview-plan-base.service.js';
|
|
5
|
+
export { InterviewPlanSendService } from './interview-plan-send.service.js';
|
|
6
|
+
export { InterviewPlanService } from './interview-plan-members.service.js';
|
|
7
|
+
export { PlanNotFoundError, InterviewNotFoundError, MemberConflictError, MemberNotFoundError, InvalidMemberInputError, InvalidStateError, } from './interview-plan-members.service.js';
|
|
8
|
+
//# sourceMappingURL=interview-plan.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interview-plan.service.js","sourceRoot":"","sources":["../../src/services/interview-plan.service.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,4EAA4E;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAOpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qCAAqC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type LLMService } from '../integrations/llm/base.js';
|
|
2
|
+
export interface SubTheme {
|
|
3
|
+
subTopicName: string;
|
|
4
|
+
mentionCount: number;
|
|
5
|
+
overallSentiment: string;
|
|
6
|
+
representativeQuotes: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface EmergentIssue {
|
|
9
|
+
name: string;
|
|
10
|
+
mentionCount: number;
|
|
11
|
+
urgency: 'high' | 'medium' | 'low';
|
|
12
|
+
representativeQuotes: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare function clusterDimensionQuotes(quotes: string[], dimension: string, llm: LLMService): Promise<SubTheme[]>;
|
|
15
|
+
export declare function discoverEmergentIssues(allEmergents: string[], llm: LLMService): Promise<EmergentIssue[]>;
|
|
16
|
+
//# sourceMappingURL=llm-clustering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-clustering.d.ts","sourceRoot":"","sources":["../../src/services/llm-clustering.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,MAAM,WAAW,QAAQ;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EAAE,EAChB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,UAAU,GACd,OAAO,CAAC,QAAQ,EAAE,CAAC,CA6BrB;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EAAE,EACtB,GAAG,EAAE,UAAU,GACd,OAAO,CAAC,aAAa,EAAE,CAAC,CA6B1B"}
|