From 9f781ce7946814b2f8d39257e6143cefee0e0c4c Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Mon, 15 Sep 2025 13:56:07 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B4=A2=E5=8A=A1?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=9A=84=E9=87=87=E9=9B=86=E5=92=8C=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leopard/core/entity/BalanceSheet.java | 95 ---------- .../leopard/core/entity/CashFlow.java | 61 ------ .../leopard/core/entity/Income.java | 87 --------- .../leopard/core/entity/Stock.java | 12 -- .../repository/BalanceSheetRepository.java | 13 -- .../core/repository/CashFlowRepository.java | 13 -- .../core/repository/IncomeRepository.java | 13 -- .../server/controller/StockController.java | 77 ++++---- .../leopard/server/helper/NumberHelper.java | 21 ++- .../leopard/server/service/StockService.java | 77 ++------ .../server/service/TuShareService.java | 117 ------------ .../task/UpdateFinanceIndicatorNode.java | 157 ++++++++++------ .../service/task/UpdateFinanceNode.java | 174 ------------------ .../src/test/resources/tushare.http | 3 +- .../leopard/strategy/StrategyApplication.java | 97 ---------- leopard-web/src/pages/stock/StockDetail.tsx | 77 +++++--- 16 files changed, 229 insertions(+), 865 deletions(-) delete mode 100644 leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/BalanceSheet.java delete mode 100644 leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/CashFlow.java delete mode 100644 leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Income.java delete mode 100644 leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/BalanceSheetRepository.java delete mode 100644 leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/CashFlowRepository.java delete mode 100644 leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/IncomeRepository.java delete mode 100644 leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceNode.java diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/BalanceSheet.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/BalanceSheet.java deleted file mode 100644 index d688da3..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/BalanceSheet.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.entity; - -import com.lanyuanxiaoyao.leopard.core.Constants; -import com.lanyuanxiaoyao.service.template.entity.SimpleEntity; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import lombok.experimental.FieldNameConstants; -import org.hibernate.annotations.Comment; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -/** - * 资产负债表 - */ -@Setter -@Getter -@ToString(callSuper = true) -@FieldNameConstants -@Entity -@DynamicUpdate -@DynamicInsert -@EntityListeners(AuditingEntityListener.class) -@Table(name = Constants.DATABASE_PREFIX + "balance_sheet") -public class BalanceSheet extends SimpleEntity { - @ManyToOne - private Stock stock; - @Comment("年报年度") - private Integer year; - @Comment("原始名称:total_share,描述:期末总股本") - private Double endingTotalShares; - @Comment("原始名称:cap_rese,描述:资本公积金") - private Double capitalSurplus; - @Comment("原始名称:undistr_porfit,描述:未分配利润") - private Double undistributedProfit; - @Comment("原始名称:money_cap,描述:货币资金") - private Double monetaryFunds; - @Comment("原始名称:accounts_receiv,描述:应收账款") - private Double accountsReceivable; - @Comment("原始名称:inventories,描述:存货") - private Double inventories; - @Comment("原始名称:total_cur_assets,描述:流动资产合计") - private Double totalCurrentAssets; - @Comment("原始名称:lt_eqt_invest,描述:长期股权投资") - private Double longTermEquityInvestments; - @Comment("原始名称:lt_rec,描述:长期应收款") - private Double longTermReceivables; - @Comment("原始名称:fix_assets,描述:固定资产") - private Double fixedAssets; - @Comment("原始名称:r_and_d,描述:研发支出") - private Double researchAndDevelopmentExpenditures; - @Comment("原始名称:goodwill,描述:商誉") - private Double goodwill; - @Comment("原始名称:total_nca,描述:非流动资产合计") - private Double totalNonCurrentAssets; - @Comment("原始名称:total_assets,描述:资产总计") - private Double totalAssets; - @Comment("原始名称:lt_borr,描述:长期借款") - private Double longTermBorrowings; - @Comment("原始名称:st_borr,描述:短期借款") - private Double shortTermBorrowings; - @Comment("原始名称:acct_payable,描述:应付账款") - private Double accountsPayable; - @Comment("原始名称:adv_receipts,描述:预收款项") - private Double advancesReceived; - @Comment("原始名称:total_cur_liab,描述:流动负债合计") - private Double totalCurrentLiabilities; - @Comment("原始名称:total_ncl,描述:非流动负债合计") - private Double totalNonCurrentLiabilities; - @Comment("原始名称:total_liab,描述:负债合计") - private Double totalLiabilities; - @Comment("原始名称:total_hldr_eqy_exc_min_int,描述:股东权益合计(不含少数股东权益)") - private Double totalShareholdersEquityExcludingMinorityInterest; - @Comment("原始名称:total_hldr_eqy_inc_min_int,描述:股东权益合计(含少数股东权益)") - private Double totalShareholdersEquityIncludingMinorityInterest; - @Comment("原始名称:total_liab_hldr_eqy,描述:负债及股东权益总计") - private Double totalLiabilitiesAndShareholdersEquity; - @Comment("原始名称:acc_receivable,描述:应收款项") - private Double receivables; - @Comment("原始名称:payables,描述:应付款项") - private Double payables; - @Comment("原始名称:accounts_receiv_bill,描述:应收票据及应收账款") - private Double notesAndAccountsReceivable; - @Comment("原始名称:accounts_pay,描述:应付票据及应付账款") - private Double notesAndAccountsPayable; - @Comment("原始名称:oth_rcv_total,描述:其他应收款(合计)(元)") - private Double otherReceivablesTotal; - @Comment("原始名称:fix_assets_total,描述:固定资产(合计)(元)") - private Double fixedAssetsTotal; -} diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/CashFlow.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/CashFlow.java deleted file mode 100644 index d9e84b9..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/CashFlow.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.entity; - -import com.lanyuanxiaoyao.leopard.core.Constants; -import com.lanyuanxiaoyao.service.template.entity.SimpleEntity; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import lombok.experimental.FieldNameConstants; -import org.hibernate.annotations.Comment; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -/** - * 现金流量表 - */ -@Setter -@Getter -@ToString(callSuper = true) -@FieldNameConstants -@Entity -@DynamicUpdate -@DynamicInsert -@EntityListeners(AuditingEntityListener.class) -@Table(name = Constants.DATABASE_PREFIX + "cash_flow") -public class CashFlow extends SimpleEntity { - @ManyToOne - private Stock stock; - @Comment("年报年度") - private Integer year; - @Comment("原始名称:net_profit,描述:净利润") - private Double netProfit; - @Comment("原始名称:finan_exp,描述:财务费用") - private Double financialExpense; - @Comment("原始名称:c_fr_sale_sg,描述:销售商品、提供劳务收到的现金") - private Double cashReceivedFromSalesAndServices; - @Comment("原始名称:c_inf_fr_operate_a,描述:经营活动现金流入小计") - private Double subtotalOfCashInflowsFromOperatingActivities; - @Comment("原始名称:c_paid_to_for_empl,描述:支付给职工以及为职工支付的现金") - private Double cashPaidToAndForEmployees; - @Comment("原始名称:c_paid_for_taxes,描述:支付的各项税费") - private Double cashPaidForVariousTaxes; - @Comment("原始名称:n_cashflow_act,描述:经营活动产生的现金流量净额") - private Double netCashFlowFromOperatingActivities; - @Comment("原始名称:stot_inflows_inv_act,描述:投资活动现金流入小计") - private Double subtotalOfCashInflowsFromInvestingActivities; - @Comment("原始名称:c_pay_acq_const_fiolta,描述:购置固定资产、无形资产和其他长期资产支付的现金") - private Double cashPaidForLongTermAssets; - @Comment("原始名称:stot_out_inv_act,描述:投资活动现金流出小计") - private Double subtotalOfCashOutflowsFromInvestingActivities; - @Comment("原始名称:stot_cashout_fnc_act,描述:筹资活动现金流出小计") - private Double subtotalOfCashOutflowsFromFinancingActivities; - @Comment("原始名称:c_cash_equ_beg_period,描述:期初现金及现金等价物余额") - private Double beginningBalanceOfCashAndCashEquivalents; - @Comment("原始名称:c_cash_equ_end_period,描述:期末现金及现金等价物余额") - private Double endingBalanceOfCashAndCashEquivalents; -} diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Income.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Income.java deleted file mode 100644 index 8415134..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Income.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.entity; - -import com.lanyuanxiaoyao.leopard.core.Constants; -import com.lanyuanxiaoyao.service.template.entity.SimpleEntity; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import lombok.experimental.FieldNameConstants; -import org.hibernate.annotations.Comment; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -/** - * 利润表 - */ -@Setter -@Getter -@ToString(callSuper = true) -@FieldNameConstants -@Entity -@DynamicUpdate -@DynamicInsert -@EntityListeners(AuditingEntityListener.class) -@Table(name = Constants.DATABASE_PREFIX + "income") -public class Income extends SimpleEntity { - @ManyToOne - private Stock stock; - @Comment("年报年度") - private Integer year; - @Comment("原始名称:basic_eps,描述:基本每股收益") - private Double basicEarningsPerShare; - @Comment("原始名称:diluted_eps,描述:稀释每股收益") - private Double dilutedEarningsPerShare; - @Comment("原始名称:total_revenue,描述:营业总收入") - private Double totalOperatingRevenue; - @Comment("原始名称:revenue,描述:营业收入") - private Double operatingRevenue; - @Comment("原始名称:total_cogs,描述:营业总成本") - private Double totalOperatingCost; - @Comment("原始名称:oper_cost,描述:减:营业成本") - private Double operatingCost; - @Comment("原始名称:sell_exp,描述:减:销售费用") - private Double sellingExpense; - @Comment("原始名称:admin_exp,描述:减:管理费用") - private Double administrativeExpense; - @Comment("原始名称:fin_exp,描述:减:财务费用") - private Double financialExpense; - @Comment("原始名称:oper_exp,描述:营业支出") - private Double operatingExpense; - @Comment("原始名称:operate_profit,描述:营业利润") - private Double operatingProfit; - @Comment("原始名称:non_oper_income,描述:加:营业外收入") - private Double addNonOperatingIncome; - @Comment("原始名称:non_oper_exp,描述:减:营业外支出") - private Double lessNonOperatingExpense; - @Comment("原始名称:total_profit,描述:利润总额") - private Double totalProfit; - @Comment("原始名称:income_tax,描述:所得税费用") - private Double incomeTaxExpense; - @Comment("原始名称:n_income,描述:净利润(含少数股东损益)") - private Double netProfitIncludingMinorityInterest; - @Comment("原始名称:n_income_attr_p,描述:净利润(不含少数股东损益)") - private Double netProfitExcludingMinorityInterest; - @Comment("原始名称:compr_inc_attr_p,描述:归属于母公司(或股东)的综合收益总额") - private Double comprehensiveIncomeAttributableToParent; - @Comment("原始名称:compr_inc_attr_m_s,描述:归属于少数股东的综合收益总额") - private Double comprehensiveIncomeAttributableToMinorityShareholders; - @Comment("原始名称:ebit,描述:息税前利润") - private Double earningsBeforeInterestAndTax; - @Comment("原始名称:undist_profit,描述:年初未分配利润") - private Double beginningUndistributedProfit; - @Comment("原始名称:distable_profit,描述:可分配利润") - private Double distributableProfit; - @Comment("原始名称:rd_exp,描述:研发费用") - private Double researchAndDevelopmentExpense; - @Comment("原始名称:fin_exp_int_exp,描述:财务费用-利息费用") - private Double financialExpenseInterestExpense; - @Comment("原始名称:continued_net_profit,描述:持续经营净利润") - private Double netProfitFromContinuingOperations; - @Comment("原始名称:end_net_profit,描述:终止经营净利润") - private Double netProfitFromDiscontinuedOperations; -} diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java index 29357aa..cd0e838 100644 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java +++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java @@ -61,18 +61,6 @@ public class Stock extends SimpleEntity { @ToString.Exclude private Set dailies; - @OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE) - @ToString.Exclude - private Set incomes; - - @OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE) - @ToString.Exclude - private Set balanceSheets; - - @OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE) - @ToString.Exclude - private Set cashFlows; - @OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE) @ToString.Exclude private Set indicators; diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/BalanceSheetRepository.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/BalanceSheetRepository.java deleted file mode 100644 index 62990e3..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/BalanceSheetRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.repository; - -import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet; -import com.lanyuanxiaoyao.service.template.repository.SimpleRepository; -import org.springframework.stereotype.Repository; - -/** - * @author lanyuanxiaoyao - * @version 20250911 - */ -@Repository -public interface BalanceSheetRepository extends SimpleRepository { -} diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/CashFlowRepository.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/CashFlowRepository.java deleted file mode 100644 index d5a9cd2..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/CashFlowRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.repository; - -import com.lanyuanxiaoyao.leopard.core.entity.CashFlow; -import com.lanyuanxiaoyao.service.template.repository.SimpleRepository; -import org.springframework.stereotype.Repository; - -/** - * @author lanyuanxiaoyao - * @version 20250911 - */ -@Repository -public interface CashFlowRepository extends SimpleRepository { -} diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/IncomeRepository.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/IncomeRepository.java deleted file mode 100644 index 305d458..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/IncomeRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.repository; - -import com.lanyuanxiaoyao.leopard.core.entity.Income; -import com.lanyuanxiaoyao.service.template.repository.SimpleRepository; -import org.springframework.stereotype.Repository; - -/** - * @author lanyuanxiaoyao - * @version 20250911 - */ -@Repository -public interface IncomeRepository extends SimpleRepository { -} diff --git a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockController.java b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockController.java index 9b55289..e4e6347 100644 --- a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockController.java +++ b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockController.java @@ -7,7 +7,6 @@ import com.lanyuanxiaoyao.leopard.server.service.StockService; import com.lanyuanxiaoyao.service.template.controller.GlobalResponse; import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport; import java.time.LocalDate; -import java.util.List; import java.util.Map; import java.util.function.Function; import org.springframework.web.bind.annotation.GetMapping; @@ -38,38 +37,42 @@ public class StockController extends SimpleControllerSupport finance(@PathVariable("id") Long id) { // 财报默认是上一年的 var year = LocalDate.now().minusYears(1).getYear(); - var balanceSheet = stockService.findBalanceSheet(id, year); - var income = stockService.findIncome(id, year); - var cashFlow = stockService.findCashFlow(id, year); + var financeIndicator = stockService.findFinanceIndicator(id, year); return GlobalResponse.responseSuccess(new FinanceItem( id, year, - balanceSheet - .map(bs -> new BalanceSheetItem( - NumberHelper.formatFinanceDouble(bs.getTotalAssets()), - NumberHelper.formatFinanceDouble(bs.getTotalCurrentAssets()), - NumberHelper.formatFinanceDouble(bs.getTotalNonCurrentAssets()), - NumberHelper.formatFinanceDouble(bs.getTotalLiabilities()), - NumberHelper.formatFinanceDouble(bs.getTotalCurrentLiabilities()), - NumberHelper.formatFinanceDouble(bs.getTotalNonCurrentLiabilities()) + financeIndicator + .map(fi -> new BalanceSheetItem( + NumberHelper.formatFinanceDouble(fi.getTotalAssets()), + NumberHelper.formatFinanceDouble(fi.getCurrentAssets()), + NumberHelper.formatPercentageDouble(fi.getCurrentAssetsToTotalAssetsRatio()), + NumberHelper.formatFinanceDouble(fi.getFixedAssets()), + NumberHelper.formatPercentageDouble(fi.getFixedAssetsToTotalAssetsRatio()), + NumberHelper.formatFinanceDouble(fi.getTotalLiabilities()), + NumberHelper.formatFinanceDouble(fi.getCurrentLiabilities()), + NumberHelper.formatPercentageDouble(fi.getCurrentLiabilitiesToTotalLiabilitiesRatio()), + NumberHelper.formatFinanceDouble(fi.getLongTermLiabilities()), + NumberHelper.formatPercentageDouble(fi.getLongTermLiabilitiesToTotalLiabilitiesRatio()) )) .orElse(new BalanceSheetItem()), - income - .map(ic -> new IncomeItem( - NumberHelper.formatFinanceDouble(ic.getTotalOperatingRevenue()), - NumberHelper.formatFinanceDouble(ic.getTotalOperatingCost()), - NumberHelper.formatFinanceDouble(ic.getTotalProfit()) + financeIndicator + .map(fi -> new IncomeItem( + NumberHelper.formatFinanceDouble(fi.getOperatingRevenue()), + NumberHelper.formatFinanceDouble(fi.getOperatingCost()), + NumberHelper.formatFinanceDouble(fi.getOperatingProfit()) )) .orElse(new IncomeItem()), - cashFlow - .map(cf -> new CashFlowItem( - NumberHelper.formatFinanceDouble(cf.getNetProfit()) + financeIndicator + .map(fi -> new CashFlowItem( + NumberHelper.formatFinanceDouble(fi.getNetProfit()) )) .orElse(new CashFlowItem()) )); } - private GlobalResponse> convertFinanceChartData(List data, String field) { + @GetMapping("finance/{id}/{field}") + public GlobalResponse> financeCharts(@PathVariable("id") Long id, @PathVariable("field") String field) { + var data = stockService.findFinanceIndicatorRecent(id, 5); return GlobalResponse.responseDetailData( data.stream() .map(item -> BeanUtil.getFieldValue(item, field)) @@ -77,16 +80,6 @@ public class StockController extends SimpleControllerSupport> financeCharts(@PathVariable("id") Long id, @PathVariable("type") String type, @PathVariable("field") String field) { - return switch (type) { - case "balanceSheet" -> convertFinanceChartData(stockService.findBalanceSheetRecent(id, 5), field); - case "income" -> convertFinanceChartData(stockService.findIncomeRecent(id, 5), field); - case "cashflow" -> convertFinanceChartData(stockService.findCashFlowRecent(id, 5), field); - default -> throw new IllegalStateException("Unexpected value: " + type); - }; - } - @Override protected Function saveItemMapper() { throw new UnsupportedOperationException(); @@ -136,14 +129,22 @@ public class StockController extends SimpleControllerSupport 100000000) { + builder.append(value.longValue() / 100000000).append("亿"); + value = value % 100000000; + } + if (value > 10000) { + builder.append(value.longValue() / 10000).append("万"); + value = value % 10000; + } + if (value > 0) { + builder.append(NumberUtil.decimalFormat("#.##", value)); + } + return builder.toString(); + } + + public static String formatPercentageDouble(Double value) { + if (ObjectUtil.isNull(value)) { + return null; + } + return NumberUtil.decimalFormat("0.00%", value); } public static Double parseDouble(String value) { diff --git a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/StockService.java b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/StockService.java index 4e4068f..e1ee3a0 100644 --- a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/StockService.java +++ b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/StockService.java @@ -1,18 +1,10 @@ package com.lanyuanxiaoyao.leopard.server.service; -import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet; -import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet_; -import com.lanyuanxiaoyao.leopard.core.entity.CashFlow; -import com.lanyuanxiaoyao.leopard.core.entity.CashFlow_; -import com.lanyuanxiaoyao.leopard.core.entity.Income; -import com.lanyuanxiaoyao.leopard.core.entity.Income_; -import com.lanyuanxiaoyao.leopard.core.entity.QBalanceSheet; -import com.lanyuanxiaoyao.leopard.core.entity.QCashFlow; -import com.lanyuanxiaoyao.leopard.core.entity.QIncome; +import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator; +import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator_; +import com.lanyuanxiaoyao.leopard.core.entity.QFinanceIndicator; import com.lanyuanxiaoyao.leopard.core.entity.Stock; -import com.lanyuanxiaoyao.leopard.core.repository.BalanceSheetRepository; -import com.lanyuanxiaoyao.leopard.core.repository.CashFlowRepository; -import com.lanyuanxiaoyao.leopard.core.repository.IncomeRepository; +import com.lanyuanxiaoyao.leopard.core.repository.FinanceIndicatorRepository; import com.lanyuanxiaoyao.leopard.core.repository.StockRepository; import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport; import java.time.LocalDate; @@ -30,63 +22,28 @@ import org.springframework.stereotype.Service; @Service public class StockService extends SimpleServiceSupport { private final StockRepository stockRepository; - private final BalanceSheetRepository balanceSheetRepository; - private final IncomeRepository incomeRepository; - private final CashFlowRepository cashFlowRepository; + private final FinanceIndicatorRepository financeIndicatorRepository; - public StockService(StockRepository repository, BalanceSheetRepository balanceSheetRepository, IncomeRepository incomeRepository, CashFlowRepository cashFlowRepository) { + + public StockService(StockRepository repository, FinanceIndicatorRepository financeIndicatorRepository) { super(repository); this.stockRepository = repository; - this.balanceSheetRepository = balanceSheetRepository; - this.incomeRepository = incomeRepository; - this.cashFlowRepository = cashFlowRepository; + this.financeIndicatorRepository = financeIndicatorRepository; } - public Optional findBalanceSheet(Long stockId, Integer year) { - return balanceSheetRepository.findOne( - QBalanceSheet.balanceSheet.year.eq(year) - .and(QBalanceSheet.balanceSheet.stock.id.eq(stockId)) + public Optional findFinanceIndicator(Long stockId, Integer year) { + return financeIndicatorRepository.findOne( + QFinanceIndicator.financeIndicator.year.eq(year) + .and(QFinanceIndicator.financeIndicator.stock.id.eq(stockId)) ); } - public Optional findIncome(Long stockId, Integer year) { - return incomeRepository.findOne( - QIncome.income.year.eq(year) - .and(QIncome.income.stock.id.eq(stockId)) - ); - } - - public Optional findCashFlow(Long stockId, Integer year) { - return cashFlowRepository.findOne( - QCashFlow.cashFlow.year.eq(year) - .and(QCashFlow.cashFlow.stock.id.eq(stockId)) - ); - } - - public List findBalanceSheetRecent(Long stockId, int years) { + public List findFinanceIndicatorRecent(Long stockId, int years) { var current = LocalDate.now(); - return balanceSheetRepository.findAll( - QBalanceSheet.balanceSheet.stock.id.eq(stockId) - .and(QBalanceSheet.balanceSheet.year.between(current.minusYears(years).getYear(), current.getYear())), - Sort.by(Sort.Direction.ASC, BalanceSheet_.YEAR) - ); - } - - public List findIncomeRecent(Long stockId, int years) { - var current = LocalDate.now(); - return incomeRepository.findAll( - QIncome.income.stock.id.eq(stockId) - .and(QIncome.income.year.between(current.minusYears(years).getYear(), current.getYear())), - Sort.by(Sort.Direction.ASC, Income_.YEAR) - ); - } - - public List findCashFlowRecent(Long stockId, int years) { - var current = LocalDate.now(); - return cashFlowRepository.findAll( - QCashFlow.cashFlow.stock.id.eq(stockId) - .and(QCashFlow.cashFlow.year.between(current.minusYears(years).getYear(), current.getYear())), - Sort.by(Sort.Direction.ASC, CashFlow_.YEAR) + return financeIndicatorRepository.findAll( + QFinanceIndicator.financeIndicator.stock.id.eq(stockId) + .and(QFinanceIndicator.financeIndicator.year.between(current.minusYears(years).getYear(), current.getYear())), + Sort.by(Sort.Direction.ASC, FinanceIndicator_.YEAR) ); } } diff --git a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/TuShareService.java b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/TuShareService.java index 18307c0..73d4212 100644 --- a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/TuShareService.java +++ b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/TuShareService.java @@ -109,123 +109,6 @@ public class TuShareService { return tuShareResponse; } - @SneakyThrows - public TuShareResponse incomeList(int year) { - var response = HttpUtil.post(API_URL, buildRequest( - "income_vip", - Map.of("period", LocalDate.of(year, 12, 31).format(TRADE_FORMAT)), - List.of( - "ts_code", - "basic_eps", - "diluted_eps", - "total_revenue", - "revenue", - "total_cogs", - "oper_cost", - "sell_exp", - "admin_exp", - "fin_exp", - "oper_exp", - "operate_profit", - "non_oper_income", - "non_oper_exp", - "total_profit", - "income_tax", - "n_income", - "n_income_attr_p", - "compr_inc_attr_p", - "compr_inc_attr_m_s", - "ebit", - "undist_profit", - "distable_profit", - "rd_exp", - "fin_exp_int_exp", - "continued_net_profit", - "end_net_profit" - ) - )); - var tuShareResponse = mapper.readValue(response, TuShareResponse.class); - if (tuShareResponse.code != 0) { - throw new RuntimeException(tuShareResponse.message); - } - return tuShareResponse; - } - - @SneakyThrows - public TuShareResponse balanceList(int year) { - var response = HttpUtil.post(API_URL, buildRequest( - "balancesheet_vip", - Map.of("period", LocalDate.of(year, 12, 31).format(TRADE_FORMAT)), - List.of( - "ts_code", - "total_share", - "cap_rese", - "undistr_porfit", - "money_cap", - "accounts_receiv", - "inventories", - "total_cur_assets", - "lt_eqt_invest", - "lt_rec", - "fix_assets", - "r_and_d", - "goodwill", - "total_nca", - "total_assets", - "lt_borr", - "st_borr", - "acct_payable", - "adv_receipts", - "total_cur_liab", - "total_ncl", - "total_liab", - "total_hldr_eqy_exc_min_int", - "total_hldr_eqy_inc_min_int", - "total_liab_hldr_eqy", - "acc_receivable", - "payables", - "accounts_receiv_bill", - "accounts_pay", - "oth_rcv_total", - "fix_assets_total" - ) - )); - var tuShareResponse = mapper.readValue(response, TuShareResponse.class); - if (tuShareResponse.code != 0) { - throw new RuntimeException(tuShareResponse.message); - } - return tuShareResponse; - } - - @SneakyThrows - public TuShareResponse cashFlowList(int year) { - var response = HttpUtil.post(API_URL, buildRequest( - "cashflow_vip", - Map.of("period", LocalDate.of(year, 12, 31).format(TRADE_FORMAT)), - List.of( - "ts_code", - "net_profit", - "finan_exp", - "c_fr_sale_sg", - "c_inf_fr_operate_a", - "c_paid_to_for_empl", - "c_paid_for_taxes", - "n_cashflow_act", - "stot_inflows_inv_act", - "c_pay_acq_const_fiolta", - "stot_out_inv_act", - "stot_cashout_fnc_act", - "c_cash_equ_beg_period", - "c_cash_equ_end_period" - ) - )); - var tuShareResponse = mapper.readValue(response, TuShareResponse.class); - if (tuShareResponse.code != 0) { - throw new RuntimeException(tuShareResponse.message); - } - return tuShareResponse; - } - public List> request(String api, Map params, List fields) throws JsonProcessingException { var response = HttpUtil.post(API_URL, buildRequest(api, params, fields)); var tuShareResponse = mapper.readValue(response, TuShareResponse.class); diff --git a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceIndicatorNode.java b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceIndicatorNode.java index 02456b0..920fcfa 100644 --- a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceIndicatorNode.java +++ b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceIndicatorNode.java @@ -1,6 +1,9 @@ package com.lanyuanxiaoyao.leopard.server.service.task; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator; +import com.lanyuanxiaoyao.leopard.core.entity.QFinanceIndicator; import com.lanyuanxiaoyao.leopard.core.entity.Stock; import com.lanyuanxiaoyao.leopard.core.repository.FinanceIndicatorRepository; import com.lanyuanxiaoyao.leopard.core.repository.StockRepository; @@ -15,7 +18,7 @@ import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @Slf4j -@LiteflowComponent("update_finance_indicators") +@LiteflowComponent("update_finance") public class UpdateFinanceIndicatorNode extends TaskNodeComponent { private final FinanceIndicatorRepository financeIndicatorRepository; private final StockRepository stockRepository; @@ -57,7 +60,11 @@ public class UpdateFinanceIndicatorNode extends TaskNodeComponent { "total_assets" ) ); - var balancesMap = balances.stream().collect(Collectors.toMap(balance -> balance.get("ts_code"), balance -> balance)); + var balancesMap = balances.stream().collect(Collectors.toMap( + map -> map.get("ts_code"), + map -> map, + (existing, replacement) -> existing + )); var incomes = tuShareService.request( "income_vip", Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)), @@ -70,21 +77,31 @@ public class UpdateFinanceIndicatorNode extends TaskNodeComponent { "n_income" ) ); - var incomesMap = incomes.stream().collect(Collectors.toMap(income -> income.get("ts_code"), income -> income)); + var incomesMap = incomes.stream().collect(Collectors.toMap( + map -> map.get("ts_code"), + map -> map, + (existing, replacement) -> existing + )); var cashFlows = tuShareService.request( "cashflow_vip", Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)), List.of( + "ts_code", "n_cashflow_act", "n_cashflow_inv_act", "n_cash_flows_fnc_act" ) ); - var cashFlowsMap = cashFlows.stream().collect(Collectors.toMap(cashFlow -> cashFlow.get("ts_code"), cashFlow -> cashFlow)); + var cashFlowsMap = cashFlows.stream().collect(Collectors.toMap( + map -> map.get("ts_code"), + map -> map, + (existing, replacement) -> existing + )); var finaIndicators = tuShareService.request( "fina_indicator_vip", Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)), List.of( + "ts_code", "ca_to_assets", "nca_to_assets", "currentdebt_to_debt", @@ -103,72 +120,92 @@ public class UpdateFinanceIndicatorNode extends TaskNodeComponent { "total_revenue_ps" ) ); - var finaIndicatorsMap = finaIndicators.stream().collect(Collectors.toMap(finaIndicator -> finaIndicator.get("ts_code"), finaIndicator -> finaIndicator)); + var finaIndicatorsMap = finaIndicators.stream().collect(Collectors.toMap( + map -> map.get("ts_code"), + map -> map, + (existing, replacement) -> existing + )); for (Stock stock : stocks) { var balance = balancesMap.get(stock.getCode()); var income = incomesMap.get(stock.getCode()); var cashFlow = cashFlowsMap.get(stock.getCode()); var finaIndicator = finaIndicatorsMap.get(stock.getCode()); - var indicator = new FinanceIndicator(); + if (ArrayUtil.isAllNull(balance, income, cashFlow, finaIndicator)) { + continue; + } + var indicator = financeIndicatorRepository.findOne( + QFinanceIndicator.financeIndicator.stock.id.eq(stock.getId()) + .and(QFinanceIndicator.financeIndicator.year.eq(year)) + ).orElse(new FinanceIndicator()); indicator.setStock(stock); indicator.setYear(year); - indicator.setTotalAssets(NumberHelper.parseDouble(balance.get("total_assets"))); - indicator.setTotalShareCapital(NumberHelper.parseDouble(balance.get("total_share"))); - indicator.setCapitalSurplus(NumberHelper.parseDouble(balance.get("cap_rese"))); - indicator.setSurplusReserve(NumberHelper.parseDouble(balance.get("surplus_rese"))); - indicator.setUndistributedProfit(NumberHelper.parseDouble(balance.get("undistr_porfit"))); - indicator.setCashAndCashEquivalents(NumberHelper.parseDouble(balance.get("cash_reser_cb"))); - indicator.setCashAndCashEquivalentsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCashAndCashEquivalents(), indicator.getTotalAssets())); - indicator.setAccountsReceivable(NumberHelper.parseDouble(balance.get("accounts_receiv_bill"))); - indicator.setAccountsReceivableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsReceivable(), indicator.getTotalAssets())); - indicator.setAccountsPayable(NumberHelper.parseDouble(balance.get("accounts_pay"))); - indicator.setAccountsPayableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsPayable(), indicator.getTotalAssets())); - indicator.setInventory(NumberHelper.parseDouble(balance.get("inventories"))); - indicator.setInventoryToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getInventory(), indicator.getTotalAssets())); - indicator.setGoodwill(NumberHelper.parseDouble(balance.get("goodwill"))); - indicator.setGoodwillToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getGoodwill(), indicator.getTotalAssets())); - indicator.setCurrentAssets(NumberHelper.parseDouble(balance.get("total_cur_assets"))); - indicator.setCurrentAssetsToTotalAssetsRatio(NumberHelper.parseDouble(finaIndicator.get("ca_to_assets"))); - indicator.setFixedAssets(NumberHelper.parseDouble(balance.get("total_nca"))); - indicator.setFixedAssetsToTotalAssetsRatio(NumberHelper.parseDouble(finaIndicator.get("nca_to_assets"))); - indicator.setTotalLiabilities(NumberHelper.parseDouble(balance.get("total_liab"))); - indicator.setCurrentLiabilities(NumberHelper.parseDouble(balance.get("total_cur_liab"))); - indicator.setCurrentLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCurrentLiabilities(), indicator.getTotalAssets())); - indicator.setCurrentLiabilitiesToTotalLiabilitiesRatio(NumberHelper.parseDouble(finaIndicator.get("currentdebt_to_debt"))); - indicator.setLongTermLiabilities(NumberHelper.parseDouble(balance.get("total_ncl"))); - indicator.setLongTermLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getLongTermLiabilities(), indicator.getTotalAssets())); - indicator.setLongTermLiabilitiesToTotalLiabilitiesRatio(NumberHelper.parseDouble(finaIndicator.get("longdeb_to_debt"))); - indicator.setLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getTotalLiabilities(), indicator.getTotalAssets())); - indicator.setShareholdersEquity(NumberHelper.parseDouble(balance.get("total_hldr_eqy_inc_min_int"))); - indicator.setShareholdersEquityToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getShareholdersEquity(), indicator.getTotalAssets())); - indicator.setOperatingRevenue(NumberHelper.parseDouble(income.get("total_revenue"))); - indicator.setOperatingCost(NumberHelper.parseDouble(income.get("total_cogs"))); - indicator.setOperatingProfit(NumberHelper.parseDouble(income.get("operate_profit"))); - indicator.setOperatingExpenses(NumberHelper.parseDouble(income.get("oper_exp"))); - indicator.setNetProfit(NumberHelper.parseDouble(income.get("n_income"))); - indicator.setCashFlowFromOperatingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_act"))); - indicator.setCashFlowFromInvestingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_inv_act"))); - indicator.setCashFlowFromFinancingActivities(NumberHelper.parseDouble(cashFlow.get("n_cash_flows_fnc_act"))); - indicator.setCurrentRatio(NumberHelper.parseDouble(finaIndicator.get("current_ratio"))); - indicator.setQuickRatio(NumberHelper.parseDouble(finaIndicator.get("quick_ratio"))); - indicator.setAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("ar_turn"))); - indicator.setDaysAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("arturn_days"))); - indicator.setInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("inv_turn"))); - indicator.setDaysInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("invturn_days"))); - indicator.setFixedAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("fa_turn"))); - indicator.setDaysFixedAssetsTurnover(NumberHelper.safeDiv(360.0, indicator.getFixedAssetsTurnover())); - indicator.setTotalAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("assets_turn"))); - indicator.setDaysTotalAssetsTurnover(NumberHelper.safeDiv(360.0, indicator.getTotalAssetsTurnover())); - indicator.setReturnOnEquity(NumberHelper.parseDouble(finaIndicator.get("roe_dt"))); - indicator.setReturnOnAssets(NumberHelper.parseDouble(finaIndicator.get("roa"))); - indicator.setOperatingGrossProfitMargin(NumberHelper.safeDiv(NumberHelper.safeMinus(indicator.getOperatingRevenue(), indicator.getOperatingCost()), indicator.getOperatingRevenue())); - indicator.setOperatingProfitMargin(NumberHelper.safeDiv(indicator.getOperatingProfit(), indicator.getOperatingRevenue())); - indicator.setOperatingSafetyMarginRatio(NumberHelper.safeDiv(indicator.getOperatingProfitMargin(), indicator.getOperatingGrossProfitMargin())); - indicator.setNetProfitMargin(NumberHelper.parseDouble(finaIndicator.get("roa_dp"))); - indicator.setEarningsPerShare(NumberHelper.parseDouble(finaIndicator.get("total_revenue_ps"))); + if (ObjectUtil.isNotNull(balance)) { + indicator.setTotalAssets(NumberHelper.parseDouble(balance.get("total_assets"))); + indicator.setTotalShareCapital(NumberHelper.parseDouble(balance.get("total_share"))); + indicator.setCapitalSurplus(NumberHelper.parseDouble(balance.get("cap_rese"))); + indicator.setSurplusReserve(NumberHelper.parseDouble(balance.get("surplus_rese"))); + indicator.setUndistributedProfit(NumberHelper.parseDouble(balance.get("undistr_porfit"))); + indicator.setCashAndCashEquivalents(NumberHelper.parseDouble(balance.get("cash_reser_cb"))); + indicator.setCashAndCashEquivalentsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCashAndCashEquivalents(), indicator.getTotalAssets())); + indicator.setAccountsReceivable(NumberHelper.parseDouble(balance.get("accounts_receiv_bill"))); + indicator.setAccountsReceivableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsReceivable(), indicator.getTotalAssets())); + indicator.setAccountsPayable(NumberHelper.parseDouble(balance.get("accounts_pay"))); + indicator.setAccountsPayableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsPayable(), indicator.getTotalAssets())); + indicator.setInventory(NumberHelper.parseDouble(balance.get("inventories"))); + indicator.setInventoryToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getInventory(), indicator.getTotalAssets())); + indicator.setGoodwill(NumberHelper.parseDouble(balance.get("goodwill"))); + indicator.setGoodwillToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getGoodwill(), indicator.getTotalAssets())); + indicator.setCurrentAssets(NumberHelper.parseDouble(balance.get("total_cur_assets"))); + indicator.setCurrentAssetsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCurrentAssets(), indicator.getTotalAssets())); + indicator.setFixedAssets(NumberHelper.parseDouble(balance.get("total_nca"))); + indicator.setFixedAssetsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getFixedAssets(), indicator.getTotalAssets())); + indicator.setTotalLiabilities(NumberHelper.parseDouble(balance.get("total_liab"))); + indicator.setCurrentLiabilities(NumberHelper.parseDouble(balance.get("total_cur_liab"))); + indicator.setCurrentLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCurrentLiabilities(), indicator.getTotalAssets())); + indicator.setCurrentLiabilitiesToTotalLiabilitiesRatio(NumberHelper.safeDiv(indicator.getCurrentLiabilities(), indicator.getTotalLiabilities())); + indicator.setLongTermLiabilities(NumberHelper.parseDouble(balance.get("total_ncl"))); + indicator.setLongTermLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getLongTermLiabilities(), indicator.getTotalAssets())); + indicator.setLongTermLiabilitiesToTotalLiabilitiesRatio(NumberHelper.safeDiv(indicator.getLongTermLiabilities(), indicator.getTotalLiabilities())); + indicator.setLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getTotalLiabilities(), indicator.getTotalAssets())); + indicator.setShareholdersEquity(NumberHelper.parseDouble(balance.get("total_hldr_eqy_inc_min_int"))); + indicator.setShareholdersEquityToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getShareholdersEquity(), indicator.getTotalAssets())); + } + if (ObjectUtil.isNotNull(income)) { + indicator.setOperatingRevenue(NumberHelper.parseDouble(income.get("total_revenue"))); + indicator.setOperatingCost(NumberHelper.parseDouble(income.get("total_cogs"))); + indicator.setOperatingProfit(NumberHelper.parseDouble(income.get("operate_profit"))); + indicator.setOperatingExpenses(NumberHelper.parseDouble(income.get("oper_exp"))); + indicator.setNetProfit(NumberHelper.parseDouble(income.get("n_income"))); + } + if (ObjectUtil.isNotNull(cashFlow)) { + indicator.setCashFlowFromOperatingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_act"))); + indicator.setCashFlowFromInvestingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_inv_act"))); + indicator.setCashFlowFromFinancingActivities(NumberHelper.parseDouble(cashFlow.get("n_cash_flows_fnc_act"))); + } + if (ObjectUtil.isNotNull(finaIndicator)) { + indicator.setCurrentRatio(NumberHelper.parseDouble(finaIndicator.get("current_ratio"))); + indicator.setQuickRatio(NumberHelper.parseDouble(finaIndicator.get("quick_ratio"))); + indicator.setAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("ar_turn"))); + indicator.setDaysAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("arturn_days"))); + indicator.setInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("inv_turn"))); + indicator.setDaysInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("invturn_days"))); + indicator.setFixedAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("fa_turn"))); + indicator.setDaysFixedAssetsTurnover(NumberHelper.safeDiv(360.0, indicator.getFixedAssetsTurnover())); + indicator.setTotalAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("assets_turn"))); + indicator.setDaysTotalAssetsTurnover(NumberHelper.safeDiv(360.0, indicator.getTotalAssetsTurnover())); + indicator.setReturnOnEquity(NumberHelper.parseDouble(finaIndicator.get("roe_dt"))); + indicator.setReturnOnAssets(NumberHelper.parseDouble(finaIndicator.get("roa"))); + indicator.setOperatingGrossProfitMargin(NumberHelper.safeDiv(NumberHelper.safeMinus(indicator.getOperatingRevenue(), indicator.getOperatingCost()), indicator.getOperatingRevenue())); + indicator.setOperatingProfitMargin(NumberHelper.safeDiv(indicator.getOperatingProfit(), indicator.getOperatingRevenue())); + indicator.setOperatingSafetyMarginRatio(NumberHelper.safeDiv(indicator.getOperatingProfitMargin(), indicator.getOperatingGrossProfitMargin())); + indicator.setNetProfitMargin(NumberHelper.parseDouble(finaIndicator.get("roa_dp"))); + indicator.setEarningsPerShare(NumberHelper.parseDouble(finaIndicator.get("total_revenue_ps"))); + } financeIndicatorRepository.save(indicator); } + + setStep((year - 1990) * 100 / (currentYear - 1990)); } } } diff --git a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceNode.java b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceNode.java deleted file mode 100644 index 6706b0e..0000000 --- a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/service/task/UpdateFinanceNode.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.lanyuanxiaoyao.leopard.server.service.task; - -import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet; -import com.lanyuanxiaoyao.leopard.core.entity.CashFlow; -import com.lanyuanxiaoyao.leopard.core.entity.Income; -import com.lanyuanxiaoyao.leopard.core.entity.QBalanceSheet; -import com.lanyuanxiaoyao.leopard.core.entity.QCashFlow; -import com.lanyuanxiaoyao.leopard.core.entity.QIncome; -import com.lanyuanxiaoyao.leopard.core.entity.Stock; -import com.lanyuanxiaoyao.leopard.core.repository.BalanceSheetRepository; -import com.lanyuanxiaoyao.leopard.core.repository.CashFlowRepository; -import com.lanyuanxiaoyao.leopard.core.repository.IncomeRepository; -import com.lanyuanxiaoyao.leopard.core.repository.StockRepository; -import com.lanyuanxiaoyao.leopard.server.helper.NumberHelper; -import com.lanyuanxiaoyao.leopard.server.service.TaskService; -import com.lanyuanxiaoyao.leopard.server.service.TuShareService; -import com.yomahub.liteflow.annotation.LiteflowComponent; -import java.time.LocalDate; -import java.util.List; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; - -/** - * 更新财务数据 - * - * @author lanyuanxiaoyao - * @version 20250911 - */ -@Slf4j -@LiteflowComponent("update_finance") -public class UpdateFinanceNode extends TaskNodeComponent { - private final StockRepository stockRepository; - private final IncomeRepository incomeRepository; - private final BalanceSheetRepository balanceSheetRepository; - private final CashFlowRepository cashFlowRepository; - - private final TuShareService tuShareService; - - public UpdateFinanceNode(TaskService taskService, StockRepository stockRepository, IncomeRepository incomeRepository, BalanceSheetRepository balanceSheetRepository, CashFlowRepository cashFlowRepository, TuShareService tuShareService) { - super(taskService); - this.stockRepository = stockRepository; - this.incomeRepository = incomeRepository; - this.balanceSheetRepository = balanceSheetRepository; - this.cashFlowRepository = cashFlowRepository; - this.tuShareService = tuShareService; - } - - @Override - public void process() { - var stocks = stockRepository.findAll(); - var stocksMap = stocks.stream().collect(Collectors.toMap(Stock::getCode, stock -> stock)); - var currentYear = LocalDate.now().getYear(); - for (int year = 1990; year < currentYear; year++) { - var response = tuShareService.incomeList(year); - for (List item : response.data().items()) { - var code = item.get(0); - if (!stocksMap.containsKey(code)) { - continue; - } - var stock = stocksMap.get(code); - var income = incomeRepository.findOne( - QIncome.income.year.eq(year) - .and(QIncome.income.stock.code.eq(stock.getCode())) - ).orElse(new Income()); - income.setStock(stock); - income.setYear(year); - income.setBasicEarningsPerShare(NumberHelper.parseDouble(item.get(1))); - income.setDilutedEarningsPerShare(NumberHelper.parseDouble(item.get(2))); - income.setTotalOperatingRevenue(NumberHelper.parseDouble(item.get(3))); - income.setOperatingRevenue(NumberHelper.parseDouble(item.get(4))); - income.setTotalOperatingCost(NumberHelper.parseDouble(item.get(5))); - income.setOperatingCost(NumberHelper.parseDouble(item.get(6))); - income.setSellingExpense(NumberHelper.parseDouble(item.get(7))); - income.setAdministrativeExpense(NumberHelper.parseDouble(item.get(8))); - income.setFinancialExpense(NumberHelper.parseDouble(item.get(9))); - income.setOperatingExpense(NumberHelper.parseDouble(item.get(10))); - income.setOperatingProfit(NumberHelper.parseDouble(item.get(11))); - income.setAddNonOperatingIncome(NumberHelper.parseDouble(item.get(12))); - income.setLessNonOperatingExpense(NumberHelper.parseDouble(item.get(13))); - income.setTotalProfit(NumberHelper.parseDouble(item.get(14))); - income.setIncomeTaxExpense(NumberHelper.parseDouble(item.get(15))); - income.setNetProfitIncludingMinorityInterest(NumberHelper.parseDouble(item.get(16))); - income.setNetProfitExcludingMinorityInterest(NumberHelper.parseDouble(item.get(17))); - income.setComprehensiveIncomeAttributableToParent(NumberHelper.parseDouble(item.get(18))); - income.setComprehensiveIncomeAttributableToMinorityShareholders(NumberHelper.parseDouble(item.get(19))); - income.setEarningsBeforeInterestAndTax(NumberHelper.parseDouble(item.get(20))); - income.setBeginningUndistributedProfit(NumberHelper.parseDouble(item.get(21))); - income.setDistributableProfit(NumberHelper.parseDouble(item.get(22))); - income.setResearchAndDevelopmentExpense(NumberHelper.parseDouble(item.get(23))); - income.setFinancialExpenseInterestExpense(NumberHelper.parseDouble(item.get(24))); - income.setNetProfitFromContinuingOperations(NumberHelper.parseDouble(item.get(25))); - income.setNetProfitFromDiscontinuedOperations(NumberHelper.parseDouble(item.get(26))); - incomeRepository.save(income); - } - - response = tuShareService.balanceList(year); - for (List item : response.data().items()) { - var code = item.get(0); - if (!stocksMap.containsKey(code)) { - continue; - } - var stock = stocksMap.get(code); - var balanceSheet = balanceSheetRepository.findOne( - QBalanceSheet.balanceSheet.year.eq(year) - .and(QBalanceSheet.balanceSheet.stock.code.eq(stock.getCode())) - ).orElse(new BalanceSheet()); - balanceSheet.setStock(stock); - balanceSheet.setYear(year); - balanceSheet.setEndingTotalShares(NumberHelper.parseDouble(item.get(1))); - balanceSheet.setCapitalSurplus(NumberHelper.parseDouble(item.get(2))); - balanceSheet.setUndistributedProfit(NumberHelper.parseDouble(item.get(3))); - balanceSheet.setMonetaryFunds(NumberHelper.parseDouble(item.get(4))); - balanceSheet.setAccountsReceivable(NumberHelper.parseDouble(item.get(5))); - balanceSheet.setInventories(NumberHelper.parseDouble(item.get(6))); - balanceSheet.setTotalCurrentAssets(NumberHelper.parseDouble(item.get(7))); - balanceSheet.setLongTermEquityInvestments(NumberHelper.parseDouble(item.get(8))); - balanceSheet.setLongTermReceivables(NumberHelper.parseDouble(item.get(9))); - balanceSheet.setFixedAssets(NumberHelper.parseDouble(item.get(10))); - balanceSheet.setResearchAndDevelopmentExpenditures(NumberHelper.parseDouble(item.get(11))); - balanceSheet.setGoodwill(NumberHelper.parseDouble(item.get(12))); - balanceSheet.setTotalNonCurrentAssets(NumberHelper.parseDouble(item.get(13))); - balanceSheet.setTotalAssets(NumberHelper.parseDouble(item.get(14))); - balanceSheet.setLongTermBorrowings(NumberHelper.parseDouble(item.get(15))); - balanceSheet.setShortTermBorrowings(NumberHelper.parseDouble(item.get(16))); - balanceSheet.setAccountsPayable(NumberHelper.parseDouble(item.get(17))); - balanceSheet.setAdvancesReceived(NumberHelper.parseDouble(item.get(18))); - balanceSheet.setTotalCurrentLiabilities(NumberHelper.parseDouble(item.get(19))); - balanceSheet.setTotalNonCurrentLiabilities(NumberHelper.parseDouble(item.get(20))); - balanceSheet.setTotalLiabilities(NumberHelper.parseDouble(item.get(21))); - balanceSheet.setTotalShareholdersEquityExcludingMinorityInterest(NumberHelper.parseDouble(item.get(22))); - balanceSheet.setTotalShareholdersEquityIncludingMinorityInterest(NumberHelper.parseDouble(item.get(23))); - balanceSheet.setTotalLiabilitiesAndShareholdersEquity(NumberHelper.parseDouble(item.get(24))); - balanceSheet.setAccountsReceivable(NumberHelper.parseDouble(item.get(25))); - balanceSheet.setPayables(NumberHelper.parseDouble(item.get(26))); - balanceSheet.setNotesAndAccountsReceivable(NumberHelper.parseDouble(item.get(27))); - balanceSheet.setNotesAndAccountsPayable(NumberHelper.parseDouble(item.get(28))); - balanceSheet.setOtherReceivablesTotal(NumberHelper.parseDouble(item.get(29))); - balanceSheet.setFixedAssetsTotal(NumberHelper.parseDouble(item.get(30))); - balanceSheetRepository.save(balanceSheet); - } - - response = tuShareService.cashFlowList(year); - for (List item : response.data().items()) { - var code = item.get(0); - if (!stocksMap.containsKey(code)) { - continue; - } - var stock = stocksMap.get(code); - var cashFlow = cashFlowRepository.findOne( - QCashFlow.cashFlow.year.eq(year) - .and(QCashFlow.cashFlow.stock.code.eq(stock.getCode())) - ).orElse(new CashFlow()); - NumberHelper.parseDouble(item.get(1)); - cashFlow.setStock(stock); - cashFlow.setYear(year); - cashFlow.setNetProfit(NumberHelper.parseDouble(item.get(1))); - cashFlow.setFinancialExpense(NumberHelper.parseDouble(item.get(2))); - cashFlow.setCashReceivedFromSalesAndServices(NumberHelper.parseDouble(item.get(3))); - cashFlow.setSubtotalOfCashInflowsFromOperatingActivities(NumberHelper.parseDouble(item.get(4))); - cashFlow.setCashPaidToAndForEmployees(NumberHelper.parseDouble(item.get(5))); - cashFlow.setCashPaidForVariousTaxes(NumberHelper.parseDouble(item.get(6))); - cashFlow.setNetCashFlowFromOperatingActivities(NumberHelper.parseDouble(item.get(7))); - cashFlow.setSubtotalOfCashInflowsFromInvestingActivities(NumberHelper.parseDouble(item.get(8))); - cashFlow.setCashPaidForLongTermAssets(NumberHelper.parseDouble(item.get(9))); - cashFlow.setSubtotalOfCashOutflowsFromInvestingActivities(NumberHelper.parseDouble(item.get(10))); - cashFlow.setSubtotalOfCashOutflowsFromFinancingActivities(NumberHelper.parseDouble(item.get(11))); - cashFlow.setBeginningBalanceOfCashAndCashEquivalents(NumberHelper.parseDouble(item.get(12))); - cashFlowRepository.save(cashFlow); - } - - setStep((year - 1990) * 100 / (currentYear - 1990)); - } - } -} diff --git a/leopard-server/src/test/resources/tushare.http b/leopard-server/src/test/resources/tushare.http index 5e01413..f7be39c 100644 --- a/leopard-server/src/test/resources/tushare.http +++ b/leopard-server/src/test/resources/tushare.http @@ -161,7 +161,7 @@ POST {{api_url}} Content-Type: application/json { - "api_name": "cashflow", + "api_name": "cashflow_vip", "token": "{{api_key}}", "params": { "ts_code": "000002.SZ", @@ -214,6 +214,7 @@ Content-Type: application/json "period": "20191231" }, "fields": [ + "ts_code", "current_ratio", "quick_ratio", "invturn_days", diff --git a/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java b/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java index 989908d..63e4ae3 100644 --- a/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java +++ b/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java @@ -1,17 +1,6 @@ package com.lanyuanxiaoyao.leopard.strategy; -import cn.hutool.core.util.NumberUtil; -import cn.hutool.core.util.ObjectUtil; -import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet; -import com.lanyuanxiaoyao.leopard.core.entity.CashFlow; -import com.lanyuanxiaoyao.leopard.core.entity.Income; -import com.lanyuanxiaoyao.leopard.core.entity.QBalanceSheet; -import com.lanyuanxiaoyao.leopard.core.entity.QCashFlow; -import com.lanyuanxiaoyao.leopard.core.entity.QIncome; -import com.lanyuanxiaoyao.leopard.core.repository.BalanceSheetRepository; -import com.lanyuanxiaoyao.leopard.core.repository.CashFlowRepository; import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository; -import com.lanyuanxiaoyao.leopard.core.repository.IncomeRepository; import com.lanyuanxiaoyao.leopard.core.repository.StockRepository; import jakarta.annotation.Resource; import jakarta.transaction.Transactional; @@ -30,12 +19,6 @@ public class StrategyApplication { private StockRepository stockRepository; @Resource private DailyRepository dailyRepository; - @Resource - private BalanceSheetRepository balanceSheetRepository; - @Resource - private IncomeRepository incomeRepository; - @Resource - private CashFlowRepository cashFlowRepository; public static void main(String[] args) { SpringApplication.run(StrategyApplication.class, args); @@ -44,85 +27,5 @@ public class StrategyApplication { @Transactional(rollbackOn = Throwable.class) @EventListener(ApplicationReadyEvent.class) public void test() { - var code = "600132.SH"; - for (int year = 2019; year <= 2019; year++) { - var balance = balanceSheetRepository.findOne( - QBalanceSheet.balanceSheet.stock.code.eq(code) - .and(QBalanceSheet.balanceSheet.year.eq(year)) - ).orElseThrow(); - var income = incomeRepository.findOne( - QIncome.income.stock.code.eq(code) - .and(QIncome.income.year.eq(year)) - ).orElseThrow(); - var cashflow = cashFlowRepository.findOne( - QCashFlow.cashFlow.stock.code.eq(code) - .and(QCashFlow.cashFlow.year.eq(year)) - ).orElseThrow(); - log.info("{} {}", year, calculateFinanceIndicator(balance, income, cashflow)); - } - } - - private FinanceIndicator calculateFinanceIndicator(BalanceSheet balance, Income income, CashFlow cashflow) { - return new FinanceIndicator( - safeDiv(balance.getTotalLiabilities(), balance.getTotalAssets()), - safeDiv(balance.getTotalAssets(), balance.getTotalNonCurrentAssets()), - safeDiv(balance.getTotalCurrentAssets(), balance.getTotalCurrentLiabilities()), - safeDiv(safeMinus(balance.getTotalCurrentAssets(), balance.getInventories()), balance.getTotalCurrentLiabilities()), - safeDiv(income.getOperatingRevenue(), balance.getNotesAndAccountsReceivable()), - safeDiv(360.0, safeDiv(income.getOperatingRevenue(), balance.getNotesAndAccountsReceivable())), - safeDiv(income.getTotalOperatingCost(), balance.getInventories()), - safeDiv(360.0, safeDiv(income.getTotalOperatingCost(), balance.getInventories())), - safeDiv(income.getOperatingRevenue(), balance.getTotalAssets()), - safeDiv(income.getNetProfitIncludingMinorityInterest(), balance.getTotalShareholdersEquityExcludingMinorityInterest()) - ); - } - - private Double safePlus(Double a, Double b) { - if (ObjectUtil.isNull(a) || ObjectUtil.isNull(b)) { - return null; - } - return a + b; - } - - private Double safeMinus(Double a, Double b) { - if (ObjectUtil.isNull(a) || ObjectUtil.isNull(b)) { - return null; - } - return a - b; - } - - private Double safeDiv(Double a, Double b) { - if (ObjectUtil.isNull(a) || ObjectUtil.isNull(b) || b == 0) { - return null; - } - return NumberUtil.div(a, b, 4); - } - - public record FinanceIndicator( - Double debtToAssetRatio, - Double longTermFundsToRealEstateRatio, - Double currentRatio, - Double quickRatio, - Double accountsReceivableTurnoverRate, - Double averageCashCollectionDays, - Double inventoryTurnoverRate, - Double averageSalesDays, - Double totalAssetTurnover, - Double roe - ) { - @Override - public String toString() { - return "负债占资产比率=" + debtToAssetRatio + - ", 长期资金占固定资产比率=" + longTermFundsToRealEstateRatio + - ", 流动比率=" + currentRatio + - ", 速动比率=" + quickRatio + - ", 应收账款周转率=" + accountsReceivableTurnoverRate + - ", 平均现金回收天数=" + averageCashCollectionDays + - ", 库存周转率=" + inventoryTurnoverRate + - ", 平均销货天数=" + averageSalesDays + - ", 总资产周转率=" + totalAssetTurnover + - ", 股东权益报酬率(ROE)=" + roe + - ';'; - } } } diff --git a/leopard-web/src/pages/stock/StockDetail.tsx b/leopard-web/src/pages/stock/StockDetail.tsx index ad96d36..6447ca9 100644 --- a/leopard-web/src/pages/stock/StockDetail.tsx +++ b/leopard-web/src/pages/stock/StockDetail.tsx @@ -2,10 +2,11 @@ import React from 'react' import {useParams} from 'react-router' import {amisRender, commonInfo, readOnlyDialogOptions, remoteMappings} from '../../util/amis.tsx' import type {Schema} from 'amis' +import {isNil} from 'es-toolkit' // 格式化财务数字显示的公共函数 const formatFinanceNumber = (value: number): string => { - if (value === null || value === undefined) { + if (isNil(value)) { return '-' } @@ -24,7 +25,17 @@ const formatFinanceNumber = (value: number): string => { return isNegative ? `-${formatted}` : formatted } -const financePropertyLabel = (id: string | undefined, label: string, type: string, field: string): Schema => { +// 格式化百分比数字显示的公共函数 +const formatPercentageNumber = (value: number): string => { + if (isNil(value)) { + return '-' + } + return `${(value * 100).toFixed(2)}%` +} + +type FinanceType = 'PERCENTAGE' | 'FINANCE' + +const financePropertyLabel = (id: string | undefined, label: string, type: FinanceType, field: string): Schema => { if (!id) { return { type: 'tpl', @@ -32,6 +43,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin } } let current = new Date().getFullYear() + let formatter: (value: number) => string = type === 'PERCENTAGE' ? formatPercentageNumber : formatFinanceNumber return { type: 'wrapper', size: 'none', @@ -54,7 +66,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin ...readOnlyDialogOptions(), body: { type: 'chart', - api: `get:${commonInfo.baseUrl}/stock/finance/${id}/${type}/${field}`, + api: `get:${commonInfo.baseUrl}/stock/finance/${id}/${field}`, height: 500, config: { tooltip: { @@ -68,7 +80,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin padding: [10, 15], formatter: (params: any) => { const item = params[0] - return `${item.name}
${item.marker}${formatFinanceNumber(item.value)}` + return `${item.name}
${item.marker}${formatter(item.value)}` }, }, grid: { @@ -116,7 +128,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin color: '#999', fontSize: 12, formatter: (value: number) => { - return formatFinanceNumber(value) + return formatter(value) }, }, axisTick: { @@ -163,7 +175,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin fontWeight: 'bold', fontSize: 12, formatter: (params: any) => { - return formatFinanceNumber(params.value) + return formatter(params.value) }, }, }, @@ -212,30 +224,49 @@ function StockDetail() { { className: 'my-2', type: 'property', + column: 4, items: [ { - label: financePropertyLabel(id, '总资产', 'balanceSheet', 'totalAssets'), + label: financePropertyLabel(id, '总资产', 'FINANCE', 'totalAssets'), content: '${balanceSheet.totalAssets}', + span: 4, }, { - label: financePropertyLabel(id, '流动资产', 'balanceSheet', 'totalCurrentAssets'), - content: '${balanceSheet.totalCurrentAssets}', + label: financePropertyLabel(id, '流动资产', 'FINANCE', 'currentAssets'), + content: '${balanceSheet.currentAssets}', }, { - label: financePropertyLabel(id, '非流动资产', 'balanceSheet', 'totalNonCurrentAssets'), - content: '${balanceSheet.totalNonCurrentAssets}', + label: financePropertyLabel(id, '流动资产占比', 'PERCENTAGE', 'currentAssetsToTotalAssetsRatio'), + content: '${balanceSheet.currentAssetsRatio}', }, { - label: financePropertyLabel(id, '总负债', 'balanceSheet', 'totalLiabilities'), + label: financePropertyLabel(id, '非流动资产', 'FINANCE', 'fixedAssets'), + content: '${balanceSheet.fixedAssets}', + }, + { + label: financePropertyLabel(id, '非流动资产占比', 'PERCENTAGE', 'fixedAssetsToTotalAssetsRatio'), + content: '${balanceSheet.fixedAssetsRatio}', + }, + { + label: financePropertyLabel(id, '总负债', 'FINANCE', 'totalLiabilities'), content: '${balanceSheet.totalLiabilities}', + span: 4, }, { - label: financePropertyLabel(id, '流动负债', 'balanceSheet', 'totalCurrentLiabilities'), - content: '${balanceSheet.totalCurrentLiabilities}', + label: financePropertyLabel(id, '流动负债', 'FINANCE', 'currentLiabilities'), + content: '${balanceSheet.currentLiabilities}', }, { - label: financePropertyLabel(id, '非流动负债', 'balanceSheet', 'totalNonCurrentLiabilities'), - content: '${balanceSheet.totalNonCurrentLiabilities}', + label: financePropertyLabel(id, '流动负债占比', 'PERCENTAGE', 'currentLiabilitiesToTotalAssetsRatio'), + content: '${balanceSheet.currentLiabilitiesRatio}', + }, + { + label: financePropertyLabel(id, '非流动负债', 'FINANCE', 'longTermLiabilities'), + content: '${balanceSheet.longTermLiabilities}', + }, + { + label: financePropertyLabel(id, '非流动负债占比', 'PERCENTAGE', 'longTermLiabilitiesToTotalAssetsRatio'), + content: '${balanceSheet.longTermLiabilitiesRatio}', }, ], }, @@ -245,16 +276,16 @@ function StockDetail() { type: 'property', items: [ { - label: financePropertyLabel(id, '营业总收入', 'income', 'totalOperatingRevenue'), - content: '${income.totalOperatingRevenue}', + label: financePropertyLabel(id, '营业收入', 'FINANCE', 'operatingRevenue'), + content: '${income.operatingRevenue}', }, { - label: financePropertyLabel(id, '营业总成本', 'income', 'totalOperatingCost'), - content: '${income.totalOperatingCost}', + label: financePropertyLabel(id, '营业成本', 'FINANCE', 'operatingCost'), + content: '${income.operatingCost}', }, { - label: financePropertyLabel(id, '营业总利润', 'income', 'totalProfit'), - content: '${income.totalProfit}', + label: financePropertyLabel(id, '营业利润', 'FINANCE', 'operatingProfit'), + content: '${income.operatingProfit}', }, ], }, @@ -264,7 +295,7 @@ function StockDetail() { type: 'property', items: [ { - label: financePropertyLabel(id, '净利润', 'cashflow', 'netProfit'), + label: financePropertyLabel(id, '净利润', 'FINANCE', 'netProfit'), content: '${cashFlow.netProfit}', }, ],