import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service.js"; import { SentimentLedgerService } from "../relationship/sentiment-ledger.service.js"; import { EmotionService } from "../emotion/emotion.service.js"; import { TASK_LEDGER_EVENT, TASK_LEDGER_WEIGHT } from "./constants.js"; // J-5 任務與記憶、關係的迴路:完成/失敗誠實回報(引擎層),但角色演出可以是遺憾/得意的語氣。 @Injectable() export class TaskFeedbackService { constructor( private readonly prisma: PrismaService, private readonly sentimentLedger: SentimentLedgerService, private readonly emotion: EmotionService, ) {} async complete(taskId: string, now: Date = new Date()) { const task = await this.prisma.client.task.findUnique({ where: { id: taskId } }); if (!task) { throw new NotFoundException(`任務不存在:${taskId}`); } await this.prisma.client.task.update({ where: { id: taskId }, data: { status: "COMPLETED", completedAt: now }, }); await this.sentimentLedger.recordEvent( task.characterId, task.userId, TASK_LEDGER_EVENT.COMPLETED, TASK_LEDGER_WEIGHT.COMPLETED, { now }, ); return this.prisma.client.task.findUnique({ where: { id: taskId } }); } async fail(taskId: string, now: Date = new Date()) { const task = await this.prisma.client.task.findUnique({ where: { id: taskId } }); if (!task) { throw new NotFoundException(`任務不存在:${taskId}`); } await this.prisma.client.task.update({ where: { id: taskId }, data: { status: "FAILED" } }); // 誠實回報失敗(引擎層行為固定),負向偏誤已由 E-2 機制放大,不需要在這裡重複加權。 await this.sentimentLedger.recordEvent( task.characterId, task.userId, TASK_LEDGER_EVENT.FAILED, TASK_LEDGER_WEIGHT.FAILED, { now }, ); return this.prisma.client.task.findUnique({ where: { id: taskId } }); } // 觸發後被忽視(追擊次數已達門檻仍無回應):輕微失落,且如實記錄一筆負向事件。 async markIgnored(taskId: string, now: Date = new Date()) { const task = await this.prisma.client.task.findUnique({ where: { id: taskId } }); if (!task) { throw new NotFoundException(`任務不存在:${taskId}`); } await this.sentimentLedger.recordEvent( task.characterId, task.userId, TASK_LEDGER_EVENT.IGNORED, TASK_LEDGER_WEIGHT.IGNORED, { now }, ); await this.emotion.processInput(task.characterId, "", { now, baselineSignal: { tag: "SAD", intensity: 0.3 }, }); } }