fix(conversations): implement soft-delete for conversation deletion
The delete conversation endpoint was a no-op — it verified ownership but never actually modified the record. Users saw conversations disappear (frontend optimistic removal) but they reappeared on refresh. Changes: - conversation.entity.ts: Add DELETED status, softDelete() and isDeleted() - conversation.service.ts: Call softDelete() + update instead of no-op - conversation-postgres.repository.ts: Exclude DELETED conversations from findByUserId() queries so they don't appear in user's list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b75d607e2b
commit
d083008001
|
|
@ -38,7 +38,8 @@ export class ConversationPostgresRepository
|
||||||
options?: { status?: ConversationStatusType; limit?: number },
|
options?: { status?: ConversationStatusType; limit?: number },
|
||||||
): Promise<ConversationEntity[]> {
|
): Promise<ConversationEntity[]> {
|
||||||
const queryBuilder = this.createTenantQueryBuilder('conversation')
|
const queryBuilder = this.createTenantQueryBuilder('conversation')
|
||||||
.andWhere('conversation.user_id = :userId', { userId });
|
.andWhere('conversation.user_id = :userId', { userId })
|
||||||
|
.andWhere('conversation.status != :deletedStatus', { deletedStatus: 'DELETED' });
|
||||||
|
|
||||||
if (options?.status) {
|
if (options?.status) {
|
||||||
queryBuilder.andWhere('conversation.status = :status', { status: options.status });
|
queryBuilder.andWhere('conversation.status = :status', { status: options.status });
|
||||||
|
|
|
||||||
|
|
@ -308,11 +308,9 @@ export class ConversationService {
|
||||||
* Delete a conversation and its messages
|
* Delete a conversation and its messages
|
||||||
*/
|
*/
|
||||||
async deleteConversation(conversationId: string, userId: string): Promise<void> {
|
async deleteConversation(conversationId: string, userId: string): Promise<void> {
|
||||||
// Verify user owns the conversation
|
const conversation = await this.getConversation(conversationId, userId);
|
||||||
await this.getConversation(conversationId, userId);
|
conversation.softDelete();
|
||||||
|
await this.conversationRepo.update(conversation);
|
||||||
// Note: In a real application, you'd want to delete messages in the repository
|
|
||||||
// For now, we rely on database cascade or separate cleanup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export const ConversationStatus = {
|
||||||
ACTIVE: 'ACTIVE',
|
ACTIVE: 'ACTIVE',
|
||||||
ENDED: 'ENDED',
|
ENDED: 'ENDED',
|
||||||
ARCHIVED: 'ARCHIVED',
|
ARCHIVED: 'ARCHIVED',
|
||||||
|
DELETED: 'DELETED',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type ConversationStatusType =
|
export type ConversationStatusType =
|
||||||
|
|
@ -236,6 +237,11 @@ export class ConversationEntity {
|
||||||
this.updatedAt = new Date();
|
this.updatedAt = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
softDelete(): void {
|
||||||
|
this.status = ConversationStatus.DELETED;
|
||||||
|
this.updatedAt = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
isActive(): boolean {
|
isActive(): boolean {
|
||||||
return this.status === ConversationStatus.ACTIVE;
|
return this.status === ConversationStatus.ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
@ -243,4 +249,8 @@ export class ConversationEntity {
|
||||||
isEnded(): boolean {
|
isEnded(): boolean {
|
||||||
return this.status === ConversationStatus.ENDED;
|
return this.status === ConversationStatus.ENDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDeleted(): boolean {
|
||||||
|
return this.status === ConversationStatus.DELETED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue