Sfoglia il codice sorgente

捐贈碼改用清單

master
Joanne.Chuang 2 settimane fa
parent
commit
c6c9497248
4 ha cambiato i file con 132 aggiunte e 4 eliminazioni
  1. +44
    -3
      Altob.NtuInvoiceGateway/Pages/Invoice.cshtml
  2. +82
    -1
      Altob.NtuInvoiceGateway/Pages/Invoice.cshtml.cs
  3. +3
    -0
      Altob.NtuInvoiceGateway/Program.cs
  4. +3
    -0
      Altob.NtuInvoiceGateway/appsettings.json

+ 44
- 3
Altob.NtuInvoiceGateway/Pages/Invoice.cshtml Vedi File

@@ -84,8 +84,10 @@
</div>
<div class="col-md-6">
<label asp-for="InvoiceData.LoveCode" class="form-label">捐贈碼</label>
<input asp-for="InvoiceData.LoveCode" class="form-control" maxlength="7" placeholder="123456" />
<small class="form-text text-muted">輸入為捐贈發票</small>
<select asp-for="InvoiceData.LoveCode" class="form-control" id="loveCodeSelect">
<option value="">請選擇捐贈機構</option>
</select>
<small class="form-text text-muted">選擇捐贈機構後即為捐贈發票</small>
<span asp-validation-for="InvoiceData.LoveCode" class="text-danger"></span>
</div>
</div>
@@ -111,8 +113,47 @@

@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<link href="js/select2/select2.min.css" rel="stylesheet" />
<link href="js/select2/select2-bootstrap-5-theme.min.css" rel="stylesheet" />
<script src="js/select2/select2.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 載入捐贈碼資料
const loveCodeSelect = $('#loveCodeSelect');

fetch('/Invoice?handler=DonateCodes')
.then(response => {
if (!response.ok) {
throw new Error('載入捐贈碼資料失敗');
}
return response.json();
})
.then(data => {
data.forEach(item => {
const optionText = `(${item.donateCode}) ${item.donateNm}`;
const option = new Option(optionText, item.donateCode, false, false);
loveCodeSelect.append(option);
});

// 初始化 select2
loveCodeSelect.select2({
theme: 'bootstrap-5',
placeholder: '請選擇捐贈機構',
allowClear: true,
language: {
noResults: function() {
return "查無結果";
},
searching: function() {
return "搜尋中...";
}
}
});
})
.catch(error => {
console.error('載入捐贈碼資料失敗:', error);
});

const errorAlert = document.getElementById('formErrorMessage');
const errorText = document.getElementById('formErrorMessageText');
const errorCloseBtn = errorAlert?.querySelector('[data-error-close]');
@@ -142,7 +183,7 @@
document.querySelector('input[name="InvoiceData.Email"]'),
document.querySelector('input[name="InvoiceData.CarrierID"]'),
document.querySelector('input[name="InvoiceData.BuyerIdentifier"]'),
document.querySelector('input[name="InvoiceData.LoveCode"]')
document.querySelector('select[name="InvoiceData.LoveCode"]')
];

const form = document.querySelector('form[method="post"]');


+ 82
- 1
Altob.NtuInvoiceGateway/Pages/Invoice.cshtml.cs Vedi File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Caching.Memory;
using Altob.NtuInvoiceGateway.Models;
using System.Text;
using System.Text.Json;
@@ -13,20 +14,27 @@ public class InvoiceModel : PageModel
{
private readonly ILogger<InvoiceModel> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IMemoryCache _memoryCache;
private readonly CompanyInfo _companyInfo;
private readonly InvoiceApiOptions _invoiceApiOptions;
private readonly DonateCodeApiOptions _donateCodeApiOptions;
private const string ServiceName = "NtuInvoiceGateway";
private const string DonateCodeCacheKey = "DonateCodeList";

public InvoiceModel(
ILogger<InvoiceModel> logger,
IHttpClientFactory httpClientFactory,
IMemoryCache memoryCache,
IOptions<CompanyInfo> companyInfo,
IOptions<InvoiceApiOptions> invoiceApiOptions)
IOptions<InvoiceApiOptions> invoiceApiOptions,
IOptions<DonateCodeApiOptions> donateCodeApiOptions)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_memoryCache = memoryCache;
_companyInfo = companyInfo.Value;
_invoiceApiOptions = invoiceApiOptions.Value;
_donateCodeApiOptions = donateCodeApiOptions.Value;
}

[BindProperty]
@@ -48,6 +56,74 @@ public class InvoiceModel : PageModel
// GET 方法保留為空,主要接收方式改為 POST JSON
}

