@grapadigital/shared-schemas 1.0.191 → 1.0.193
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/dist/schemas/action.schema.d.ts +19 -0
- package/dist/schemas/action.schema.js +48 -1
- package/dist/schemas/comment.schema.d.ts +13 -2
- package/dist/schemas/comment.schema.js +64 -4
- package/package.json +1 -1
- package/src/schemas/action.schema.ts +37 -0
- package/src/schemas/comment.schema.ts +69 -5
|
@@ -60,6 +60,24 @@ export declare const ResponsiblesSchema: MongooseSchema<Responsibles, import("mo
|
|
|
60
60
|
}>;
|
|
61
61
|
export declare const ACTION_BUSINESS_UNIT_VALUES: readonly ["service", "comercial", "squad_a", "squad_b"];
|
|
62
62
|
export type ActionBusinessUnit = (typeof ACTION_BUSINESS_UNIT_VALUES)[number];
|
|
63
|
+
export declare class ActionPromptConfig {
|
|
64
|
+
context?: string;
|
|
65
|
+
targetPrompt?: string;
|
|
66
|
+
influencerPrompt?: string;
|
|
67
|
+
brandPrompt?: string;
|
|
68
|
+
contentPrompt?: string;
|
|
69
|
+
polarityPrompt?: string;
|
|
70
|
+
funnelPrompt?: string;
|
|
71
|
+
}
|
|
72
|
+
export declare const ActionPromptConfigSchema: MongooseSchema<ActionPromptConfig, import("mongoose").Model<ActionPromptConfig, any, any, any, import("mongoose").Document<unknown, any, ActionPromptConfig, any, {}> & ActionPromptConfig & {
|
|
73
|
+
_id: Types.ObjectId;
|
|
74
|
+
} & {
|
|
75
|
+
__v: number;
|
|
76
|
+
}, any>, {}, {}, {}, {}, import("mongoose").DefaultSchemaOptions, ActionPromptConfig, import("mongoose").Document<unknown, {}, import("mongoose").FlatRecord<ActionPromptConfig>, {}, import("mongoose").ResolveSchemaOptions<import("mongoose").DefaultSchemaOptions>> & import("mongoose").FlatRecord<ActionPromptConfig> & {
|
|
77
|
+
_id: Types.ObjectId;
|
|
78
|
+
} & {
|
|
79
|
+
__v: number;
|
|
80
|
+
}>;
|
|
63
81
|
export declare const ACTION_STATUS_VALUES: readonly ["config", "inProgress", "finished", "archived", "cancelled"];
|
|
64
82
|
export type ActionStatus = (typeof ACTION_STATUS_VALUES)[number];
|
|
65
83
|
export declare class Action {
|
|
@@ -102,6 +120,7 @@ export declare class Action {
|
|
|
102
120
|
tags?: Types.ObjectId[];
|
|
103
121
|
businessUnit?: ActionBusinessUnit;
|
|
104
122
|
campaign?: Types.ObjectId | Campaign;
|
|
123
|
+
promptConfig?: ActionPromptConfig | null;
|
|
105
124
|
}
|
|
106
125
|
export declare const ActionSchema: MongooseSchema<Action, import("mongoose").Model<Action, any, any, any, import("mongoose").Document<unknown, any, Action, any, {}> & Action & Required<{
|
|
107
126
|
_id: Types.ObjectId;
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.ActionModel = exports.ActionSchema = exports.Action = exports.ACTION_STATUS_VALUES = exports.ACTION_BUSINESS_UNIT_VALUES = exports.ResponsiblesSchema = exports.Responsibles = exports.ResponsibleSchema = exports.Responsible = void 0;
|
|
12
|
+
exports.ActionModel = exports.ActionSchema = exports.Action = exports.ACTION_STATUS_VALUES = exports.ActionPromptConfigSchema = exports.ActionPromptConfig = exports.ACTION_BUSINESS_UNIT_VALUES = exports.ResponsiblesSchema = exports.Responsibles = exports.ResponsibleSchema = exports.Responsible = void 0;
|
|
13
13
|
const mongoose_1 = require("@nestjs/mongoose");
|
|
14
14
|
const swagger_1 = require("@nestjs/swagger");
|
|
15
15
|
const mongoose_2 = require("mongoose");
|
|
@@ -64,6 +64,48 @@ exports.ACTION_BUSINESS_UNIT_VALUES = [
|
|
|
64
64
|
'squad_a',
|
|
65
65
|
'squad_b',
|
|
66
66
|
];
|
|
67
|
+
let ActionPromptConfig = class ActionPromptConfig {
|
|
68
|
+
};
|
|
69
|
+
__decorate([
|
|
70
|
+
(0, swagger_1.ApiProperty)({ required: false, description: 'Contexto da campanha (cliente, influencer, produto) para os prompts de classificação de comentário por IA' }),
|
|
71
|
+
(0, mongoose_1.Prop)({ type: String, required: false }),
|
|
72
|
+
__metadata("design:type", String)
|
|
73
|
+
], ActionPromptConfig.prototype, "context", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, swagger_1.ApiProperty)({ required: false, description: 'Prompt customizado do estágio "alvo" (target)' }),
|
|
76
|
+
(0, mongoose_1.Prop)({ type: String, required: false }),
|
|
77
|
+
__metadata("design:type", String)
|
|
78
|
+
], ActionPromptConfig.prototype, "targetPrompt", void 0);
|
|
79
|
+
__decorate([
|
|
80
|
+
(0, swagger_1.ApiProperty)({ required: false, description: 'Prompt customizado do estágio "assunto" (subject) para o grupo Influencer' }),
|
|
81
|
+
(0, mongoose_1.Prop)({ type: String, required: false }),
|
|
82
|
+
__metadata("design:type", String)
|
|
83
|
+
], ActionPromptConfig.prototype, "influencerPrompt", void 0);
|
|
84
|
+
__decorate([
|
|
85
|
+
(0, swagger_1.ApiProperty)({ required: false, description: 'Prompt customizado do estágio "assunto" (subject) para o grupo Marca' }),
|
|
86
|
+
(0, mongoose_1.Prop)({ type: String, required: false }),
|
|
87
|
+
__metadata("design:type", String)
|
|
88
|
+
], ActionPromptConfig.prototype, "brandPrompt", void 0);
|
|
89
|
+
__decorate([
|
|
90
|
+
(0, swagger_1.ApiProperty)({ required: false, description: 'Prompt customizado do estágio "assunto" (subject) para o grupo Conteúdo' }),
|
|
91
|
+
(0, mongoose_1.Prop)({ type: String, required: false }),
|
|
92
|
+
__metadata("design:type", String)
|
|
93
|
+
], ActionPromptConfig.prototype, "contentPrompt", void 0);
|
|
94
|
+
__decorate([
|
|
95
|
+
(0, swagger_1.ApiProperty)({ required: false, description: 'Prompt customizado do estágio "polaridade" (polarity)' }),
|
|
96
|
+
(0, mongoose_1.Prop)({ type: String, required: false }),
|
|
97
|
+
__metadata("design:type", String)
|
|
98
|
+
], ActionPromptConfig.prototype, "polarityPrompt", void 0);
|
|
99
|
+
__decorate([
|
|
100
|
+
(0, swagger_1.ApiProperty)({ required: false, description: 'Prompt customizado do estágio "funil" (funnel)' }),
|
|
101
|
+
(0, mongoose_1.Prop)({ type: String, required: false }),
|
|
102
|
+
__metadata("design:type", String)
|
|
103
|
+
], ActionPromptConfig.prototype, "funnelPrompt", void 0);
|
|
104
|
+
ActionPromptConfig = __decorate([
|
|
105
|
+
(0, mongoose_1.Schema)({ _id: false })
|
|
106
|
+
], ActionPromptConfig);
|
|
107
|
+
exports.ActionPromptConfig = ActionPromptConfig;
|
|
108
|
+
exports.ActionPromptConfigSchema = mongoose_1.SchemaFactory.createForClass(ActionPromptConfig);
|
|
67
109
|
exports.ACTION_STATUS_VALUES = [
|
|
68
110
|
'config',
|
|
69
111
|
'inProgress',
|
|
@@ -283,6 +325,11 @@ __decorate([
|
|
|
283
325
|
(0, mongoose_1.Prop)({ type: mongoose_2.Schema.Types.ObjectId, ref: 'Campaign', required: false }),
|
|
284
326
|
__metadata("design:type", Object)
|
|
285
327
|
], Action.prototype, "campaign", void 0);
|
|
328
|
+
__decorate([
|
|
329
|
+
(0, swagger_1.ApiProperty)({ type: () => ActionPromptConfig, required: false, description: 'Customização de prompts de classificação de comentário por IA para esta action — campos ausentes caem no prompt genérico default' }),
|
|
330
|
+
(0, mongoose_1.Prop)({ type: exports.ActionPromptConfigSchema, required: false, default: null }),
|
|
331
|
+
__metadata("design:type", Object)
|
|
332
|
+
], Action.prototype, "promptConfig", void 0);
|
|
286
333
|
Action = __decorate([
|
|
287
334
|
(0, mongoose_1.Schema)({ timestamps: true })
|
|
288
335
|
], Action);
|
|
@@ -27,6 +27,12 @@ import { HydratedDocument, Schema as MongooseSchema, Types } from 'mongoose';
|
|
|
27
27
|
import { Content } from './content.schema';
|
|
28
28
|
import { Action } from './action.schema';
|
|
29
29
|
export type CommentDocument = HydratedDocument<Comment>;
|
|
30
|
+
export declare const COMMENT_TARGET_VALUES: readonly ["brand", "influencer", "content", "emoji", "spam"];
|
|
31
|
+
export type CommentTarget = (typeof COMMENT_TARGET_VALUES)[number];
|
|
32
|
+
export declare const COMMENT_SUBJECT_VALUES: readonly ["compliment_to_person", "criticism", "question", "brand_recognition", "influencer_response", "account", "reaction", "doubt", "about_story", "about_self", "competition", "customer_service", "tagged_someone", "other", "emoji_reaction"];
|
|
33
|
+
export type CommentSubject = (typeof COMMENT_SUBJECT_VALUES)[number];
|
|
34
|
+
export declare const COMMENT_CLASSIFICATION_STATUS_VALUES: readonly ["pending", "processing", "completed", "failed", "not_applicable"];
|
|
35
|
+
export type CommentClassificationStatus = (typeof COMMENT_CLASSIFICATION_STATUS_VALUES)[number];
|
|
30
36
|
export declare class Comment {
|
|
31
37
|
_id?: Types.ObjectId;
|
|
32
38
|
content: Types.ObjectId | Content;
|
|
@@ -40,11 +46,16 @@ export declare class Comment {
|
|
|
40
46
|
postedAt: Date;
|
|
41
47
|
likeCount?: number;
|
|
42
48
|
parent?: Types.ObjectId | Comment | null;
|
|
43
|
-
target?:
|
|
49
|
+
target?: CommentTarget | null;
|
|
50
|
+
subject?: CommentSubject | null;
|
|
44
51
|
polarity?: 'positive' | 'negative' | 'neutral' | null;
|
|
45
|
-
funnel?: 'awareness' | 'consideration' | 'conversion' | null;
|
|
52
|
+
funnel?: 'awareness' | 'consideration' | 'conversion' | 'none' | null;
|
|
46
53
|
isEmojiOnly?: boolean;
|
|
47
54
|
isSpam?: boolean;
|
|
55
|
+
classificationStatus?: CommentClassificationStatus;
|
|
56
|
+
classificationError?: string | null;
|
|
57
|
+
classificationProcessedAt?: Date | null;
|
|
58
|
+
sourceCommentId?: Types.ObjectId | Comment | null;
|
|
48
59
|
createdAt?: Date;
|
|
49
60
|
updatedAt?: Date;
|
|
50
61
|
}
|
|
@@ -9,9 +9,34 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.CommentModel = exports.CommentSchema = exports.Comment = void 0;
|
|
12
|
+
exports.CommentModel = exports.CommentSchema = exports.Comment = exports.COMMENT_CLASSIFICATION_STATUS_VALUES = exports.COMMENT_SUBJECT_VALUES = exports.COMMENT_TARGET_VALUES = void 0;
|
|
13
13
|
const mongoose_1 = require("@nestjs/mongoose");
|
|
14
14
|
const mongoose_2 = require("mongoose");
|
|
15
|
+
exports.COMMENT_TARGET_VALUES = ['brand', 'influencer', 'content', 'emoji', 'spam'];
|
|
16
|
+
exports.COMMENT_SUBJECT_VALUES = [
|
|
17
|
+
'compliment_to_person',
|
|
18
|
+
'criticism',
|
|
19
|
+
'question',
|
|
20
|
+
'brand_recognition',
|
|
21
|
+
'influencer_response',
|
|
22
|
+
'account',
|
|
23
|
+
'reaction',
|
|
24
|
+
'doubt',
|
|
25
|
+
'about_story',
|
|
26
|
+
'about_self',
|
|
27
|
+
'competition',
|
|
28
|
+
'customer_service',
|
|
29
|
+
'tagged_someone',
|
|
30
|
+
'other',
|
|
31
|
+
'emoji_reaction',
|
|
32
|
+
];
|
|
33
|
+
exports.COMMENT_CLASSIFICATION_STATUS_VALUES = [
|
|
34
|
+
'pending',
|
|
35
|
+
'processing',
|
|
36
|
+
'completed',
|
|
37
|
+
'failed',
|
|
38
|
+
'not_applicable',
|
|
39
|
+
];
|
|
15
40
|
let Comment = class Comment {
|
|
16
41
|
};
|
|
17
42
|
__decorate([
|
|
@@ -65,12 +90,21 @@ __decorate([
|
|
|
65
90
|
__decorate([
|
|
66
91
|
(0, mongoose_1.Prop)({
|
|
67
92
|
type: String,
|
|
68
|
-
enum:
|
|
93
|
+
enum: exports.COMMENT_TARGET_VALUES,
|
|
69
94
|
required: false,
|
|
70
95
|
default: null,
|
|
71
96
|
}),
|
|
72
97
|
__metadata("design:type", Object)
|
|
73
98
|
], Comment.prototype, "target", void 0);
|
|
99
|
+
__decorate([
|
|
100
|
+
(0, mongoose_1.Prop)({
|
|
101
|
+
type: String,
|
|
102
|
+
enum: exports.COMMENT_SUBJECT_VALUES,
|
|
103
|
+
required: false,
|
|
104
|
+
default: null,
|
|
105
|
+
}),
|
|
106
|
+
__metadata("design:type", Object)
|
|
107
|
+
], Comment.prototype, "subject", void 0);
|
|
74
108
|
__decorate([
|
|
75
109
|
(0, mongoose_1.Prop)({
|
|
76
110
|
type: String,
|
|
@@ -83,7 +117,7 @@ __decorate([
|
|
|
83
117
|
__decorate([
|
|
84
118
|
(0, mongoose_1.Prop)({
|
|
85
119
|
type: String,
|
|
86
|
-
enum: ['awareness', 'consideration', 'conversion'],
|
|
120
|
+
enum: ['awareness', 'consideration', 'conversion', 'none'],
|
|
87
121
|
required: false,
|
|
88
122
|
default: null,
|
|
89
123
|
}),
|
|
@@ -97,6 +131,27 @@ __decorate([
|
|
|
97
131
|
(0, mongoose_1.Prop)({ type: Boolean, required: false, default: false }),
|
|
98
132
|
__metadata("design:type", Boolean)
|
|
99
133
|
], Comment.prototype, "isSpam", void 0);
|
|
134
|
+
__decorate([
|
|
135
|
+
(0, mongoose_1.Prop)({
|
|
136
|
+
type: String,
|
|
137
|
+
enum: exports.COMMENT_CLASSIFICATION_STATUS_VALUES,
|
|
138
|
+
required: false,
|
|
139
|
+
default: 'pending',
|
|
140
|
+
}),
|
|
141
|
+
__metadata("design:type", String)
|
|
142
|
+
], Comment.prototype, "classificationStatus", void 0);
|
|
143
|
+
__decorate([
|
|
144
|
+
(0, mongoose_1.Prop)({ type: String, required: false, default: null }),
|
|
145
|
+
__metadata("design:type", Object)
|
|
146
|
+
], Comment.prototype, "classificationError", void 0);
|
|
147
|
+
__decorate([
|
|
148
|
+
(0, mongoose_1.Prop)({ type: Date, required: false, default: null }),
|
|
149
|
+
__metadata("design:type", Object)
|
|
150
|
+
], Comment.prototype, "classificationProcessedAt", void 0);
|
|
151
|
+
__decorate([
|
|
152
|
+
(0, mongoose_1.Prop)({ type: mongoose_2.Schema.Types.ObjectId, ref: 'Comment', required: false, default: null }),
|
|
153
|
+
__metadata("design:type", Object)
|
|
154
|
+
], Comment.prototype, "sourceCommentId", void 0);
|
|
100
155
|
__decorate([
|
|
101
156
|
(0, mongoose_1.Prop)({ default: Date.now, required: false }),
|
|
102
157
|
__metadata("design:type", Date)
|
|
@@ -110,8 +165,13 @@ Comment = __decorate([
|
|
|
110
165
|
], Comment);
|
|
111
166
|
exports.Comment = Comment;
|
|
112
167
|
exports.CommentSchema = mongoose_1.SchemaFactory.createForClass(Comment);
|
|
113
|
-
|
|
168
|
+
// partialFilterExpression não aceita $exists:false (só $exists:true) — usamos
|
|
169
|
+
// igualdade com null, que no Mongo casa tanto "campo ausente" quanto "campo
|
|
170
|
+
// null" (cobre o documento original, sem exigir que quem insere via
|
|
171
|
+
// bulkWrite raw declare sourceCommentId explicitamente).
|
|
172
|
+
exports.CommentSchema.index({ content: 1, externalId: 1 }, { unique: true, partialFilterExpression: { sourceCommentId: null } });
|
|
114
173
|
exports.CommentSchema.index({ action: 1, funnel: 1 });
|
|
115
174
|
exports.CommentSchema.index({ action: 1, target: 1 });
|
|
116
175
|
exports.CommentSchema.index({ content: 1, parent: 1 });
|
|
176
|
+
exports.CommentSchema.index({ content: 1, classificationStatus: 1 });
|
|
117
177
|
exports.CommentModel = (0, mongoose_2.model)('Comment', exports.CommentSchema);
|
package/package.json
CHANGED
|
@@ -55,6 +55,39 @@ export const ACTION_BUSINESS_UNIT_VALUES = [
|
|
|
55
55
|
] as const;
|
|
56
56
|
export type ActionBusinessUnit = (typeof ACTION_BUSINESS_UNIT_VALUES)[number];
|
|
57
57
|
|
|
58
|
+
@Schema({ _id: false })
|
|
59
|
+
export class ActionPromptConfig {
|
|
60
|
+
@ApiProperty({ required: false, description: 'Contexto da campanha (cliente, influencer, produto) para os prompts de classificação de comentário por IA' })
|
|
61
|
+
@Prop({ type: String, required: false })
|
|
62
|
+
context?: string;
|
|
63
|
+
|
|
64
|
+
@ApiProperty({ required: false, description: 'Prompt customizado do estágio "alvo" (target)' })
|
|
65
|
+
@Prop({ type: String, required: false })
|
|
66
|
+
targetPrompt?: string;
|
|
67
|
+
|
|
68
|
+
@ApiProperty({ required: false, description: 'Prompt customizado do estágio "assunto" (subject) para o grupo Influencer' })
|
|
69
|
+
@Prop({ type: String, required: false })
|
|
70
|
+
influencerPrompt?: string;
|
|
71
|
+
|
|
72
|
+
@ApiProperty({ required: false, description: 'Prompt customizado do estágio "assunto" (subject) para o grupo Marca' })
|
|
73
|
+
@Prop({ type: String, required: false })
|
|
74
|
+
brandPrompt?: string;
|
|
75
|
+
|
|
76
|
+
@ApiProperty({ required: false, description: 'Prompt customizado do estágio "assunto" (subject) para o grupo Conteúdo' })
|
|
77
|
+
@Prop({ type: String, required: false })
|
|
78
|
+
contentPrompt?: string;
|
|
79
|
+
|
|
80
|
+
@ApiProperty({ required: false, description: 'Prompt customizado do estágio "polaridade" (polarity)' })
|
|
81
|
+
@Prop({ type: String, required: false })
|
|
82
|
+
polarityPrompt?: string;
|
|
83
|
+
|
|
84
|
+
@ApiProperty({ required: false, description: 'Prompt customizado do estágio "funil" (funnel)' })
|
|
85
|
+
@Prop({ type: String, required: false })
|
|
86
|
+
funnelPrompt?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const ActionPromptConfigSchema = SchemaFactory.createForClass(ActionPromptConfig);
|
|
90
|
+
|
|
58
91
|
export const ACTION_STATUS_VALUES = [
|
|
59
92
|
'config',
|
|
60
93
|
'inProgress',
|
|
@@ -236,6 +269,10 @@ export class Action {
|
|
|
236
269
|
@ApiProperty({ type: () => Campaign, required: false })
|
|
237
270
|
@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'Campaign', required: false })
|
|
238
271
|
campaign?: Types.ObjectId | Campaign;
|
|
272
|
+
|
|
273
|
+
@ApiProperty({ type: () => ActionPromptConfig, required: false, description: 'Customização de prompts de classificação de comentário por IA para esta action — campos ausentes caem no prompt genérico default' })
|
|
274
|
+
@Prop({ type: ActionPromptConfigSchema, required: false, default: null })
|
|
275
|
+
promptConfig?: ActionPromptConfig | null;
|
|
239
276
|
}
|
|
240
277
|
|
|
241
278
|
export const ActionSchema = SchemaFactory.createForClass(Action);
|
|
@@ -10,6 +10,37 @@ import { Action } from './action.schema';
|
|
|
10
10
|
|
|
11
11
|
export type CommentDocument = HydratedDocument<Comment>;
|
|
12
12
|
|
|
13
|
+
export const COMMENT_TARGET_VALUES = ['brand', 'influencer', 'content', 'emoji', 'spam'] as const;
|
|
14
|
+
export type CommentTarget = (typeof COMMENT_TARGET_VALUES)[number];
|
|
15
|
+
|
|
16
|
+
export const COMMENT_SUBJECT_VALUES = [
|
|
17
|
+
'compliment_to_person',
|
|
18
|
+
'criticism',
|
|
19
|
+
'question',
|
|
20
|
+
'brand_recognition',
|
|
21
|
+
'influencer_response',
|
|
22
|
+
'account',
|
|
23
|
+
'reaction',
|
|
24
|
+
'doubt',
|
|
25
|
+
'about_story',
|
|
26
|
+
'about_self',
|
|
27
|
+
'competition',
|
|
28
|
+
'customer_service',
|
|
29
|
+
'tagged_someone',
|
|
30
|
+
'other',
|
|
31
|
+
'emoji_reaction',
|
|
32
|
+
] as const;
|
|
33
|
+
export type CommentSubject = (typeof COMMENT_SUBJECT_VALUES)[number];
|
|
34
|
+
|
|
35
|
+
export const COMMENT_CLASSIFICATION_STATUS_VALUES = [
|
|
36
|
+
'pending',
|
|
37
|
+
'processing',
|
|
38
|
+
'completed',
|
|
39
|
+
'failed',
|
|
40
|
+
'not_applicable',
|
|
41
|
+
] as const;
|
|
42
|
+
export type CommentClassificationStatus = (typeof COMMENT_CLASSIFICATION_STATUS_VALUES)[number];
|
|
43
|
+
|
|
13
44
|
@Schema({ timestamps: true })
|
|
14
45
|
export class Comment {
|
|
15
46
|
@Prop({ type: MongooseSchema.Types.ObjectId, auto: true, required: false })
|
|
@@ -50,11 +81,19 @@ export class Comment {
|
|
|
50
81
|
|
|
51
82
|
@Prop({
|
|
52
83
|
type: String,
|
|
53
|
-
enum:
|
|
84
|
+
enum: COMMENT_TARGET_VALUES,
|
|
54
85
|
required: false,
|
|
55
86
|
default: null,
|
|
56
87
|
})
|
|
57
|
-
target?:
|
|
88
|
+
target?: CommentTarget | null;
|
|
89
|
+
|
|
90
|
+
@Prop({
|
|
91
|
+
type: String,
|
|
92
|
+
enum: COMMENT_SUBJECT_VALUES,
|
|
93
|
+
required: false,
|
|
94
|
+
default: null,
|
|
95
|
+
})
|
|
96
|
+
subject?: CommentSubject | null;
|
|
58
97
|
|
|
59
98
|
@Prop({
|
|
60
99
|
type: String,
|
|
@@ -66,11 +105,11 @@ export class Comment {
|
|
|
66
105
|
|
|
67
106
|
@Prop({
|
|
68
107
|
type: String,
|
|
69
|
-
enum: ['awareness', 'consideration', 'conversion'],
|
|
108
|
+
enum: ['awareness', 'consideration', 'conversion', 'none'],
|
|
70
109
|
required: false,
|
|
71
110
|
default: null,
|
|
72
111
|
})
|
|
73
|
-
funnel?: 'awareness' | 'consideration' | 'conversion' | null;
|
|
112
|
+
funnel?: 'awareness' | 'consideration' | 'conversion' | 'none' | null;
|
|
74
113
|
|
|
75
114
|
@Prop({ type: Boolean, required: false, default: false })
|
|
76
115
|
isEmojiOnly?: boolean;
|
|
@@ -78,6 +117,23 @@ export class Comment {
|
|
|
78
117
|
@Prop({ type: Boolean, required: false, default: false })
|
|
79
118
|
isSpam?: boolean;
|
|
80
119
|
|
|
120
|
+
@Prop({
|
|
121
|
+
type: String,
|
|
122
|
+
enum: COMMENT_CLASSIFICATION_STATUS_VALUES,
|
|
123
|
+
required: false,
|
|
124
|
+
default: 'pending',
|
|
125
|
+
})
|
|
126
|
+
classificationStatus?: CommentClassificationStatus;
|
|
127
|
+
|
|
128
|
+
@Prop({ type: String, required: false, default: null })
|
|
129
|
+
classificationError?: string | null;
|
|
130
|
+
|
|
131
|
+
@Prop({ type: Date, required: false, default: null })
|
|
132
|
+
classificationProcessedAt?: Date | null;
|
|
133
|
+
|
|
134
|
+
@Prop({ type: MongooseSchema.Types.ObjectId, ref: 'Comment', required: false, default: null })
|
|
135
|
+
sourceCommentId?: Types.ObjectId | Comment | null;
|
|
136
|
+
|
|
81
137
|
@Prop({ default: Date.now, required: false })
|
|
82
138
|
createdAt?: Date;
|
|
83
139
|
|
|
@@ -87,9 +143,17 @@ export class Comment {
|
|
|
87
143
|
|
|
88
144
|
export const CommentSchema = SchemaFactory.createForClass(Comment);
|
|
89
145
|
|
|
90
|
-
|
|
146
|
+
// partialFilterExpression não aceita $exists:false (só $exists:true) — usamos
|
|
147
|
+
// igualdade com null, que no Mongo casa tanto "campo ausente" quanto "campo
|
|
148
|
+
// null" (cobre o documento original, sem exigir que quem insere via
|
|
149
|
+
// bulkWrite raw declare sourceCommentId explicitamente).
|
|
150
|
+
CommentSchema.index(
|
|
151
|
+
{ content: 1, externalId: 1 },
|
|
152
|
+
{ unique: true, partialFilterExpression: { sourceCommentId: null } },
|
|
153
|
+
);
|
|
91
154
|
CommentSchema.index({ action: 1, funnel: 1 });
|
|
92
155
|
CommentSchema.index({ action: 1, target: 1 });
|
|
93
156
|
CommentSchema.index({ content: 1, parent: 1 });
|
|
157
|
+
CommentSchema.index({ content: 1, classificationStatus: 1 });
|
|
94
158
|
|
|
95
159
|
export const CommentModel = MongooseModel('Comment', CommentSchema);
|