block.service.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { Injectable, NotFoundException, BadRequestException, Inject, forwardRef } from '@nestjs/common';
  2. import { FFBLangChainService } from '../../FFB/services/ffb-langchain.service';
  3. import { MongoCoreService } from '../../mongo/mongo-core.service';
  4. import { BlockRepository } from '../repositories/block.repository';
  5. import { Block } from '../schemas/site.schema';
  6. import { PhaseRepository } from '../repositories/phase.repository';
  7. import { SiteRepository } from '../repositories/site.repository';
  8. import { FFBProductionService } from '../../FFB/services/ffb-production.service';
  9. @Injectable()
  10. export class BlockService {
  11. private repo: BlockRepository;
  12. private phaseRepo: PhaseRepository;
  13. private siteRepo: SiteRepository;
  14. constructor(
  15. private readonly mongoCore: MongoCoreService,
  16. @Inject(forwardRef(() => FFBProductionService))
  17. private readonly ffbService: FFBProductionService,
  18. private readonly ffbLangChainService: FFBLangChainService,
  19. ) { }
  20. private async getRepos() {
  21. if (!this.repo) {
  22. const db = await this.mongoCore.getDb();
  23. this.repo = new BlockRepository(db);
  24. this.phaseRepo = new PhaseRepository(db);
  25. this.siteRepo = new SiteRepository(db);
  26. await this.repo.init();
  27. await this.phaseRepo.init();
  28. await this.siteRepo.init();
  29. }
  30. return { repo: this.repo, phaseRepo: this.phaseRepo, siteRepo: this.siteRepo };
  31. }
  32. async create(block: Block): Promise<Block> {
  33. const { repo, phaseRepo, siteRepo } = await this.getRepos();
  34. // 1. Validate Phase exists
  35. const phase = await phaseRepo.findById(block.phaseId);
  36. if (!phase) {
  37. throw new BadRequestException(`Phase with ID ${block.phaseId} does not exist.`);
  38. }
  39. // 2. Validate Site exists
  40. const site = await siteRepo.findById(phase.siteId);
  41. if (!site) {
  42. throw new BadRequestException(`Site with ID ${phase.siteId} (from Phase ${block.phaseId}) does not exist.`);
  43. }
  44. return repo.create(block);
  45. }
  46. async findAll(filter: Record<string, any> = {}): Promise<Block[]> {
  47. const { repo } = await this.getRepos();
  48. return repo.findAll(filter);
  49. }
  50. async findById(id: string): Promise<Block | null> {
  51. const { repo } = await this.getRepos();
  52. return repo.findById(id);
  53. }
  54. async update(id: string, update: Partial<Block>): Promise<void> {
  55. const { repo, phaseRepo, siteRepo } = await this.getRepos();
  56. const block = await repo.findById(id);
  57. if (!block) throw new NotFoundException('Block not found');
  58. if (update.phaseId) {
  59. // 1. Validate Phase exists
  60. const phase = await phaseRepo.findById(update.phaseId);
  61. if (!phase) throw new BadRequestException(`Phase with ID ${update.phaseId} does not exist.`);
  62. // 2. Validate Site exists
  63. const site = await siteRepo.findById(phase.siteId);
  64. if (!site) throw new BadRequestException(`Site with ID ${phase.siteId} (from Phase ${update.phaseId}) does not exist.`);
  65. }
  66. await repo.update(id, update);
  67. }
  68. async delete(id: string): Promise<void> {
  69. const { repo } = await this.getRepos();
  70. const block = await repo.findById(id);
  71. if (!block) throw new NotFoundException('Block not found');
  72. // Cascading Delete:
  73. // 1. Delete all FFB Production records referencing this block
  74. await this.ffbService.deleteMany({ 'block.id': id });
  75. // 2. Delete the block itself
  76. await repo.delete(id);
  77. }
  78. async generateDescription(data: { name: string, phaseName?: string, size?: number, numOfTrees?: number }): Promise<{ description: string }> {
  79. 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` : ''}.
  80. Choose 1-2 aspects from the following to incorporate naturally:
  81. - Block health and maturity (e.g., trees at peak production age, healthy canopy development, consistent fruit set)
  82. - Field accessibility (e.g., easily accessible by tractor paths, slightly sloped area requiring specialized harvesting tools)
  83. - Environmental features (e.g., bordering primary forest, presence of natural irrigation channels, rich nutrient-holding soil)
  84. - Monitoring and care (e.g., subject to intensive pest monitoring, recently fertilized, under strict irrigation schedule)
  85. Guidelines:
  86. - Maintain a grounded, operational tone.
  87. - Do not mention the bullet points explicitly; weave them into the narrative.
  88. - Ensure the length is exactly 2-4 sentences.
  89. Description:`;
  90. const description = await this.ffbLangChainService.chatStateless(prompt);
  91. return { description: description.replace(/^Description:\s*/i, '').trim() };
  92. }
  93. }