public async Task<IActionResult> OnGetDonateCodesAsync()
{
var actionName = nameof(OnGetDonateCodesAsync);

try
{
// 檢查 Cache 是否有資料
if (_memoryCache.TryGetValue(DonateCodeCacheKey, out List<DonateGroup>? cachedData) && cachedData != null)
{
_logger.LogInformation("{ServiceName} - {ActionName} returning cached donate codes, count: {Count}",
ServiceName, actionName, cachedData.Count);
return new JsonResult(cachedData);
}

// Cache 中沒有資料,從 API 取得
if (string.IsNullOrWhiteSpace(_donateCodeApiOptions.Endpoint))
{
_logger.LogError("{ServiceName} - {ActionName} DonateCode API endpoint is not configured", ServiceName, actionName);
return new JsonResult(new List<DonateGroup>()) { StatusCode = 500 };
}

_logger.LogInformation("{ServiceName} - {ActionName} fetching donate codes from API: {Endpoint}",
ServiceName, actionName, _donateCodeApiOptions.Endpoint);

var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.GetAsync(_donateCodeApiOptions.Endpoint);

if (!response.IsSuccessStatusCode)
{
_logger.LogError("{ServiceName} - {ActionName} failed to fetch donate codes, status: {StatusCode}",
ServiceName, actionName, response.StatusCode);
return new JsonResult(new List<DonateGroup>()) { StatusCode = (int)response.StatusCode };
}

var responseContent = await response.Content.ReadAsStringAsync();
var donateCodes = JsonSerializer.Deserialize<List<DonateGroup>>(responseContent, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});

if (donateCodes != null && donateCodes.Count > 0)
{
// 按照 Seq 欄位排序
var sortedDonateCodes = donateCodes.OrderBy(d => d.Seq).ToList();

// 將資料存入 Cache,有效期限 7 天
var cacheOptions = new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(7)
};
_memoryCache.Set(DonateCodeCacheKey, sortedDonateCodes, cacheOptions);

_logger.LogInformation("{ServiceName} - {ActionName} cached {Count} donate codes for 7 days",
ServiceName, actionName, sortedDonateCodes.Count);

return new JsonResult(sortedDonateCodes);
}

_logger.LogWarning("{ServiceName} - {ActionName} received empty donate code list from API", ServiceName, actionName);
return new JsonResult(new List<DonateGroup>());
}
catch (Exception ex)
{
_logger.LogError(ex, "{ServiceName} - {ActionName} error fetching donate codes", ServiceName, actionName);
return new JsonResult(new List<DonateGroup>()) { StatusCode = 500 };
}
}

public async Task<IActionResult> OnPostAsync()
{
NormalizeInvoiceData();
@@ -237,6 +313,11 @@ public class InvoiceModel : PageModel
InvoiceData.CarPlateNum = InvoiceData.CarPlateNum ?? string.Empty;
InvoiceData.OrderID = InvoiceData.OrderID ?? string.Empty;
InvoiceData.TaxType = InvoiceData.TaxType ?? string.Empty;
InvoiceData.Email = InvoiceData.Email ?? string.Empty;
InvoiceData.CarrierID = InvoiceData.CarrierID ?? string.Empty;
InvoiceData.BuyerIdentifier = InvoiceData.BuyerIdentifier ?? string.Empty;
InvoiceData.LoveCode = InvoiceData.LoveCode ?? string.Empty;
}

private void RestoreDisplayValuesFromTempData()


+ 3
- 0
Altob.NtuInvoiceGateway/Program.cs Vedi File

@@ -15,12 +15,15 @@ builder.Host.UseSerilog((context, services, configuration) =>
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddHttpClient();
builder.Services.AddMemoryCache();

// Configure company info
builder.Services.Configure<Altob.NtuInvoiceGateway.Models.CompanyInfo>(
builder.Configuration.GetSection("CompanyInfo"));
builder.Services.Configure<Altob.NtuInvoiceGateway.Models.InvoiceApiOptions>(
builder.Configuration.GetSection("InvoiceApi"));
builder.Services.Configure<Altob.NtuInvoiceGateway.Models.DonateCodeApiOptions>(
builder.Configuration.GetSection("DonateCodeApi"));
builder.Services.Configure<IpWhitelistOptions>(
builder.Configuration.GetSection("IpWhitelist"));



+ 3
- 0
Altob.NtuInvoiceGateway/appsettings.json Vedi File

@@ -36,5 +36,8 @@
},
"InvoiceApi": {
"Endpoint": "http://192.168.110.72:22055/api/Intella/invoiceInfo"
},
"DonateCodeApi": {
"Endpoint": "https://dataset.einvoice.nat.gov.tw/ods/portal/api/v1/DonateCodeList"
}
}

Loading…
Annulla
Salva