perf: 优化财务数据的采集和显示
This commit is contained in:
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -61,18 +61,6 @@ public class Stock extends SimpleEntity {
|
|||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private Set<Daily> dailies;
|
private Set<Daily> dailies;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
|
|
||||||
@ToString.Exclude
|
|
||||||
private Set<Income> incomes;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
|
|
||||||
@ToString.Exclude
|
|
||||||
private Set<BalanceSheet> balanceSheets;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
|
|
||||||
@ToString.Exclude
|
|
||||||
private Set<CashFlow> cashFlows;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
|
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private Set<FinanceIndicator> indicators;
|
private Set<FinanceIndicator> indicators;
|
||||||
|
|||||||
@@ -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<BalanceSheet> {
|
|
||||||
}
|
|
||||||
@@ -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<CashFlow> {
|
|
||||||
}
|
|
||||||
@@ -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<Income> {
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ import com.lanyuanxiaoyao.leopard.server.service.StockService;
|
|||||||
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
|
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
|
||||||
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
|
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -38,38 +37,42 @@ public class StockController extends SimpleControllerSupport<Stock, Void, StockC
|
|||||||
public GlobalResponse<FinanceItem> finance(@PathVariable("id") Long id) {
|
public GlobalResponse<FinanceItem> finance(@PathVariable("id") Long id) {
|
||||||
// 财报默认是上一年的
|
// 财报默认是上一年的
|
||||||
var year = LocalDate.now().minusYears(1).getYear();
|
var year = LocalDate.now().minusYears(1).getYear();
|
||||||
var balanceSheet = stockService.findBalanceSheet(id, year);
|
var financeIndicator = stockService.findFinanceIndicator(id, year);
|
||||||
var income = stockService.findIncome(id, year);
|
|
||||||
var cashFlow = stockService.findCashFlow(id, year);
|
|
||||||
return GlobalResponse.responseSuccess(new FinanceItem(
|
return GlobalResponse.responseSuccess(new FinanceItem(
|
||||||
id,
|
id,
|
||||||
year,
|
year,
|
||||||
balanceSheet
|
financeIndicator
|
||||||
.map(bs -> new BalanceSheetItem(
|
.map(fi -> new BalanceSheetItem(
|
||||||
NumberHelper.formatFinanceDouble(bs.getTotalAssets()),
|
NumberHelper.formatFinanceDouble(fi.getTotalAssets()),
|
||||||
NumberHelper.formatFinanceDouble(bs.getTotalCurrentAssets()),
|
NumberHelper.formatFinanceDouble(fi.getCurrentAssets()),
|
||||||
NumberHelper.formatFinanceDouble(bs.getTotalNonCurrentAssets()),
|
NumberHelper.formatPercentageDouble(fi.getCurrentAssetsToTotalAssetsRatio()),
|
||||||
NumberHelper.formatFinanceDouble(bs.getTotalLiabilities()),
|
NumberHelper.formatFinanceDouble(fi.getFixedAssets()),
|
||||||
NumberHelper.formatFinanceDouble(bs.getTotalCurrentLiabilities()),
|
NumberHelper.formatPercentageDouble(fi.getFixedAssetsToTotalAssetsRatio()),
|
||||||
NumberHelper.formatFinanceDouble(bs.getTotalNonCurrentLiabilities())
|
NumberHelper.formatFinanceDouble(fi.getTotalLiabilities()),
|
||||||
|
NumberHelper.formatFinanceDouble(fi.getCurrentLiabilities()),
|
||||||
|
NumberHelper.formatPercentageDouble(fi.getCurrentLiabilitiesToTotalLiabilitiesRatio()),
|
||||||
|
NumberHelper.formatFinanceDouble(fi.getLongTermLiabilities()),
|
||||||
|
NumberHelper.formatPercentageDouble(fi.getLongTermLiabilitiesToTotalLiabilitiesRatio())
|
||||||
))
|
))
|
||||||
.orElse(new BalanceSheetItem()),
|
.orElse(new BalanceSheetItem()),
|
||||||
income
|
financeIndicator
|
||||||
.map(ic -> new IncomeItem(
|
.map(fi -> new IncomeItem(
|
||||||
NumberHelper.formatFinanceDouble(ic.getTotalOperatingRevenue()),
|
NumberHelper.formatFinanceDouble(fi.getOperatingRevenue()),
|
||||||
NumberHelper.formatFinanceDouble(ic.getTotalOperatingCost()),
|
NumberHelper.formatFinanceDouble(fi.getOperatingCost()),
|
||||||
NumberHelper.formatFinanceDouble(ic.getTotalProfit())
|
NumberHelper.formatFinanceDouble(fi.getOperatingProfit())
|
||||||
))
|
))
|
||||||
.orElse(new IncomeItem()),
|
.orElse(new IncomeItem()),
|
||||||
cashFlow
|
financeIndicator
|
||||||
.map(cf -> new CashFlowItem(
|
.map(fi -> new CashFlowItem(
|
||||||
NumberHelper.formatFinanceDouble(cf.getNetProfit())
|
NumberHelper.formatFinanceDouble(fi.getNetProfit())
|
||||||
))
|
))
|
||||||
.orElse(new CashFlowItem())
|
.orElse(new CashFlowItem())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlobalResponse<Map<String, Object>> convertFinanceChartData(List<?> data, String field) {
|
@GetMapping("finance/{id}/{field}")
|
||||||
|
public GlobalResponse<Map<String, Object>> financeCharts(@PathVariable("id") Long id, @PathVariable("field") String field) {
|
||||||
|
var data = stockService.findFinanceIndicatorRecent(id, 5);
|
||||||
return GlobalResponse.responseDetailData(
|
return GlobalResponse.responseDetailData(
|
||||||
data.stream()
|
data.stream()
|
||||||
.map(item -> BeanUtil.getFieldValue(item, field))
|
.map(item -> BeanUtil.getFieldValue(item, field))
|
||||||
@@ -77,16 +80,6 @@ public class StockController extends SimpleControllerSupport<Stock, Void, StockC
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("finance/{id}/{type}/{field}")
|
|
||||||
public GlobalResponse<Map<String, Object>> 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
|
@Override
|
||||||
protected Function<Void, Stock> saveItemMapper() {
|
protected Function<Void, Stock> saveItemMapper() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
@@ -136,14 +129,22 @@ public class StockController extends SimpleControllerSupport<Stock, Void, StockC
|
|||||||
|
|
||||||
public record BalanceSheetItem(
|
public record BalanceSheetItem(
|
||||||
String totalAssets,
|
String totalAssets,
|
||||||
String totalCurrentAssets,
|
String currentAssets,
|
||||||
String totalNonCurrentAssets,
|
String currentAssetsRatio,
|
||||||
|
String fixedAssets,
|
||||||
|
String fixedAssetsRatio,
|
||||||
String totalLiabilities,
|
String totalLiabilities,
|
||||||
String totalCurrentLiabilities,
|
String currentLiabilities,
|
||||||
String totalNonCurrentLiabilities
|
String currentLiabilitiesRatio,
|
||||||
|
String longTermLiabilities,
|
||||||
|
String longTermLiabilitiesRatio
|
||||||
) {
|
) {
|
||||||
public BalanceSheetItem() {
|
public BalanceSheetItem() {
|
||||||
this(
|
this(
|
||||||
|
NumberHelper.FINANCE_NULL_DOUBLE,
|
||||||
|
NumberHelper.FINANCE_NULL_DOUBLE,
|
||||||
|
NumberHelper.FINANCE_NULL_DOUBLE,
|
||||||
|
NumberHelper.FINANCE_NULL_DOUBLE,
|
||||||
NumberHelper.FINANCE_NULL_DOUBLE,
|
NumberHelper.FINANCE_NULL_DOUBLE,
|
||||||
NumberHelper.FINANCE_NULL_DOUBLE,
|
NumberHelper.FINANCE_NULL_DOUBLE,
|
||||||
NumberHelper.FINANCE_NULL_DOUBLE,
|
NumberHelper.FINANCE_NULL_DOUBLE,
|
||||||
@@ -155,9 +156,9 @@ public class StockController extends SimpleControllerSupport<Stock, Void, StockC
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record IncomeItem(
|
public record IncomeItem(
|
||||||
String totalOperatingRevenue,
|
String operatingRevenue,
|
||||||
String totalOperatingCost,
|
String operatingCost,
|
||||||
String totalProfit
|
String operatingProfit
|
||||||
) {
|
) {
|
||||||
public IncomeItem() {
|
public IncomeItem() {
|
||||||
this(
|
this(
|
||||||
|
|||||||
@@ -16,7 +16,26 @@ public class NumberHelper {
|
|||||||
if (ObjectUtil.isNull(value)) {
|
if (ObjectUtil.isNull(value)) {
|
||||||
return FINANCE_NULL_DOUBLE;
|
return FINANCE_NULL_DOUBLE;
|
||||||
}
|
}
|
||||||
return NumberUtil.decimalFormat("#.##", value);
|
var builder = new StringBuilder();
|
||||||
|
if (value > 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) {
|
public static Double parseDouble(String value) {
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
package com.lanyuanxiaoyao.leopard.server.service;
|
package com.lanyuanxiaoyao.leopard.server.service;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet;
|
import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator;
|
||||||
import com.lanyuanxiaoyao.leopard.core.entity.BalanceSheet_;
|
import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator_;
|
||||||
import com.lanyuanxiaoyao.leopard.core.entity.CashFlow;
|
import com.lanyuanxiaoyao.leopard.core.entity.QFinanceIndicator;
|
||||||
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.Stock;
|
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
||||||
import com.lanyuanxiaoyao.leopard.core.repository.BalanceSheetRepository;
|
import com.lanyuanxiaoyao.leopard.core.repository.FinanceIndicatorRepository;
|
||||||
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.core.repository.StockRepository;
|
||||||
import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport;
|
import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@@ -30,63 +22,28 @@ import org.springframework.stereotype.Service;
|
|||||||
@Service
|
@Service
|
||||||
public class StockService extends SimpleServiceSupport<Stock> {
|
public class StockService extends SimpleServiceSupport<Stock> {
|
||||||
private final StockRepository stockRepository;
|
private final StockRepository stockRepository;
|
||||||
private final BalanceSheetRepository balanceSheetRepository;
|
private final FinanceIndicatorRepository financeIndicatorRepository;
|
||||||
private final IncomeRepository incomeRepository;
|
|
||||||
private final CashFlowRepository cashFlowRepository;
|
|
||||||
|
|
||||||
public StockService(StockRepository repository, BalanceSheetRepository balanceSheetRepository, IncomeRepository incomeRepository, CashFlowRepository cashFlowRepository) {
|
|
||||||
|
public StockService(StockRepository repository, FinanceIndicatorRepository financeIndicatorRepository) {
|
||||||
super(repository);
|
super(repository);
|
||||||
this.stockRepository = repository;
|
this.stockRepository = repository;
|
||||||
this.balanceSheetRepository = balanceSheetRepository;
|
this.financeIndicatorRepository = financeIndicatorRepository;
|
||||||
this.incomeRepository = incomeRepository;
|
|
||||||
this.cashFlowRepository = cashFlowRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<BalanceSheet> findBalanceSheet(Long stockId, Integer year) {
|
public Optional<FinanceIndicator> findFinanceIndicator(Long stockId, Integer year) {
|
||||||
return balanceSheetRepository.findOne(
|
return financeIndicatorRepository.findOne(
|
||||||
QBalanceSheet.balanceSheet.year.eq(year)
|
QFinanceIndicator.financeIndicator.year.eq(year)
|
||||||
.and(QBalanceSheet.balanceSheet.stock.id.eq(stockId))
|
.and(QFinanceIndicator.financeIndicator.stock.id.eq(stockId))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Income> findIncome(Long stockId, Integer year) {
|
public List<FinanceIndicator> findFinanceIndicatorRecent(Long stockId, int years) {
|
||||||
return incomeRepository.findOne(
|
|
||||||
QIncome.income.year.eq(year)
|
|
||||||
.and(QIncome.income.stock.id.eq(stockId))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<CashFlow> findCashFlow(Long stockId, Integer year) {
|
|
||||||
return cashFlowRepository.findOne(
|
|
||||||
QCashFlow.cashFlow.year.eq(year)
|
|
||||||
.and(QCashFlow.cashFlow.stock.id.eq(stockId))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<BalanceSheet> findBalanceSheetRecent(Long stockId, int years) {
|
|
||||||
var current = LocalDate.now();
|
var current = LocalDate.now();
|
||||||
return balanceSheetRepository.findAll(
|
return financeIndicatorRepository.findAll(
|
||||||
QBalanceSheet.balanceSheet.stock.id.eq(stockId)
|
QFinanceIndicator.financeIndicator.stock.id.eq(stockId)
|
||||||
.and(QBalanceSheet.balanceSheet.year.between(current.minusYears(years).getYear(), current.getYear())),
|
.and(QFinanceIndicator.financeIndicator.year.between(current.minusYears(years).getYear(), current.getYear())),
|
||||||
Sort.by(Sort.Direction.ASC, BalanceSheet_.YEAR)
|
Sort.by(Sort.Direction.ASC, FinanceIndicator_.YEAR)
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Income> 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<CashFlow> 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)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,123 +109,6 @@ public class TuShareService {
|
|||||||
return tuShareResponse;
|
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<Map<String, String>> request(String api, Map<String, Object> params, List<String> fields) throws JsonProcessingException {
|
public List<Map<String, String>> request(String api, Map<String, Object> params, List<String> fields) throws JsonProcessingException {
|
||||||
var response = HttpUtil.post(API_URL, buildRequest(api, params, fields));
|
var response = HttpUtil.post(API_URL, buildRequest(api, params, fields));
|
||||||
var tuShareResponse = mapper.readValue(response, TuShareResponse.class);
|
var tuShareResponse = mapper.readValue(response, TuShareResponse.class);
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.lanyuanxiaoyao.leopard.server.service.task;
|
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.FinanceIndicator;
|
||||||
|
import com.lanyuanxiaoyao.leopard.core.entity.QFinanceIndicator;
|
||||||
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
||||||
import com.lanyuanxiaoyao.leopard.core.repository.FinanceIndicatorRepository;
|
import com.lanyuanxiaoyao.leopard.core.repository.FinanceIndicatorRepository;
|
||||||
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
|
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
|
||||||
@@ -15,7 +18,7 @@ import java.util.stream.Collectors;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@LiteflowComponent("update_finance_indicators")
|
@LiteflowComponent("update_finance")
|
||||||
public class UpdateFinanceIndicatorNode extends TaskNodeComponent {
|
public class UpdateFinanceIndicatorNode extends TaskNodeComponent {
|
||||||
private final FinanceIndicatorRepository financeIndicatorRepository;
|
private final FinanceIndicatorRepository financeIndicatorRepository;
|
||||||
private final StockRepository stockRepository;
|
private final StockRepository stockRepository;
|
||||||
@@ -57,7 +60,11 @@ public class UpdateFinanceIndicatorNode extends TaskNodeComponent {
|
|||||||
"total_assets"
|
"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(
|
var incomes = tuShareService.request(
|
||||||
"income_vip",
|
"income_vip",
|
||||||
Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)),
|
Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)),
|
||||||
@@ -70,21 +77,31 @@ public class UpdateFinanceIndicatorNode extends TaskNodeComponent {
|
|||||||
"n_income"
|
"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(
|
var cashFlows = tuShareService.request(
|
||||||
"cashflow_vip",
|
"cashflow_vip",
|
||||||
Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)),
|
Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)),
|
||||||
List.of(
|
List.of(
|
||||||
|
"ts_code",
|
||||||
"n_cashflow_act",
|
"n_cashflow_act",
|
||||||
"n_cashflow_inv_act",
|
"n_cashflow_inv_act",
|
||||||
"n_cash_flows_fnc_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(
|
var finaIndicators = tuShareService.request(
|
||||||
"fina_indicator_vip",
|
"fina_indicator_vip",
|
||||||
Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)),
|
Map.of("period", LocalDate.of(year, 12, 31).format(TuShareService.TRADE_FORMAT)),
|
||||||
List.of(
|
List.of(
|
||||||
|
"ts_code",
|
||||||
"ca_to_assets",
|
"ca_to_assets",
|
||||||
"nca_to_assets",
|
"nca_to_assets",
|
||||||
"currentdebt_to_debt",
|
"currentdebt_to_debt",
|
||||||
@@ -103,72 +120,92 @@ public class UpdateFinanceIndicatorNode extends TaskNodeComponent {
|
|||||||
"total_revenue_ps"
|
"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) {
|
for (Stock stock : stocks) {
|
||||||
var balance = balancesMap.get(stock.getCode());
|
var balance = balancesMap.get(stock.getCode());
|
||||||
var income = incomesMap.get(stock.getCode());
|
var income = incomesMap.get(stock.getCode());
|
||||||
var cashFlow = cashFlowsMap.get(stock.getCode());
|
var cashFlow = cashFlowsMap.get(stock.getCode());
|
||||||
var finaIndicator = finaIndicatorsMap.get(stock.getCode());
|
var finaIndicator = finaIndicatorsMap.get(stock.getCode());
|
||||||
var indicator = new FinanceIndicator();
|
if (ArrayUtil.<Object>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.setStock(stock);
|
||||||
indicator.setYear(year);
|
indicator.setYear(year);
|
||||||
indicator.setTotalAssets(NumberHelper.parseDouble(balance.get("total_assets")));
|
if (ObjectUtil.isNotNull(balance)) {
|
||||||
indicator.setTotalShareCapital(NumberHelper.parseDouble(balance.get("total_share")));
|
indicator.setTotalAssets(NumberHelper.parseDouble(balance.get("total_assets")));
|
||||||
indicator.setCapitalSurplus(NumberHelper.parseDouble(balance.get("cap_rese")));
|
indicator.setTotalShareCapital(NumberHelper.parseDouble(balance.get("total_share")));
|
||||||
indicator.setSurplusReserve(NumberHelper.parseDouble(balance.get("surplus_rese")));
|
indicator.setCapitalSurplus(NumberHelper.parseDouble(balance.get("cap_rese")));
|
||||||
indicator.setUndistributedProfit(NumberHelper.parseDouble(balance.get("undistr_porfit")));
|
indicator.setSurplusReserve(NumberHelper.parseDouble(balance.get("surplus_rese")));
|
||||||
indicator.setCashAndCashEquivalents(NumberHelper.parseDouble(balance.get("cash_reser_cb")));
|
indicator.setUndistributedProfit(NumberHelper.parseDouble(balance.get("undistr_porfit")));
|
||||||
indicator.setCashAndCashEquivalentsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCashAndCashEquivalents(), indicator.getTotalAssets()));
|
indicator.setCashAndCashEquivalents(NumberHelper.parseDouble(balance.get("cash_reser_cb")));
|
||||||
indicator.setAccountsReceivable(NumberHelper.parseDouble(balance.get("accounts_receiv_bill")));
|
indicator.setCashAndCashEquivalentsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCashAndCashEquivalents(), indicator.getTotalAssets()));
|
||||||
indicator.setAccountsReceivableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsReceivable(), indicator.getTotalAssets()));
|
indicator.setAccountsReceivable(NumberHelper.parseDouble(balance.get("accounts_receiv_bill")));
|
||||||
indicator.setAccountsPayable(NumberHelper.parseDouble(balance.get("accounts_pay")));
|
indicator.setAccountsReceivableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsReceivable(), indicator.getTotalAssets()));
|
||||||
indicator.setAccountsPayableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsPayable(), indicator.getTotalAssets()));
|
indicator.setAccountsPayable(NumberHelper.parseDouble(balance.get("accounts_pay")));
|
||||||
indicator.setInventory(NumberHelper.parseDouble(balance.get("inventories")));
|
indicator.setAccountsPayableToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getAccountsPayable(), indicator.getTotalAssets()));
|
||||||
indicator.setInventoryToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getInventory(), indicator.getTotalAssets()));
|
indicator.setInventory(NumberHelper.parseDouble(balance.get("inventories")));
|
||||||
indicator.setGoodwill(NumberHelper.parseDouble(balance.get("goodwill")));
|
indicator.setInventoryToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getInventory(), indicator.getTotalAssets()));
|
||||||
indicator.setGoodwillToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getGoodwill(), indicator.getTotalAssets()));
|
indicator.setGoodwill(NumberHelper.parseDouble(balance.get("goodwill")));
|
||||||
indicator.setCurrentAssets(NumberHelper.parseDouble(balance.get("total_cur_assets")));
|
indicator.setGoodwillToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getGoodwill(), indicator.getTotalAssets()));
|
||||||
indicator.setCurrentAssetsToTotalAssetsRatio(NumberHelper.parseDouble(finaIndicator.get("ca_to_assets")));
|
indicator.setCurrentAssets(NumberHelper.parseDouble(balance.get("total_cur_assets")));
|
||||||
indicator.setFixedAssets(NumberHelper.parseDouble(balance.get("total_nca")));
|
indicator.setCurrentAssetsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCurrentAssets(), indicator.getTotalAssets()));
|
||||||
indicator.setFixedAssetsToTotalAssetsRatio(NumberHelper.parseDouble(finaIndicator.get("nca_to_assets")));
|
indicator.setFixedAssets(NumberHelper.parseDouble(balance.get("total_nca")));
|
||||||
indicator.setTotalLiabilities(NumberHelper.parseDouble(balance.get("total_liab")));
|
indicator.setFixedAssetsToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getFixedAssets(), indicator.getTotalAssets()));
|
||||||
indicator.setCurrentLiabilities(NumberHelper.parseDouble(balance.get("total_cur_liab")));
|
indicator.setTotalLiabilities(NumberHelper.parseDouble(balance.get("total_liab")));
|
||||||
indicator.setCurrentLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCurrentLiabilities(), indicator.getTotalAssets()));
|
indicator.setCurrentLiabilities(NumberHelper.parseDouble(balance.get("total_cur_liab")));
|
||||||
indicator.setCurrentLiabilitiesToTotalLiabilitiesRatio(NumberHelper.parseDouble(finaIndicator.get("currentdebt_to_debt")));
|
indicator.setCurrentLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getCurrentLiabilities(), indicator.getTotalAssets()));
|
||||||
indicator.setLongTermLiabilities(NumberHelper.parseDouble(balance.get("total_ncl")));
|
indicator.setCurrentLiabilitiesToTotalLiabilitiesRatio(NumberHelper.safeDiv(indicator.getCurrentLiabilities(), indicator.getTotalLiabilities()));
|
||||||
indicator.setLongTermLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getLongTermLiabilities(), indicator.getTotalAssets()));
|
indicator.setLongTermLiabilities(NumberHelper.parseDouble(balance.get("total_ncl")));
|
||||||
indicator.setLongTermLiabilitiesToTotalLiabilitiesRatio(NumberHelper.parseDouble(finaIndicator.get("longdeb_to_debt")));
|
indicator.setLongTermLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getLongTermLiabilities(), indicator.getTotalAssets()));
|
||||||
indicator.setLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getTotalLiabilities(), indicator.getTotalAssets()));
|
indicator.setLongTermLiabilitiesToTotalLiabilitiesRatio(NumberHelper.safeDiv(indicator.getLongTermLiabilities(), indicator.getTotalLiabilities()));
|
||||||
indicator.setShareholdersEquity(NumberHelper.parseDouble(balance.get("total_hldr_eqy_inc_min_int")));
|
indicator.setLiabilitiesToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getTotalLiabilities(), indicator.getTotalAssets()));
|
||||||
indicator.setShareholdersEquityToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getShareholdersEquity(), indicator.getTotalAssets()));
|
indicator.setShareholdersEquity(NumberHelper.parseDouble(balance.get("total_hldr_eqy_inc_min_int")));
|
||||||
indicator.setOperatingRevenue(NumberHelper.parseDouble(income.get("total_revenue")));
|
indicator.setShareholdersEquityToTotalAssetsRatio(NumberHelper.safeDiv(indicator.getShareholdersEquity(), indicator.getTotalAssets()));
|
||||||
indicator.setOperatingCost(NumberHelper.parseDouble(income.get("total_cogs")));
|
}
|
||||||
indicator.setOperatingProfit(NumberHelper.parseDouble(income.get("operate_profit")));
|
if (ObjectUtil.isNotNull(income)) {
|
||||||
indicator.setOperatingExpenses(NumberHelper.parseDouble(income.get("oper_exp")));
|
indicator.setOperatingRevenue(NumberHelper.parseDouble(income.get("total_revenue")));
|
||||||
indicator.setNetProfit(NumberHelper.parseDouble(income.get("n_income")));
|
indicator.setOperatingCost(NumberHelper.parseDouble(income.get("total_cogs")));
|
||||||
indicator.setCashFlowFromOperatingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_act")));
|
indicator.setOperatingProfit(NumberHelper.parseDouble(income.get("operate_profit")));
|
||||||
indicator.setCashFlowFromInvestingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_inv_act")));
|
indicator.setOperatingExpenses(NumberHelper.parseDouble(income.get("oper_exp")));
|
||||||
indicator.setCashFlowFromFinancingActivities(NumberHelper.parseDouble(cashFlow.get("n_cash_flows_fnc_act")));
|
indicator.setNetProfit(NumberHelper.parseDouble(income.get("n_income")));
|
||||||
indicator.setCurrentRatio(NumberHelper.parseDouble(finaIndicator.get("current_ratio")));
|
}
|
||||||
indicator.setQuickRatio(NumberHelper.parseDouble(finaIndicator.get("quick_ratio")));
|
if (ObjectUtil.isNotNull(cashFlow)) {
|
||||||
indicator.setAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("ar_turn")));
|
indicator.setCashFlowFromOperatingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_act")));
|
||||||
indicator.setDaysAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("arturn_days")));
|
indicator.setCashFlowFromInvestingActivities(NumberHelper.parseDouble(cashFlow.get("n_cashflow_inv_act")));
|
||||||
indicator.setInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("inv_turn")));
|
indicator.setCashFlowFromFinancingActivities(NumberHelper.parseDouble(cashFlow.get("n_cash_flows_fnc_act")));
|
||||||
indicator.setDaysInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("invturn_days")));
|
}
|
||||||
indicator.setFixedAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("fa_turn")));
|
if (ObjectUtil.isNotNull(finaIndicator)) {
|
||||||
indicator.setDaysFixedAssetsTurnover(NumberHelper.safeDiv(360.0, indicator.getFixedAssetsTurnover()));
|
indicator.setCurrentRatio(NumberHelper.parseDouble(finaIndicator.get("current_ratio")));
|
||||||
indicator.setTotalAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("assets_turn")));
|
indicator.setQuickRatio(NumberHelper.parseDouble(finaIndicator.get("quick_ratio")));
|
||||||
indicator.setDaysTotalAssetsTurnover(NumberHelper.safeDiv(360.0, indicator.getTotalAssetsTurnover()));
|
indicator.setAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("ar_turn")));
|
||||||
indicator.setReturnOnEquity(NumberHelper.parseDouble(finaIndicator.get("roe_dt")));
|
indicator.setDaysAccountsReceivableTurnover(NumberHelper.parseDouble(finaIndicator.get("arturn_days")));
|
||||||
indicator.setReturnOnAssets(NumberHelper.parseDouble(finaIndicator.get("roa")));
|
indicator.setInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("inv_turn")));
|
||||||
indicator.setOperatingGrossProfitMargin(NumberHelper.safeDiv(NumberHelper.safeMinus(indicator.getOperatingRevenue(), indicator.getOperatingCost()), indicator.getOperatingRevenue()));
|
indicator.setDaysInventoryTurnover(NumberHelper.parseDouble(finaIndicator.get("invturn_days")));
|
||||||
indicator.setOperatingProfitMargin(NumberHelper.safeDiv(indicator.getOperatingProfit(), indicator.getOperatingRevenue()));
|
indicator.setFixedAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("fa_turn")));
|
||||||
indicator.setOperatingSafetyMarginRatio(NumberHelper.safeDiv(indicator.getOperatingProfitMargin(), indicator.getOperatingGrossProfitMargin()));
|
indicator.setDaysFixedAssetsTurnover(NumberHelper.safeDiv(360.0, indicator.getFixedAssetsTurnover()));
|
||||||
indicator.setNetProfitMargin(NumberHelper.parseDouble(finaIndicator.get("roa_dp")));
|
indicator.setTotalAssetsTurnover(NumberHelper.parseDouble(finaIndicator.get("assets_turn")));
|
||||||
indicator.setEarningsPerShare(NumberHelper.parseDouble(finaIndicator.get("total_revenue_ps")));
|
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);
|
financeIndicatorRepository.save(indicator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setStep((year - 1990) * 100 / (currentYear - 1990));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<String> 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<String> 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<String> 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -161,7 +161,7 @@ POST {{api_url}}
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"api_name": "cashflow",
|
"api_name": "cashflow_vip",
|
||||||
"token": "{{api_key}}",
|
"token": "{{api_key}}",
|
||||||
"params": {
|
"params": {
|
||||||
"ts_code": "000002.SZ",
|
"ts_code": "000002.SZ",
|
||||||
@@ -214,6 +214,7 @@ Content-Type: application/json
|
|||||||
"period": "20191231"
|
"period": "20191231"
|
||||||
},
|
},
|
||||||
"fields": [
|
"fields": [
|
||||||
|
"ts_code",
|
||||||
"current_ratio",
|
"current_ratio",
|
||||||
"quick_ratio",
|
"quick_ratio",
|
||||||
"invturn_days",
|
"invturn_days",
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
package com.lanyuanxiaoyao.leopard.strategy;
|
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.DailyRepository;
|
||||||
import com.lanyuanxiaoyao.leopard.core.repository.IncomeRepository;
|
|
||||||
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
|
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
@@ -30,12 +19,6 @@ public class StrategyApplication {
|
|||||||
private StockRepository stockRepository;
|
private StockRepository stockRepository;
|
||||||
@Resource
|
@Resource
|
||||||
private DailyRepository dailyRepository;
|
private DailyRepository dailyRepository;
|
||||||
@Resource
|
|
||||||
private BalanceSheetRepository balanceSheetRepository;
|
|
||||||
@Resource
|
|
||||||
private IncomeRepository incomeRepository;
|
|
||||||
@Resource
|
|
||||||
private CashFlowRepository cashFlowRepository;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(StrategyApplication.class, args);
|
SpringApplication.run(StrategyApplication.class, args);
|
||||||
@@ -44,85 +27,5 @@ public class StrategyApplication {
|
|||||||
@Transactional(rollbackOn = Throwable.class)
|
@Transactional(rollbackOn = Throwable.class)
|
||||||
@EventListener(ApplicationReadyEvent.class)
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
public void test() {
|
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 +
|
|
||||||
';';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import React from 'react'
|
|||||||
import {useParams} from 'react-router'
|
import {useParams} from 'react-router'
|
||||||
import {amisRender, commonInfo, readOnlyDialogOptions, remoteMappings} from '../../util/amis.tsx'
|
import {amisRender, commonInfo, readOnlyDialogOptions, remoteMappings} from '../../util/amis.tsx'
|
||||||
import type {Schema} from 'amis'
|
import type {Schema} from 'amis'
|
||||||
|
import {isNil} from 'es-toolkit'
|
||||||
|
|
||||||
// 格式化财务数字显示的公共函数
|
// 格式化财务数字显示的公共函数
|
||||||
const formatFinanceNumber = (value: number): string => {
|
const formatFinanceNumber = (value: number): string => {
|
||||||
if (value === null || value === undefined) {
|
if (isNil(value)) {
|
||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +25,17 @@ const formatFinanceNumber = (value: number): string => {
|
|||||||
return isNegative ? `-${formatted}` : formatted
|
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) {
|
if (!id) {
|
||||||
return {
|
return {
|
||||||
type: 'tpl',
|
type: 'tpl',
|
||||||
@@ -32,6 +43,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let current = new Date().getFullYear()
|
let current = new Date().getFullYear()
|
||||||
|
let formatter: (value: number) => string = type === 'PERCENTAGE' ? formatPercentageNumber : formatFinanceNumber
|
||||||
return {
|
return {
|
||||||
type: 'wrapper',
|
type: 'wrapper',
|
||||||
size: 'none',
|
size: 'none',
|
||||||
@@ -54,7 +66,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin
|
|||||||
...readOnlyDialogOptions(),
|
...readOnlyDialogOptions(),
|
||||||
body: {
|
body: {
|
||||||
type: 'chart',
|
type: 'chart',
|
||||||
api: `get:${commonInfo.baseUrl}/stock/finance/${id}/${type}/${field}`,
|
api: `get:${commonInfo.baseUrl}/stock/finance/${id}/${field}`,
|
||||||
height: 500,
|
height: 500,
|
||||||
config: {
|
config: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -68,7 +80,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin
|
|||||||
padding: [10, 15],
|
padding: [10, 15],
|
||||||
formatter: (params: any) => {
|
formatter: (params: any) => {
|
||||||
const item = params[0]
|
const item = params[0]
|
||||||
return `${item.name}<br/>${item.marker}${formatFinanceNumber(item.value)}`
|
return `${item.name}<br/>${item.marker}${formatter(item.value)}`
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
@@ -116,7 +128,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin
|
|||||||
color: '#999',
|
color: '#999',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
formatter: (value: number) => {
|
formatter: (value: number) => {
|
||||||
return formatFinanceNumber(value)
|
return formatter(value)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisTick: {
|
axisTick: {
|
||||||
@@ -163,7 +175,7 @@ const financePropertyLabel = (id: string | undefined, label: string, type: strin
|
|||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
formatter: (params: any) => {
|
formatter: (params: any) => {
|
||||||
return formatFinanceNumber(params.value)
|
return formatter(params.value)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -212,30 +224,49 @@ function StockDetail() {
|
|||||||
{
|
{
|
||||||
className: 'my-2',
|
className: 'my-2',
|
||||||
type: 'property',
|
type: 'property',
|
||||||
|
column: 4,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '总资产', 'balanceSheet', 'totalAssets'),
|
label: financePropertyLabel(id, '总资产', 'FINANCE', 'totalAssets'),
|
||||||
content: '${balanceSheet.totalAssets}',
|
content: '${balanceSheet.totalAssets}',
|
||||||
|
span: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '流动资产', 'balanceSheet', 'totalCurrentAssets'),
|
label: financePropertyLabel(id, '流动资产', 'FINANCE', 'currentAssets'),
|
||||||
content: '${balanceSheet.totalCurrentAssets}',
|
content: '${balanceSheet.currentAssets}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '非流动资产', 'balanceSheet', 'totalNonCurrentAssets'),
|
label: financePropertyLabel(id, '流动资产占比', 'PERCENTAGE', 'currentAssetsToTotalAssetsRatio'),
|
||||||
content: '${balanceSheet.totalNonCurrentAssets}',
|
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}',
|
content: '${balanceSheet.totalLiabilities}',
|
||||||
|
span: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '流动负债', 'balanceSheet', 'totalCurrentLiabilities'),
|
label: financePropertyLabel(id, '流动负债', 'FINANCE', 'currentLiabilities'),
|
||||||
content: '${balanceSheet.totalCurrentLiabilities}',
|
content: '${balanceSheet.currentLiabilities}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '非流动负债', 'balanceSheet', 'totalNonCurrentLiabilities'),
|
label: financePropertyLabel(id, '流动负债占比', 'PERCENTAGE', 'currentLiabilitiesToTotalAssetsRatio'),
|
||||||
content: '${balanceSheet.totalNonCurrentLiabilities}',
|
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',
|
type: 'property',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '营业总收入', 'income', 'totalOperatingRevenue'),
|
label: financePropertyLabel(id, '营业收入', 'FINANCE', 'operatingRevenue'),
|
||||||
content: '${income.totalOperatingRevenue}',
|
content: '${income.operatingRevenue}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '营业总成本', 'income', 'totalOperatingCost'),
|
label: financePropertyLabel(id, '营业成本', 'FINANCE', 'operatingCost'),
|
||||||
content: '${income.totalOperatingCost}',
|
content: '${income.operatingCost}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '营业总利润', 'income', 'totalProfit'),
|
label: financePropertyLabel(id, '营业利润', 'FINANCE', 'operatingProfit'),
|
||||||
content: '${income.totalProfit}',
|
content: '${income.operatingProfit}',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -264,7 +295,7 @@ function StockDetail() {
|
|||||||
type: 'property',
|
type: 'property',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: financePropertyLabel(id, '净利润', 'cashflow', 'netProfit'),
|
label: financePropertyLabel(id, '净利润', 'FINANCE', 'netProfit'),
|
||||||
content: '${cashFlow.netProfit}',
|
content: '${cashFlow.netProfit}',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user