253 lines
9.2 KiB
C#
253 lines
9.2 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using eSPJ.Models;
|
|
using eSPJ.Services;
|
|
|
|
namespace eSPJ.Controllers.SpjDriverUpstController
|
|
{
|
|
[Route("upst/detail-penjemputan")]
|
|
public class DetailPenjemputanController : Controller
|
|
{
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly DetailPenjemputanService _detailService;
|
|
private readonly ILogger<DetailPenjemputanController> _logger;
|
|
|
|
public DetailPenjemputanController(
|
|
IHttpClientFactory httpClientFactory,
|
|
IConfiguration configuration,
|
|
DetailPenjemputanService detailService,
|
|
ILogger<DetailPenjemputanController> logger)
|
|
{
|
|
_httpClientFactory = httpClientFactory;
|
|
_configuration = configuration;
|
|
_detailService = detailService;
|
|
_logger = logger;
|
|
}
|
|
|
|
[HttpGet("")]
|
|
public IActionResult Index()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Index.cshtml");
|
|
}
|
|
[HttpGet("tanpa-tps")]
|
|
public IActionResult TanpaTps()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/TanpaTps.cshtml");
|
|
}
|
|
|
|
[HttpGet("batal")]
|
|
public IActionResult Batal()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Batal.cshtml");
|
|
}
|
|
|
|
[HttpGet("detail-batal")]
|
|
public IActionResult DetailBatal()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/DetailBatal.cshtml");
|
|
}
|
|
|
|
[HttpPost("")]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Submit([FromForm] DetailPenjemputanRequest request)
|
|
{
|
|
try
|
|
{
|
|
var result = await _detailService.SubmitPenjemputanAsync(request);
|
|
|
|
if (result.Success)
|
|
{
|
|
TempData["Success"] = result.Message;
|
|
}
|
|
else
|
|
{
|
|
TempData["Error"] = result.Message;
|
|
}
|
|
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error submitting penjemputan data");
|
|
TempData["Error"] = "Terjadi kesalahan saat menyimpan data.";
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
}
|
|
|
|
[HttpPost("ocr-timbangan")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> OcrTimbangan(IFormFile? Foto)
|
|
{
|
|
if (Foto == null || Foto.Length == 0)
|
|
{
|
|
return BadRequest(new { success = false, message = "Foto tidak ditemukan." });
|
|
}
|
|
|
|
if (Foto.Length > 5 * 1024 * 1024)
|
|
{
|
|
return BadRequest(new { success = false, message = "Ukuran foto terlalu besar. Maksimal 5MB." });
|
|
}
|
|
|
|
var apiKey = _configuration["OpenRouter:OCRkey"];
|
|
if (string.IsNullOrWhiteSpace(apiKey))
|
|
{
|
|
return StatusCode(500, new { success = false, message = "OpenRouter API key belum diset." });
|
|
}
|
|
|
|
byte[] fileBytes;
|
|
await using (var ms = new MemoryStream())
|
|
{
|
|
await Foto.CopyToAsync(ms);
|
|
fileBytes = ms.ToArray();
|
|
}
|
|
|
|
var mimeType = string.IsNullOrWhiteSpace(Foto.ContentType) ? "image/jpeg" : Foto.ContentType;
|
|
var base64 = Convert.ToBase64String(fileBytes);
|
|
var dataUrl = $"data:{mimeType};base64,{base64}";
|
|
|
|
var payload = new
|
|
{
|
|
// model = "nvidia/nemotron-nano-12b-v2-vl:free",
|
|
model = "google/gemini-2.5-flash-image",
|
|
// model = "google/gemini-2.5-flash-lite",
|
|
// model = "google/gemini-2.5-flash-lite-preview-09-2025",
|
|
temperature = 0,
|
|
messages = new object[]
|
|
{
|
|
new
|
|
{
|
|
role = "user",
|
|
content = new object[]
|
|
{
|
|
new
|
|
{
|
|
type = "text",
|
|
text = @"
|
|
Baca angka berat timbangan digital pada foto.
|
|
|
|
Rules:
|
|
- Abaikan tulisan seperti ZERO, TARE, STABLE, AC, PACK, PCS, KG, ADD, HOLD.
|
|
- Jawab hanya angka dengan format 2 digit desimal pakai titik (contoh: 54.45).
|
|
- Jika tidak terbaca jawab: UNREADABLE
|
|
- Fokus pada angka layar LED merah yang menyala.
|
|
|
|
Saya berikan 3 contoh foto timbangan yang benar:
|
|
Foto 1 = 75.23
|
|
Foto 2 = 79.86
|
|
Foto 3 = 54.45
|
|
|
|
Sekarang baca angka pada foto terakhir."
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = "https://res.cloudinary.com/drejcprhe/image/upload/v1770888384/Notes_-_2026-02-11_08.52.31_wonhbm.jpg" }
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = "https://res.cloudinary.com/drejcprhe/image/upload/v1770888429/Notes_-_2026-02-11_08.52.34_xairzy.jpg" }
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = "https://res.cloudinary.com/drejcprhe/image/upload/v1770888473/ChatGPT_Image_Feb_11_2026_03_00_33_PM_ujhdlw.png" }
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = dataUrl }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(payload);
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, "https://openrouter.ai/api/v1/chat/completions");
|
|
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {apiKey}");
|
|
request.Headers.TryAddWithoutValidation("Accept", "application/json");
|
|
request.Headers.TryAddWithoutValidation("HTTP-Referer", "https://yourdomain.com");
|
|
request.Headers.TryAddWithoutValidation("X-Title", "eSPJ OCR Timbangan");
|
|
|
|
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
|
|
var client = _httpClientFactory.CreateClient();
|
|
using var response = await client.SendAsync(request);
|
|
var responseText = await response.Content.ReadAsStringAsync();
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
return StatusCode((int)response.StatusCode, new
|
|
{
|
|
success = false,
|
|
message = "OpenRouter request gagal.",
|
|
detail = responseText
|
|
});
|
|
}
|
|
|
|
using var doc = JsonDocument.Parse(responseText);
|
|
|
|
var content = doc.RootElement
|
|
.GetProperty("choices")[0]
|
|
.GetProperty("message")
|
|
.GetProperty("content")
|
|
.GetString() ?? "";
|
|
|
|
content = content.Trim();
|
|
|
|
if (content.Contains("UNREADABLE", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Ok(new
|
|
{
|
|
success = false,
|
|
message = "Angka tidak terbaca.",
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
// cari format angka 2 desimal
|
|
var match = Regex.Match(content, @"-?\d{1,5}([.,]\d{2})");
|
|
|
|
if (!match.Success)
|
|
{
|
|
return Ok(new
|
|
{
|
|
success = false,
|
|
message = "AI tidak menemukan angka valid.",
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
var normalized = match.Value.Replace(',', '.');
|
|
|
|
if (!decimal.TryParse(normalized, NumberStyles.Any, CultureInfo.InvariantCulture, out var weight))
|
|
{
|
|
return Ok(new
|
|
{
|
|
success = false,
|
|
message = "Format angka AI tidak valid.",
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
return Ok(new
|
|
{
|
|
success = true,
|
|
weight = weight.ToString("0.00", CultureInfo.InvariantCulture),
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
}
|
|
}
|