Skip to content

Commit 2b65c9a

Browse files
authored
代码提交 (#9)
* 新增akshare请求单次报错退避重试逻辑,修复日志打印与日期不符问题 * 新增统一副图技术指标组件,统一样式 * 新增行业涨幅排名热力图 * 新增行业涨幅排名详情页,优化页面样式 * 补充测试文件提交 * 删除多余测试文件 * 优化行业详情页样式,新增成分股-股票详情弹窗 * 新增自选-股票详情-分时图,优化部分样式 * 新增实时成交,买卖队列展示 * 调整股票详情弹窗样式 * 优化股票详情样式,优化股票同步报错重试逻辑 * 新增同步东财行业数据(需配置文件里手动配置cookie) * 补提交配置文件 * 优化东方财富行业涨幅查性能 * 新增东财cookie配置,我的—东财Cookie。 优化行业板块样式
1 parent 2f38e86 commit 2b65c9a

36 files changed

Lines changed: 3344 additions & 418 deletions

‎.gitignore‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,14 @@ build/
3131

3232
### VS Code ###
3333
.vscode/
34-
.trae/
34+
.trae/
35+
/.opencode/
36+
/.claude/
37+
/.workbuddy/
38+
/index.js
39+
/gridlist.html
40+
41+
### 本地环境配置(真实凭据,永不提交)###
42+
aquant-backend/src/main/resources/application-dev.yaml
43+
aquant-backend/src/main/resources/application-dev.yml
44+
aquant-backend/src/main/resources/application-local.yaml

‎aquant-backend/pom.xml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@
6969
<version>4.11.0</version>
7070
</dependency>
7171

72+
<!-- 模拟 Chrome TLS/JA3/JA4 指纹(BCJSSE),规避东财 WAF 对 JDK 默认 TLS 指纹的拦截 -->
73+
<dependency>
74+
<groupId>com.github.zhkl0228</groupId>
75+
<artifactId>impersonator-bctls</artifactId>
76+
<version>1.5.2</version>
77+
</dependency>
78+
7279
<!-- 基金公司官网当前额度表 HTML 解析 -->
7380
<dependency>
7481
<groupId>org.jsoup</groupId>

‎aquant-backend/src/main/java/com/brotherc/aquant/common/exception/ExceptionEnum.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public enum ExceptionEnum {
1010
SYS_ERROR(1000001, "系统异常"),
1111
SYS_CHECK_ERROR(1000002, "系统校验异常"),
1212
API_REQUEST_ERROR(1000003, "外部API访问异常"),
13+
API_DATA_NOT_FOUND(1000004, "外部API数据不存在"),
1314

1415
STOCK_STRATEGY_DUAL_MA_ILLEGAL(1000101, "短期均线必须小于长期均线"),
1516
STOCK_STRATEGY_MACD_PARAMS_ILLEGAL(1000111, "MACD参数非法,快线周期必须小于慢线周期且各周期必须大于0"),
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.brotherc.aquant.common.exception;
2+
3+
/**
4+
* 外部接口确认暂无该标的数据(如上市首日新股尚无日线),重试无法恢复,
5+
* 调用方应跳过重试,等待下次同步触发时按水位自动补齐。
6+
*/
7+
public class ExternalDataNotFoundException extends BusinessException {
8+
9+
public ExternalDataNotFoundException() {
10+
super(ExceptionEnum.API_DATA_NOT_FOUND);
11+
}
12+
}

‎aquant-backend/src/main/java/com/brotherc/aquant/industry/controller/IndustrySourceController.java‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
import com.brotherc.aquant.industry.service.StockIndustryBoardEmQueryService;
1515
import com.brotherc.aquant.industry.service.StockIndustryBoardEmSyncService;
1616
import com.brotherc.aquant.industry.service.StockIndustryBoardHistoryService;
17+
import com.brotherc.aquant.industry.service.StockIndustryBoardService;
1718
import com.brotherc.aquant.sync.entity.StockSync;
1819
import com.brotherc.aquant.sync.repository.StockSyncRepository;
1920
import lombok.RequiredArgsConstructor;
21+
import org.springdoc.core.annotations.ParameterObject;
2022
import org.springframework.beans.BeanUtils;
23+
import org.springframework.data.domain.Page;
24+
import org.springframework.data.domain.Pageable;
2125
import org.springframework.format.annotation.DateTimeFormat;
2226
import org.springframework.web.bind.annotation.GetMapping;
2327
import org.springframework.web.bind.annotation.RequestMapping;
@@ -36,6 +40,7 @@ public class IndustrySourceController {
3640
private final StockIndustryBoardAnalysisService analysisService;
3741
private final StockIndustryBoardEmQueryService emQueryService;
3842
private final StockIndustryBoardHistoryService historyService;
43+
private final StockIndustryBoardService boardService;
3944
private final StockBoardConstituentService constituentService;
4045
private final StockBoardConstituentEmService emConstituentService;
4146
private final StockIndustryBoardRepository boardRepository;
@@ -64,6 +69,19 @@ public ResponseDTO<IndustrySourceSnapshotVO<StockIndustryBoardVO>> overview(
6469
? thsOverview(industry, tradeDate) : emQueryService.overview(industry, tradeDate)));
6570
}
6671

72+
@GetMapping("/board/page")
73+
public ResponseDTO<IndustrySourceSnapshotVO<Page<StockIndustryBoardVO>>> boardPage(
74+
@RequestParam(defaultValue = "THS") IndustryDataSource source,
75+
@RequestParam(required = false) String boardName,
76+
@ParameterObject Pageable pageable
77+
) {
78+
StockIndustryBoardPageReqVO reqVO = new StockIndustryBoardPageReqVO(Boolean.FALSE);
79+
reqVO.setBoardName(boardName);
80+
return ResponseDTO.success(resolve(source, current -> current == IndustryDataSource.THS
81+
? boardService.stockIndustryBoardPage(reqVO, pageable)
82+
: emQueryService.boardPage(boardName, pageable)));
83+
}
84+
6785
@GetMapping("/history/kline")
6886
public ResponseDTO<IndustrySourceSnapshotVO<List<StockIndustryBoardKVO>>> history(
6987
@RequestParam(defaultValue = "THS") IndustryDataSource source,
@@ -113,7 +131,10 @@ private <T> IndustrySourceSnapshotVO<T> resolve(IndustryDataSource requested, ja
113131
}
114132

115133
private boolean isUnavailable(Object content) {
116-
return content == null || (content instanceof List<?> list && list.isEmpty());
134+
if (content == null) return true;
135+
if (content instanceof List<?> list) return list.isEmpty();
136+
if (content instanceof Page<?> page) return page.isEmpty();
137+
return false;
117138
}
118139

119140
private StockIndustryBoardVO thsOverview(String industry, LocalDate tradeDate) {

‎aquant-backend/src/main/java/com/brotherc/aquant/industry/controller/StockIndustryBoardController.java‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.brotherc.aquant.industry.model.vo.StockIndustryBoardVO;
88
import com.brotherc.aquant.industry.model.vo.IndustryRiseAnalysisVO;
99
import com.brotherc.aquant.industry.model.vo.StockIndustryConstituentSnapshotVO;
10+
import com.brotherc.aquant.industry.model.vo.StockBoardTrendsVO;
11+
import com.brotherc.aquant.industry.service.StockBoardMinuteService;
1012
import com.brotherc.aquant.industry.service.StockIndustryBoardAnalysisService;
1113
import com.brotherc.aquant.industry.service.StockBoardConstituentService;
1214
import com.brotherc.aquant.industry.service.StockIndustryBoardHistoryService;
@@ -40,6 +42,7 @@ public class StockIndustryBoardController {
4042
private final StockIndustryBoardHistoryService stockIndustryBoardHistoryService;
4143
private final StockIndustryBoardAnalysisService stockIndustryBoardAnalysisService;
4244
private final StockBoardConstituentService stockBoardConstituentService;
45+
private final StockBoardMinuteService stockBoardMinuteService;
4346

4447
@Operation(summary = "分页查询板块数据")
4548
@GetMapping("/page")
@@ -76,4 +79,18 @@ public ResponseDTO<StockIndustryConstituentSnapshotVO> constituents(
7679
: ResponseDTO.fail(ExceptionEnum.API_REQUEST_ERROR.getCode(), snapshot.getMessage(), snapshot);
7780
}
7881

82+
@Operation(summary = "获取板块当日分时走势(实时,10秒缓存)")
83+
@GetMapping("/minute/intraday")
84+
public ResponseDTO<StockBoardTrendsVO> intradayTrends(
85+
@Parameter(description = "板块代码(行业名称)") @RequestParam @NotBlank String boardCode) {
86+
return ResponseDTO.success(stockBoardMinuteService.getIntradayTrends(boardCode));
87+
}
88+
89+
@Operation(summary = "获取板块近5日分时走势")
90+
@GetMapping("/minute/trends5d")
91+
public ResponseDTO<StockBoardTrendsVO> trends5d(
92+
@Parameter(description = "板块代码(行业名称)") @RequestParam @NotBlank String boardCode) {
93+
return ResponseDTO.success(stockBoardMinuteService.getTrends5d(boardCode));
94+
}
95+
7996
}

‎aquant-backend/src/main/java/com/brotherc/aquant/industry/entity/StockIndustryBoardHistoryEm.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
*/
1212
@Data
1313
@Entity
14-
@Table(name = "stock_industry_board_history_em")
14+
@Table(name = "stock_industry_board_history_em", indexes = {
15+
@Index(name = "uk_industry_history_em_sector_date", columnList = "sector_name, trade_date", unique = true),
16+
@Index(name = "idx_industry_history_em_trade_date", columnList = "trade_date")
17+
})
1518
public class StockIndustryBoardHistoryEm {
1619

1720
/**
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.brotherc.aquant.industry.model.vo;
2+
3+
import lombok.Data;
4+
5+
import java.math.BigDecimal;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
/**
10+
* 板块分时走势(当日/五日共用,五日时 trends 按交易日分段)
11+
*/
12+
@Data
13+
public class StockBoardTrendsVO {
14+
15+
/**
16+
* 板块代码(库内 sectorName,如"院线")
17+
*/
18+
private String boardCode;
19+
20+
/**
21+
* 板块名称
22+
*/
23+
private String name;
24+
25+
/**
26+
* 昨收
27+
*/
28+
private BigDecimal prevClose;
29+
30+
/**
31+
* 最新价
32+
*/
33+
private BigDecimal latestPrice;
34+
35+
/**
36+
* 分时点(时间升序,HH:mm 或五日 yyyy-MM-dd HH:mm)
37+
*/
38+
private List<Point> points = new ArrayList<>();
39+
40+
@Data
41+
public static class Point {
42+
private String time;
43+
private BigDecimal price;
44+
private BigDecimal avgPrice;
45+
/** 单分钟成交量(手) */
46+
private BigDecimal volume;
47+
}
48+
}

‎aquant-backend/src/main/java/com/brotherc/aquant/industry/repository/StockIndustryBoardEmRepository.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.brotherc.aquant.industry.repository;
22

33
import com.brotherc.aquant.industry.entity.StockIndustryBoardEm;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
46
import org.springframework.data.jpa.repository.JpaRepository;
57
import org.springframework.stereotype.Repository;
68

@@ -10,4 +12,5 @@
1012
public interface StockIndustryBoardEmRepository extends JpaRepository<StockIndustryBoardEm, Long> {
1113
StockIndustryBoardEm findBySectorName(String sectorName);
1214
List<StockIndustryBoardEm> findAllByOrderBySeqNoAsc();
15+
Page<StockIndustryBoardEm> findBySectorNameContaining(String sectorName, Pageable pageable);
1316
}

‎aquant-backend/src/main/java/com/brotherc/aquant/industry/repository/StockIndustryBoardHistoryEmRepository.java‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.brotherc.aquant.industry.entity.StockIndustryBoardHistoryEm;
44
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.data.jpa.repository.Query;
6+
import org.springframework.data.repository.query.Param;
57
import org.springframework.stereotype.Repository;
68

79
import java.util.List;
@@ -12,4 +14,18 @@ public interface StockIndustryBoardHistoryEmRepository extends JpaRepository<Sto
1214
List<StockIndustryBoardHistoryEm> findBySectorNameOrderByTradeDateAsc(String sectorName);
1315
StockIndustryBoardHistoryEm findBySectorNameAndTradeDate(String sectorName, String tradeDate);
1416
StockIndustryBoardHistoryEm findTopBySectorNameOrderByTradeDateDesc(String sectorName);
17+
18+
/**
19+
* 查询每个板块在 startDate 之前最近一个交易日的行情(用于补算涨跌幅的昨收基准)。
20+
* 不能写成"外层全表 + 相关子查询求 max"的形式:该表含 2000 年至今全量 K 线(约 200 万行),
21+
* 相关子查询会对外层每行各做一次索引回溯,实测 25 秒仍无法完成并触发接口超时;
22+
* 改为先按板块分组求最大日期(走 (sector_name, trade_date) 索引松散扫描),再回表精确关联,
23+
* 实测约 60ms
24+
*/
25+
@Query(value = "select h.* from stock_industry_board_history_em h " +
26+
"join (select sector_name, max(trade_date) as max_date from stock_industry_board_history_em " +
27+
"where trade_date < :startDate group by sector_name) p " +
28+
"on h.sector_name = p.sector_name and h.trade_date = p.max_date " +
29+
"order by h.sector_name asc", nativeQuery = true)
30+
List<StockIndustryBoardHistoryEm> findLatestBeforeTradeDateForEachSector(@Param("startDate") String startDate);
1531
}

0 commit comments

Comments
 (0)