| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Results;
- namespace Application.Features.Api.Stocks.GetStockDividends;
- internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Result<Response>>
- {
- private const ushort MaxPerPage = 200;
- public async Task<Result<Response>> Handle(Query request, CancellationToken ct)
- {
- var input = request.CodeOrIsin?.Trim();
- if (string.IsNullOrEmpty(input))
- {
- return Result.Failure<Response>(Error.Problem("Stock.CodeRequired", "종목 코드가 필요합니다."));
- }
- // 단축코드(6자리) 우선 → Stock 조인으로 ISIN·IssucoCustno 확보. 미매칭이면 입력을 ISIN 으로 간주.
- var stock = await db.Stock.AsNoTracking()
- .Where(c => c.Code == input || c.ISIN == input)
- .Select(c => new { c.ISIN, c.IssucoCustno })
- .FirstOrDefaultAsync(ct);
- var isin = stock?.ISIN ?? input;
- var issucoCustno = stock?.IssucoCustno;
- var page = request.Page < 1 ? 1 : request.Page;
- var perPage = request.PerPage is 0 or > MaxPerPage ? (ushort)30 : request.PerPage;
- var query = db.Dividend.AsNoTracking().Where(c => c.Isin == isin);
- var total = await query.CountAsync(ct);
- var dividends = await query
- .OrderByDescending(c => c.RgtStdDt)
- .Skip((page - 1) * perPage)
- .Take(perPage)
- .Select(c => new
- {
- c.RgtStdDt,
- c.KorSecnNm,
- c.SecnKacdNm,
- c.Pval,
- c.CashAllocAmt,
- c.StkAllocRatio,
- c.MartpDivRate,
- c.Th1PayTermBeginDt
- })
- .ToListAsync(ct);
- // 권리락일·확정구분 결합 — DividendSchedule (IssucoCustno + 페이지 내 RgtStdDt). 세부유형별 여러 행이면 권리락일 있는 행 우선.
- var scheduleByDate = new Dictionary<DateOnly, (DateOnly? XrgtDt, string? FixType)>();
- if (issucoCustno is not null && dividends.Count > 0)
- {
- var dates = dividends.Select(c => c.RgtStdDt).Distinct().ToList();
- var schedules = await db.DividendSchedule.AsNoTracking()
- .Where(c => c.IssucoCustno == issucoCustno.Value && dates.Contains(c.RgtStdDt))
- .Select(c => new { c.RgtStdDt, c.XrgtDt, c.FixTpcd })
- .ToListAsync(ct);
- foreach (var s in schedules)
- {
- if (!scheduleByDate.TryGetValue(s.RgtStdDt, out var existing) || (existing.XrgtDt is null && s.XrgtDt is not null))
- {
- scheduleByDate[s.RgtStdDt] = (s.XrgtDt, s.FixTpcd);
- }
- }
- }
- var list = dividends.Select(c =>
- {
- scheduleByDate.TryGetValue(c.RgtStdDt, out var sched);
- return new Response.Row
- {
- RgtStdDt = c.RgtStdDt,
- Name = c.KorSecnNm,
- SecurityKind = c.SecnKacdNm,
- FaceValue = c.Pval,
- CashPerShare = c.CashAllocAmt,
- StockAllocRatio = c.StkAllocRatio,
- MarketDividendRate = c.MartpDivRate,
- CashPayDate = c.Th1PayTermBeginDt,
- XrgtDt = sched.XrgtDt,
- FixType = sched.FixType
- };
- }).ToList();
- return new Response
- {
- Isin = isin,
- Total = total,
- List = list
- };
- }
- }
|