UPDATE
This commit is contained in:
@@ -0,0 +1,487 @@
|
|||||||
|
# Fertilization Recommendation Result API Spec
|
||||||
|
|
||||||
|
این فایل مشخص میکند که فرانتاند برای صفحه `SmartFertilizationRecommendation` دقیقاً چه خروجیای از بکاند نیاز دارد، مخصوصاً برای:
|
||||||
|
|
||||||
|
- Hero Card
|
||||||
|
- ماشینحساب مساحت مزرعه
|
||||||
|
- آنالیز ترکیبات
|
||||||
|
- مراحل مصرف
|
||||||
|
- نکات ایمنی
|
||||||
|
- کودهای جایگزین
|
||||||
|
- Bottom Sheet جزئیات
|
||||||
|
|
||||||
|
نکته مهم: برای ماشینحساب، فرانتاند **نباید** مقدار را از رشتههایی مثل `150 kg/ha` parse کند. بکاند باید مقادیر عددی استاندارد و مستقل برگرداند.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## هدف اصلی
|
||||||
|
|
||||||
|
برای اینکه ماشینحساب مقدار مصرف دقیق کار کند، بکاند باید علاوه بر متن نمایشی، **مقدار عددی پایه** را نیز برگرداند.
|
||||||
|
|
||||||
|
فرمول مورد نیاز فرانت:
|
||||||
|
|
||||||
|
```text
|
||||||
|
مقدار کل = مقدار مصرف در هر متر مربع × مساحت مزرعه
|
||||||
|
```
|
||||||
|
|
||||||
|
یا اگر واحد پایه بر حسب هکتار ارسال شود:
|
||||||
|
|
||||||
|
```text
|
||||||
|
مقدار کل = مقدار مصرف در هکتار × مساحت (هکتار)
|
||||||
|
```
|
||||||
|
|
||||||
|
اما پیشنهاد قطعی برای فرانت این است که بکاند هر دو را بدهد:
|
||||||
|
|
||||||
|
- `base_amount_per_hectare`
|
||||||
|
- `base_amount_per_square_meter`
|
||||||
|
|
||||||
|
تا هیچ تبدیل واحدی در UI لازم نباشد.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Endpoint پیشنهادی
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/fertilization/recommend/
|
||||||
|
```
|
||||||
|
|
||||||
|
یا اگر ساختار فعلی پروژه حفظ شود:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/fertilization-recommendation/recommend/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Request پیشنهادی
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"crop_id": "wheat",
|
||||||
|
"growth_stage": "flowering",
|
||||||
|
"area": {
|
||||||
|
"value": 2.5,
|
||||||
|
"unit": "hectare"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### توضیح فیلدهای Request
|
||||||
|
|
||||||
|
| فیلد | نوع | اجباری | توضیح |
|
||||||
|
|------|-----|--------|-------|
|
||||||
|
| `farm_uuid` | string | بله | شناسه مزرعه |
|
||||||
|
| `crop_id` | string | بله | شناسه محصول |
|
||||||
|
| `growth_stage` | string | بله | مرحله رشد محصول |
|
||||||
|
| `area.value` | number | اختیاری | مساحت مزرعه برای محاسبه مستقیم مقدار کل |
|
||||||
|
| `area.unit` | string | اختیاری | واحد مساحت؛ ترجیحاً `hectare` یا `square_meter` |
|
||||||
|
|
||||||
|
اگر `area` ارسال نشود، فرانت با دادههای پایه محاسبه را خودش انجام میدهد.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Response پیشنهادی
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"recommendation_id": "fert-rec-001",
|
||||||
|
"crop": {
|
||||||
|
"id": "wheat",
|
||||||
|
"name": "گندم"
|
||||||
|
},
|
||||||
|
"growth_stage": {
|
||||||
|
"id": "flowering",
|
||||||
|
"name": "گلدهی"
|
||||||
|
},
|
||||||
|
"primary_recommendation": {
|
||||||
|
"fertilizer_code": "npk-20-20-20",
|
||||||
|
"fertilizer_name": "کود کامل 20-20-20",
|
||||||
|
"display_title": "کود کامل 20-20-20",
|
||||||
|
"fertilizer_type": "NPK",
|
||||||
|
"npk_ratio": {
|
||||||
|
"n": 20,
|
||||||
|
"p": 20,
|
||||||
|
"k": 20,
|
||||||
|
"label": "20-20-20"
|
||||||
|
},
|
||||||
|
"application_method": {
|
||||||
|
"id": "foliar_fertigation",
|
||||||
|
"label": "محلول پاشی / آب آبیاری"
|
||||||
|
},
|
||||||
|
"application_interval": {
|
||||||
|
"value": 14,
|
||||||
|
"unit": "day",
|
||||||
|
"label": "هر 14 روز"
|
||||||
|
},
|
||||||
|
"dosage": {
|
||||||
|
"base_amount_per_hectare": 150,
|
||||||
|
"base_amount_per_square_meter": 0.015,
|
||||||
|
"unit": "kg",
|
||||||
|
"label": "150 کیلوگرم در هکتار",
|
||||||
|
"calculation_basis": "product"
|
||||||
|
},
|
||||||
|
"total_amount": {
|
||||||
|
"value": 375,
|
||||||
|
"unit": "kg",
|
||||||
|
"label": "375 کیلوگرم"
|
||||||
|
},
|
||||||
|
"reasoning": "این ترکیب برای مرحله گلدهی به دلیل نیاز متعادل به ازت، فسفر و پتاسیم پیشنهاد شده است.",
|
||||||
|
"summary": "مناسب برای حفظ رشد رویشی و پشتیبانی از گلدهی"
|
||||||
|
},
|
||||||
|
"nutrient_analysis": {
|
||||||
|
"macro": [
|
||||||
|
{
|
||||||
|
"key": "n",
|
||||||
|
"name": "نیتروژن (N)",
|
||||||
|
"value": 20,
|
||||||
|
"unit": "percent",
|
||||||
|
"description": "نیتروژن برای رشد رویشی و افزایش سطح برگ ضروری است."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "p",
|
||||||
|
"name": "فسفر (P)",
|
||||||
|
"value": 20,
|
||||||
|
"unit": "percent",
|
||||||
|
"description": "فسفر برای توسعه ریشه و انتقال انرژی اهمیت دارد."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "k",
|
||||||
|
"name": "پتاسیم (K)",
|
||||||
|
"value": 20,
|
||||||
|
"unit": "percent",
|
||||||
|
"description": "پتاسیم به کیفیت محصول و مقاومت به تنش کمک میکند."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"micro": [
|
||||||
|
{
|
||||||
|
"key": "fe",
|
||||||
|
"name": "آهن",
|
||||||
|
"value": 0.5,
|
||||||
|
"unit": "percent",
|
||||||
|
"description": "آهن در تولید کلروفیل و جلوگیری از زردی موثر است."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "zn",
|
||||||
|
"name": "روی",
|
||||||
|
"value": 1,
|
||||||
|
"unit": "percent",
|
||||||
|
"description": "روی در رشد متعادل و فعالیت آنزیمها نقش دارد."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"application_guide": {
|
||||||
|
"safety_warning": "هنگام محلول پاشی از دستکش و ماسک استفاده کنید و در ساعات خنک روز مصرف انجام شود.",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step_number": 1,
|
||||||
|
"title": "آماده سازی",
|
||||||
|
"description": "مقدار توصیه شده از کود را در یک سطل آب تمیز حل کنید."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_number": 2,
|
||||||
|
"title": "ترکیب",
|
||||||
|
"description": "محلول را به مخزن اصلی سم پاش یا سیستم آبیاری اضافه کنید و خوب هم بزنید."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_number": 3,
|
||||||
|
"title": "مصرف",
|
||||||
|
"description": "به صورت یکنواخت روی گیاه اسپری کنید یا در سیستم آبیاری تزریق نمایید."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alternative_recommendations": [
|
||||||
|
{
|
||||||
|
"fertilizer_code": "npk-10-52-10",
|
||||||
|
"fertilizer_name": "کود 10-52-10",
|
||||||
|
"fertilizer_type": "NPK (فسفر بالا)",
|
||||||
|
"usage_method": "محلول پاشی",
|
||||||
|
"description": "برای تقویت ریشه و پشتیبانی از گلدهی در صورت نبود پیشنهاد اصلی مناسب است."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fertilizer_code": "npk-12-12-36",
|
||||||
|
"fertilizer_name": "کود 12-12-36",
|
||||||
|
"fertilizer_type": "NPK (پتاس بالا)",
|
||||||
|
"usage_method": "تزریق در آبیاری",
|
||||||
|
"description": "برای بهبود کیفیت محصول و افزایش پتاسیم قابل استفاده است."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فیلدهای ضروری برای فرانت
|
||||||
|
|
||||||
|
### 1) اطلاعات اصلی پیشنهاد
|
||||||
|
|
||||||
|
این فیلدها برای Hero Card لازماند.
|
||||||
|
|
||||||
|
| فیلد | نوع | اجباری | توضیح |
|
||||||
|
|------|-----|--------|-------|
|
||||||
|
| `primary_recommendation.fertilizer_name` | string | بله | نام اصلی کود |
|
||||||
|
| `primary_recommendation.display_title` | string | بهتر است | عنوان نمایشی اگر با نام اصلی فرق دارد |
|
||||||
|
| `primary_recommendation.fertilizer_type` | string | بله | نوع کود مثل NPK |
|
||||||
|
| `primary_recommendation.npk_ratio.label` | string | بله | متن آماده برای نمایش مثل `20-20-20` |
|
||||||
|
| `primary_recommendation.npk_ratio.n` | number | بله | درصد نیتروژن |
|
||||||
|
| `primary_recommendation.npk_ratio.p` | number | بله | درصد فسفر |
|
||||||
|
| `primary_recommendation.npk_ratio.k` | number | بله | درصد پتاسیم |
|
||||||
|
| `primary_recommendation.application_method.label` | string | بله | روش مصرف نمایشی |
|
||||||
|
| `primary_recommendation.application_interval.label` | string | بله | فاصله مصرف نمایشی |
|
||||||
|
| `primary_recommendation.reasoning` | string | بله | دلیل توصیه برای بخش توضیحات |
|
||||||
|
|
||||||
|
### 2) فیلدهای ضروری برای ماشینحساب
|
||||||
|
|
||||||
|
این بخش مهمترین قسمت برای بکاند است.
|
||||||
|
|
||||||
|
| فیلد | نوع | اجباری | توضیح |
|
||||||
|
|------|-----|--------|-------|
|
||||||
|
| `primary_recommendation.dosage.base_amount_per_hectare` | number | بله | مقدار پایه مصرف در هر هکتار |
|
||||||
|
| `primary_recommendation.dosage.base_amount_per_square_meter` | number | بله | مقدار پایه مصرف در هر متر مربع |
|
||||||
|
| `primary_recommendation.dosage.unit` | string | بله | واحد مقدار مصرف؛ مثل `kg` یا `liter` |
|
||||||
|
| `primary_recommendation.dosage.label` | string | بله | متن آماده برای نمایش مثل `150 کیلوگرم در هکتار` |
|
||||||
|
| `primary_recommendation.total_amount.value` | number | اختیاری | اگر بکاند بر اساس مساحت ورودی مقدار کل را حساب کند |
|
||||||
|
| `primary_recommendation.total_amount.unit` | string | اختیاری | واحد مقدار کل |
|
||||||
|
| `primary_recommendation.total_amount.label` | string | اختیاری | متن نمایشی مقدار کل |
|
||||||
|
|
||||||
|
### چرا `base_amount_per_square_meter` لازم است؟
|
||||||
|
|
||||||
|
چون شما گفتید ماشینحساب باید مقدار **کیلوگرم در هر متر مربع** را از بکاند بگیرد.
|
||||||
|
|
||||||
|
پس بکاند باید این مقدار را صریح برگرداند، نه اینکه فرانت از `kg/ha` خودش تبدیل کند.
|
||||||
|
|
||||||
|
نمونه:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"base_amount_per_hectare": 150,
|
||||||
|
"base_amount_per_square_meter": 0.015,
|
||||||
|
"unit": "kg"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
تبدیل مرجع:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1 hectare = 10,000 square meters
|
||||||
|
150 kg/ha = 0.015 kg/m²
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فیلدهای آنالیز ترکیبات
|
||||||
|
|
||||||
|
برای اینکه فرانت مجبور به parse کردن متن `npkRatio` نباشد، بکاند باید آنالیز را ساختارمند بفرستد.
|
||||||
|
|
||||||
|
### ماکرو
|
||||||
|
|
||||||
|
| فیلد | نوع | اجباری | توضیح |
|
||||||
|
|------|-----|--------|-------|
|
||||||
|
| `nutrient_analysis.macro[].key` | string | بله | کلید استاندارد مثل `n`, `p`, `k` |
|
||||||
|
| `nutrient_analysis.macro[].name` | string | بله | نام نمایشی فارسی |
|
||||||
|
| `nutrient_analysis.macro[].value` | number | بله | درصد عنصر |
|
||||||
|
| `nutrient_analysis.macro[].unit` | string | بله | معمولاً `percent` |
|
||||||
|
| `nutrient_analysis.macro[].description` | string | بهتر است | توضیح برای Bottom Sheet |
|
||||||
|
|
||||||
|
### ریزمغذیها
|
||||||
|
|
||||||
|
| فیلد | نوع | اجباری | توضیح |
|
||||||
|
|------|-----|--------|-------|
|
||||||
|
| `nutrient_analysis.micro[].key` | string | بله | مثل `fe`, `zn`, `mn`, `b` |
|
||||||
|
| `nutrient_analysis.micro[].name` | string | بله | نام فارسی عنصر |
|
||||||
|
| `nutrient_analysis.micro[].value` | number | بله | درصد عنصر |
|
||||||
|
| `nutrient_analysis.micro[].unit` | string | بله | معمولاً `percent` |
|
||||||
|
| `nutrient_analysis.micro[].description` | string | بهتر است | توضیح برای Bottom Sheet |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فیلدهای دستورالعمل مصرف
|
||||||
|
|
||||||
|
| فیلد | نوع | اجباری | توضیح |
|
||||||
|
|------|-----|--------|-------|
|
||||||
|
| `application_guide.safety_warning` | string | بله | متن هشدار ایمنی |
|
||||||
|
| `application_guide.steps[].step_number` | number | بله | شماره مرحله |
|
||||||
|
| `application_guide.steps[].title` | string | بله | عنوان مرحله |
|
||||||
|
| `application_guide.steps[].description` | string | بله | توضیح مرحله |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فیلدهای کودهای جایگزین
|
||||||
|
|
||||||
|
| فیلد | نوع | اجباری | توضیح |
|
||||||
|
|------|-----|--------|-------|
|
||||||
|
| `alternative_recommendations[].fertilizer_code` | string | بله | شناسه یکتا |
|
||||||
|
| `alternative_recommendations[].fertilizer_name` | string | بله | نام کود جایگزین |
|
||||||
|
| `alternative_recommendations[].fertilizer_type` | string | بله | نوع کود |
|
||||||
|
| `alternative_recommendations[].usage_method` | string | بله | روش مصرف |
|
||||||
|
| `alternative_recommendations[].description` | string | بله | توضیح برای Bottom Sheet |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فیلدهای لازم برای Bottom Sheet
|
||||||
|
|
||||||
|
برای باز شدن Bottom Sheet روی مواد غذایی و کودهای جایگزین، بهتر است توضیح آماده از بکاند بیاید.
|
||||||
|
|
||||||
|
| بخش | فیلد پیشنهادی |
|
||||||
|
|-----|---------------|
|
||||||
|
| مواد اصلی | `nutrient_analysis.macro[].description` |
|
||||||
|
| ریزمغذیها | `nutrient_analysis.micro[].description` |
|
||||||
|
| کود جایگزین | `alternative_recommendations[].description` |
|
||||||
|
|
||||||
|
این باعث میشود فرانت مجبور نباشد متنهای توضیحی hard-code کند.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فرمت واحدها
|
||||||
|
|
||||||
|
پیشنهاد میشود بکاند از مقادیر استاندارد زیر استفاده کند:
|
||||||
|
|
||||||
|
### واحد مقدار کود
|
||||||
|
|
||||||
|
- `kg`
|
||||||
|
- `gram`
|
||||||
|
- `liter`
|
||||||
|
- `milliliter`
|
||||||
|
|
||||||
|
### واحد مساحت
|
||||||
|
|
||||||
|
- `hectare`
|
||||||
|
- `square_meter`
|
||||||
|
|
||||||
|
### واحد درصد عناصر
|
||||||
|
|
||||||
|
- `percent`
|
||||||
|
|
||||||
|
### واحد فاصله مصرف
|
||||||
|
|
||||||
|
- `day`
|
||||||
|
- `week`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## قوانین پیشنهادی برای بکاند
|
||||||
|
|
||||||
|
### 1) متن نمایشی و مقدار عددی را با هم برگردانید
|
||||||
|
|
||||||
|
اشتباه:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"amountPerHectare": "150 kg/ha"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
صحیح:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dosage": {
|
||||||
|
"base_amount_per_hectare": 150,
|
||||||
|
"base_amount_per_square_meter": 0.015,
|
||||||
|
"unit": "kg",
|
||||||
|
"label": "150 کیلوگرم در هکتار"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2) هیچ داده مهمی فقط داخل `reasoning` دفن نشود
|
||||||
|
|
||||||
|
مواردی مثل:
|
||||||
|
|
||||||
|
- درصد N, P, K
|
||||||
|
- درصد ریزمغذیها
|
||||||
|
- مقدار مصرف پایه
|
||||||
|
- روش مصرف
|
||||||
|
|
||||||
|
باید فیلد مستقل داشته باشند، نه فقط متن آزاد.
|
||||||
|
|
||||||
|
### 3) نام مرحله رشد و نام محصول را هم برگردانید
|
||||||
|
|
||||||
|
تا Header صفحه بدون lookup اضافه ساخته شود.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## حداقل خروجی لازم برای نسخه فعلی فرانت
|
||||||
|
|
||||||
|
اگر بکاند بخواهد فقط حداقل دادهی لازم را برگرداند، این ساختار minimum پیشنهاد میشود:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"crop": {
|
||||||
|
"id": "wheat",
|
||||||
|
"name": "گندم"
|
||||||
|
},
|
||||||
|
"growth_stage": {
|
||||||
|
"id": "flowering",
|
||||||
|
"name": "گلدهی"
|
||||||
|
},
|
||||||
|
"primary_recommendation": {
|
||||||
|
"fertilizer_name": "کود کامل 20-20-20",
|
||||||
|
"fertilizer_type": "NPK",
|
||||||
|
"npk_ratio": {
|
||||||
|
"n": 20,
|
||||||
|
"p": 20,
|
||||||
|
"k": 20,
|
||||||
|
"label": "20-20-20"
|
||||||
|
},
|
||||||
|
"application_method": {
|
||||||
|
"label": "محلول پاشی / آب آبیاری"
|
||||||
|
},
|
||||||
|
"application_interval": {
|
||||||
|
"label": "هر 14 روز"
|
||||||
|
},
|
||||||
|
"dosage": {
|
||||||
|
"base_amount_per_hectare": 150,
|
||||||
|
"base_amount_per_square_meter": 0.015,
|
||||||
|
"unit": "kg",
|
||||||
|
"label": "150 کیلوگرم در هکتار"
|
||||||
|
},
|
||||||
|
"reasoning": "توضیحات توصیه"
|
||||||
|
},
|
||||||
|
"nutrient_analysis": {
|
||||||
|
"macro": [
|
||||||
|
{ "key": "n", "name": "نیتروژن (N)", "value": 20, "unit": "percent" },
|
||||||
|
{ "key": "p", "name": "فسفر (P)", "value": 20, "unit": "percent" },
|
||||||
|
{ "key": "k", "name": "پتاسیم (K)", "value": 20, "unit": "percent" }
|
||||||
|
],
|
||||||
|
"micro": []
|
||||||
|
},
|
||||||
|
"application_guide": {
|
||||||
|
"safety_warning": "هشدار ایمنی",
|
||||||
|
"steps": [
|
||||||
|
{ "step_number": 1, "title": "آماده سازی", "description": "..." },
|
||||||
|
{ "step_number": 2, "title": "ترکیب", "description": "..." },
|
||||||
|
{ "step_number": 3, "title": "مصرف", "description": "..." }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alternative_recommendations": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## نتیجه نهایی
|
||||||
|
|
||||||
|
برای اینکه UI فعلی بدون parse کردن رشتهها و بدون hard-code اضافی درست کار کند، بکاند باید حداقل این موارد را صریح برگرداند:
|
||||||
|
|
||||||
|
1. نام کود
|
||||||
|
2. نوع کود
|
||||||
|
3. نسبت NPK به صورت عددی و متنی
|
||||||
|
4. روش مصرف
|
||||||
|
5. فاصله مصرف
|
||||||
|
6. مقدار پایه در هکتار
|
||||||
|
7. مقدار پایه در متر مربع
|
||||||
|
8. واحد مقدار مصرف
|
||||||
|
9. استدلال توصیه
|
||||||
|
10. آنالیز ماکرو و ریزمغذیها به صورت ساختارمند
|
||||||
|
11. هشدار ایمنی
|
||||||
|
12. مراحل مصرف
|
||||||
|
13. کودهای جایگزین با توضیح
|
||||||
|
|
||||||
|
اگر خواستی، قدم بعدی میتوانم همین فایل را به یک قرارداد نهایی هماهنگ با TypeScript interface های `src/libs/api/services/fertilizationRecommendationService.ts` هم تبدیل کنم.
|
||||||
@@ -4,11 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { apiClient } from "../client";
|
import { apiClient } from "../client";
|
||||||
import type {
|
|
||||||
RecommendationTaskInitResponse,
|
|
||||||
RecommendationTaskStatusResponse,
|
|
||||||
} from "./recommendationTask";
|
|
||||||
import { normalizeRecommendationTaskStatus } from "./recommendationTask";
|
|
||||||
|
|
||||||
const PREFIX = "/api/fertilization-recommendation";
|
const PREFIX = "/api/fertilization-recommendation";
|
||||||
const RECOMMEND_PREFIX = "/api/fertilization";
|
const RECOMMEND_PREFIX = "/api/fertilization";
|
||||||
@@ -40,12 +35,95 @@ export interface FertilizationConfigResponse {
|
|||||||
cropOptions: CropOption[];
|
cropOptions: CropOption[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FertilizationPlan {
|
export interface FertilizationNpkRatio {
|
||||||
npkRatio: string;
|
n: number;
|
||||||
amountPerHectare: string;
|
p: number;
|
||||||
applicationMethod: string;
|
k: number;
|
||||||
applicationInterval: string;
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationApplicationMethod {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationApplicationInterval {
|
||||||
|
label: string;
|
||||||
|
unit: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationDosage {
|
||||||
|
label: string;
|
||||||
|
unit: string;
|
||||||
|
calculation_basis: string;
|
||||||
|
base_amount_per_hectare: number;
|
||||||
|
base_amount_per_square_meter: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationPrimaryRecommendation {
|
||||||
|
fertilizer_code: string;
|
||||||
|
fertilizer_name: string;
|
||||||
|
display_title: string;
|
||||||
|
fertilizer_type: string;
|
||||||
reasoning: string;
|
reasoning: string;
|
||||||
|
summary: string;
|
||||||
|
npk_ratio: FertilizationNpkRatio;
|
||||||
|
application_method: FertilizationApplicationMethod;
|
||||||
|
application_interval: FertilizationApplicationInterval;
|
||||||
|
dosage: FertilizationDosage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationNutrientItem {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
unit: string;
|
||||||
|
description: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationApplicationStep {
|
||||||
|
step_number: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationApplicationGuide {
|
||||||
|
safety_warning: string;
|
||||||
|
steps: FertilizationApplicationStep[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationAlternativeRecommendation {
|
||||||
|
fertilizer_code: string;
|
||||||
|
fertilizer_name: string;
|
||||||
|
fertilizer_type: string;
|
||||||
|
usage_method: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationSection {
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
type: string;
|
||||||
|
content?: string;
|
||||||
|
items?: string[];
|
||||||
|
applicationMethod?: string;
|
||||||
|
fertilizerType?: string;
|
||||||
|
validityPeriod?: string;
|
||||||
|
amount?: string;
|
||||||
|
expandableExplanation?: string;
|
||||||
|
timing?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FertilizationRecommendationResult {
|
||||||
|
primary_recommendation: FertilizationPrimaryRecommendation;
|
||||||
|
nutrient_analysis: {
|
||||||
|
macro: FertilizationNutrientItem[];
|
||||||
|
micro: FertilizationNutrientItem[];
|
||||||
|
};
|
||||||
|
application_guide: FertilizationApplicationGuide;
|
||||||
|
alternative_recommendations: FertilizationAlternativeRecommendation[];
|
||||||
|
sections: FertilizationSection[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FertilizationRecommendPayload {
|
export interface FertilizationRecommendPayload {
|
||||||
@@ -58,17 +136,9 @@ export interface FertilizationRecommendPayload {
|
|||||||
waterEC?: string;
|
waterEC?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FertilizationRecommendationResult {
|
|
||||||
plan: FertilizationPlan;
|
|
||||||
status?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FertilizationRecommendResponse =
|
|
||||||
| FertilizationRecommendationResult
|
|
||||||
| RecommendationTaskInitResponse;
|
|
||||||
|
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
status: string;
|
code: number;
|
||||||
|
msg: string;
|
||||||
data: T;
|
data: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,26 +147,6 @@ async function unwrap<T>(promise: Promise<ApiResponse<T>>): Promise<T> {
|
|||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeTaskInitResponse(
|
|
||||||
task: RecommendationTaskInitResponse,
|
|
||||||
): RecommendationTaskInitResponse {
|
|
||||||
return {
|
|
||||||
...task,
|
|
||||||
status: normalizeRecommendationTaskStatus(task.status),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeRecommendationResult(
|
|
||||||
result: FertilizationRecommendationResult,
|
|
||||||
): FertilizationRecommendationResult {
|
|
||||||
return result.status
|
|
||||||
? {
|
|
||||||
...result,
|
|
||||||
status: normalizeRecommendationTaskStatus(result.status),
|
|
||||||
}
|
|
||||||
: result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fertilizationRecommendationService = {
|
export const fertilizationRecommendationService = {
|
||||||
getConfig(farmUuid: string): Promise<FertilizationConfigResponse> {
|
getConfig(farmUuid: string): Promise<FertilizationConfigResponse> {
|
||||||
return unwrap(
|
return unwrap(
|
||||||
@@ -108,39 +158,12 @@ export const fertilizationRecommendationService = {
|
|||||||
|
|
||||||
recommend(
|
recommend(
|
||||||
payload?: FertilizationRecommendPayload,
|
payload?: FertilizationRecommendPayload,
|
||||||
): Promise<FertilizationRecommendResponse> {
|
): Promise<FertilizationRecommendationResult> {
|
||||||
return unwrap(
|
return unwrap(
|
||||||
apiClient.post<ApiResponse<FertilizationRecommendResponse>>(
|
apiClient.post<ApiResponse<FertilizationRecommendationResult>>(
|
||||||
`${RECOMMEND_PREFIX}/recommend/`,
|
`${RECOMMEND_PREFIX}/recommend/`,
|
||||||
payload ?? {},
|
payload ?? {},
|
||||||
),
|
),
|
||||||
).then((response) =>
|
|
||||||
"task_id" in response
|
|
||||||
? normalizeTaskInitResponse(response)
|
|
||||||
: normalizeRecommendationResult(response),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getRecommendStatus(
|
|
||||||
taskId: string,
|
|
||||||
farmUuid: string,
|
|
||||||
): Promise<
|
|
||||||
RecommendationTaskStatusResponse<FertilizationRecommendationResult>
|
|
||||||
> {
|
|
||||||
return unwrap(
|
|
||||||
apiClient.get<
|
|
||||||
ApiResponse<
|
|
||||||
RecommendationTaskStatusResponse<FertilizationRecommendationResult>
|
|
||||||
>
|
|
||||||
>(
|
|
||||||
`${PREFIX}/recommend/status/${taskId}/?farm_uuid=${encodeURIComponent(farmUuid)}`,
|
|
||||||
),
|
|
||||||
).then((response) => ({
|
|
||||||
...response,
|
|
||||||
status: normalizeRecommendationTaskStatus(response.status),
|
|
||||||
result: response.result
|
|
||||||
? normalizeRecommendationResult(response.result)
|
|
||||||
: undefined,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
+653
-194
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user