| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- import { Injectable, NotFoundException, BadRequestException, Inject, forwardRef } from '@nestjs/common';
- import { FFBLangChainService } from '../../FFB/services/ffb-langchain.service';
- import { MongoCoreService } from '../../mongo/mongo-core.service';
- import { BlockRepository } from '../repositories/block.repository';
- import { Block } from '../schemas/site.schema';
- import { PhaseRepository } from '../repositories/phase.repository';
- import { SiteRepository } from '../repositories/site.repository';
- import { FFBProductionService } from '../../FFB/services/ffb-production.service';
- @Injectable()
- export class BlockService {
- private repo: BlockRepository;
- private phaseRepo: PhaseRepository;
- private siteRepo: SiteRepository;
- constructor(
- private readonly mongoCore: MongoCoreService,
- @Inject(forwardRef(() => FFBProductionService))
- private readonly ffbService: FFBProductionService,
- private readonly ffbLangChainService: FFBLangChainService,
- ) { }
- private async getRepos() {
- if (!this.repo) {
- const db = await this.mongoCore.getDb();
- this.repo = new BlockRepository(db);
- this.phaseRepo = new PhaseRepository(db);
- this.siteRepo = new SiteRepository(db);
- await this.repo.init();
- await this.phaseRepo.init();
- await this.siteRepo.init();
- }
- return { repo: this.repo, phaseRepo: this.phaseRepo, siteRepo: this.siteRepo };
- }
- async create(block: Block): Promise<Block> {
- const { repo, phaseRepo, siteRepo } = await this.getRepos();
- // 1. Validate Phase exists
- const phase = await phaseRepo.findById(block.phaseId);
- if (!phase) {
- throw new BadRequestException(`Phase with ID ${block.phaseId} does not exist.`);
- }
- // 2. Validate Site exists
- const site = await siteRepo.findById(phase.siteId);
- if (!site) {
- throw new BadRequestException(`Site with ID ${phase.siteId} (from Phase ${block.phaseId}) does not exist.`);
- }
- return repo.create(block);
- }
- async findAll(filter: Record<string, any> = {}): Promise<Block[]> {
- const { repo } = await this.getRepos();
- return repo.findAll(filter);
- }
- async findById(id: string): Promise<Block | null> {
- const { repo } = await this.getRepos();
- return repo.findById(id);
- }
- async update(id: string, update: Partial<Block>): Promise<void> {
- const { repo, phaseRepo, siteRepo } = await this.getRepos();
- const block = await repo.findById(id);
- if (!block) throw new NotFoundException('Block not found');
- if (update.phaseId) {
- // 1. Validate Phase exists
- const phase = await phaseRepo.findById(update.phaseId);
- if (!phase) throw new BadRequestException(`Phase with ID ${update.phaseId} does not exist.`);
- // 2. Validate Site exists
- const site = await siteRepo.findById(phase.siteId);
- if (!site) throw new BadRequestException(`Site with ID ${phase.siteId} (from Phase ${update.phaseId}) does not exist.`);
- }
- await repo.update(id, update);
- }
- async delete(id: string): Promise<void> {
- const { repo } = await this.getRepos();
- const block = await repo.findById(id);
- if (!block) throw new NotFoundException('Block not found');
- // Cascading Delete:
- // 1. Delete all FFB Production records referencing this block
- await this.ffbService.deleteMany({ 'block.id': id });
- // 2. Delete the block itself
- await repo.delete(id);
- }
- async generateDescription(data: { name: string, phaseName?: string, size?: number, numOfTrees?: number }): Promise<{ description: string }> {
- const prompt = `Write a realistic, professional description (2–4 sentences) for an oil palm plantation block named "${data.name}"${data.phaseName ? ` within phase "${data.phaseName}"` : ''}.${data.size ? ` It has a size of ${data.size} units` : ''}${data.numOfTrees ? ` and contains ${data.numOfTrees} trees` : ''}.
- Choose 1-2 aspects from the following to incorporate naturally:
- - Block health and maturity (e.g., trees at peak production age, healthy canopy development, consistent fruit set)
- - Field accessibility (e.g., easily accessible by tractor paths, slightly sloped area requiring specialized harvesting tools)
- - Environmental features (e.g., bordering primary forest, presence of natural irrigation channels, rich nutrient-holding soil)
- - Monitoring and care (e.g., subject to intensive pest monitoring, recently fertilized, under strict irrigation schedule)
- Guidelines:
- - Maintain a grounded, operational tone.
- - Do not mention the bullet points explicitly; weave them into the narrative.
- - Ensure the length is exactly 2-4 sentences.
- Description:`;
- const description = await this.ffbLangChainService.chatStateless(prompt);
- return { description: description.replace(/^Description:\s*/i, '').trim() };
- }
- }
|