summaryrefslogtreecommitdiff
path: root/src/FundLab.Web/App.fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/FundLab.Web/App.fs')
-rw-r--r--src/FundLab.Web/App.fs89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/FundLab.Web/App.fs b/src/FundLab.Web/App.fs
index 7dea8bb..fe16411 100644
--- a/src/FundLab.Web/App.fs
+++ b/src/FundLab.Web/App.fs
@@ -505,6 +505,29 @@ type RawFundValuation =
positions: RawValuationPosition array
}
+type RawMarketRefreshTarget =
+ {
+ instrumentCode: string
+ assetClass: string
+ snapshotDate: string
+ price: string
+ }
+
+type RawMarketRefreshFailure =
+ {
+ instrumentCode: string
+ assetClass: string
+ reason: string
+ }
+
+type RawMarketRefresh =
+ {
+ fundId: string
+ asOfDate: string
+ refreshed: RawMarketRefreshTarget array
+ failures: RawMarketRefreshFailure array
+ }
+
type CreateAttempt =
{
idempotencyKey: string
@@ -996,6 +1019,9 @@ module Api =
[<Import("getFundValuation", "./src/api.js")>]
let getFundValuation (token: string) (fundId: string) : JS.Promise<RawFundValuation> = jsNative
+ [<Import("refreshFundMarketData", "./src/api.js")>]
+ let refreshFundMarketData (token: string) (fundId: string) : JS.Promise<RawMarketRefresh> = jsNative
+
[<Import("createCapitalDeposit", "./src/api.js")>]
let createCapitalDeposit (token: string) (fundId: string) (payload: CreateCapitalPayload) : JS.Promise<obj> = jsNative
@@ -1492,6 +1518,9 @@ type Model =
valuationInFlight: bool
valuation: FundValuationView option
valuationMessage: string option
+ marketRefreshSeq: int
+ marketRefreshInFlight: bool
+ marketRefreshMessage: string option
returnsSelectedDate: string option
planReadSeq: int
planInFlight: bool
@@ -1627,6 +1656,9 @@ type Msg =
| ValuationReadRequested
| ValuationReadCompleted of requestId: int * fundId: string * valuation: RawFundValuation
| ValuationReadFailed of requestId: int * fundId: string * message: string
+ | MarketRefreshRequested
+ | MarketRefreshCompleted of requestId: int * fundId: string * response: RawMarketRefresh
+ | MarketRefreshFailed of requestId: int * fundId: string * message: string
| ReturnsDateChanged of string
| InvestmentPlansReadRequested
| InvestmentPlansReadCompleted of requestId: int * plans: RawInvestmentPlan array
@@ -1797,6 +1829,9 @@ let init () =
valuationInFlight = false
valuation = None
valuationMessage = None
+ marketRefreshSeq = 0
+ marketRefreshInFlight = false
+ marketRefreshMessage = None
returnsSelectedDate = None
planReadSeq = 0
planInFlight = false
@@ -2058,6 +2093,13 @@ let private readValuationCommand token fundId requestId =
(fun valuation -> ValuationReadCompleted(requestId, fundId, valuation))
(fun error -> ValuationReadFailed(requestId, fundId, errorText error))
+let private refreshMarketDataCommand token fundId requestId =
+ Cmd.OfPromise.either
+ (fun () -> Api.refreshFundMarketData token fundId)
+ ()
+ (fun response -> MarketRefreshCompleted(requestId, fundId, response))
+ (fun error -> MarketRefreshFailed(requestId, fundId, errorText error))
+
let private readInvestmentPlansCommand token fundId requestId =
Cmd.OfPromise.either
(fun () -> Api.getInvestmentPlans token fundId)
@@ -3710,6 +3752,42 @@ let update message model =
Cmd.none
else
model, Cmd.none
+ | MarketRefreshRequested ->
+ match model.createdFund with
+ | Some fund when not (String.IsNullOrWhiteSpace model.token) ->
+ let requestId = model.marketRefreshSeq + 1
+
+ {
+ model with
+ marketRefreshSeq = requestId
+ marketRefreshInFlight = true
+ marketRefreshMessage = None
+ },
+ refreshMarketDataCommand model.token fund.id requestId
+ | _ -> model, Cmd.none
+ | MarketRefreshCompleted (requestId, fundId, response) ->
+ if requestId = model.marketRefreshSeq
+ && (match model.createdFund with Some fund -> fund.id = fundId | None -> false) then
+ {
+ model with
+ marketRefreshInFlight = false
+ marketRefreshMessage =
+ Some(sprintf "行情刷新完成(%s):更新 %d 项,失败 %d 项" response.asOfDate response.refreshed.Length response.failures.Length)
+ },
+ Cmd.ofMsg ValuationReadRequested
+ else
+ model, Cmd.none
+ | MarketRefreshFailed (requestId, fundId, message) ->
+ if requestId = model.marketRefreshSeq
+ && (match model.createdFund with Some fund -> fund.id = fundId | None -> false) then
+ {
+ model with
+ marketRefreshInFlight = false
+ marketRefreshMessage = Some(sprintf "行情刷新失败 — %s" message)
+ },
+ Cmd.none
+ else
+ model, Cmd.none
| ReturnsDateChanged value ->
{ model with returnsSelectedDate = (if String.IsNullOrWhiteSpace value then None else Some value) }, Cmd.none
| InvestmentPlansReadRequested ->
@@ -5743,8 +5821,19 @@ let private stocksPanel model dispatch =
prop.onClick (fun _ -> dispatch ValuationReadRequested)
prop.text ((if model.valuationInFlight then "估值中..." else "刷新估值"): string)
]
+ Html.button [
+ prop.className "secondary-action market-refresh-action"
+ prop.disabled model.marketRefreshInFlight
+ prop.onClick (fun _ -> dispatch MarketRefreshRequested)
+ prop.text ((if model.marketRefreshInFlight then "刷新中..." else "刷新行情"): string)
+ ]
]
]
+ Html.p [
+ prop.className "market-refresh-message"
+ prop.hidden (model.marketRefreshMessage.IsNone)
+ prop.text (model.marketRefreshMessage |> Option.defaultValue "")
+ ]
match model.valuation with
| Some valuation ->
Html.div [