@litou.cjs/stock-data-downloader 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +151 -0
  3. package/dist/cli/index.d.ts +7 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +207 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/core/downloader.d.ts +54 -0
  8. package/dist/core/downloader.d.ts.map +1 -0
  9. package/dist/core/downloader.js +149 -0
  10. package/dist/core/downloader.js.map +1 -0
  11. package/dist/core/fuzzySearch.d.ts +32 -0
  12. package/dist/core/fuzzySearch.d.ts.map +1 -0
  13. package/dist/core/fuzzySearch.js +85 -0
  14. package/dist/core/fuzzySearch.js.map +1 -0
  15. package/dist/datasources/cninfo-stable.d.ts +33 -0
  16. package/dist/datasources/cninfo-stable.d.ts.map +1 -0
  17. package/dist/datasources/cninfo-stable.js +99 -0
  18. package/dist/datasources/cninfo-stable.js.map +1 -0
  19. package/dist/datasources/cninfo.d.ts +71 -0
  20. package/dist/datasources/cninfo.d.ts.map +1 -0
  21. package/dist/datasources/cninfo.js +242 -0
  22. package/dist/datasources/cninfo.js.map +1 -0
  23. package/dist/datasources/orgid.d.ts +48 -0
  24. package/dist/datasources/orgid.d.ts.map +1 -0
  25. package/dist/datasources/orgid.js +106 -0
  26. package/dist/datasources/orgid.js.map +1 -0
  27. package/dist/types/index.d.ts +83 -0
  28. package/dist/types/index.d.ts.map +1 -0
  29. package/dist/types/index.js +5 -0
  30. package/dist/types/index.js.map +1 -0
  31. package/dist/utils/fileUtils.d.ts +23 -0
  32. package/dist/utils/fileUtils.d.ts.map +1 -0
  33. package/dist/utils/fileUtils.js +51 -0
  34. package/dist/utils/fileUtils.js.map +1 -0
  35. package/dist/utils/logger.d.ts +14 -0
  36. package/dist/utils/logger.d.ts.map +1 -0
  37. package/dist/utils/logger.js +40 -0
  38. package/dist/utils/logger.js.map +1 -0
  39. package/dist/utils/progress.d.ts +11 -0
  40. package/dist/utils/progress.d.ts.map +1 -0
  41. package/dist/utils/progress.js +31 -0
  42. package/dist/utils/progress.js.map +1 -0
  43. package/package.json +74 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * 股票代码与组织ID映射工具
3
+ *
4
+ * 通过巨潮资讯网官方股票列表接口获取 orgId
5
+ * orgId 格式: gs{market}{code}
6
+ * - 上交所: gssh0600519 (sh = 上海)
7
+ * - 深交所: gssz0002415 (sz = 深圳)
8
+ */
9
+ import axios from 'axios';
10
+ const STOCK_LIST_URL = 'http://www.cninfo.com.cn/new/data/szse_stock.json';
11
+ const CACHE_TTL = 24 * 60 * 60 * 1000; // 24小时缓存
12
+ let stockListCache = null;
13
+ /**
14
+ * 加载股票列表(带缓存)
15
+ */
16
+ async function loadStockList() {
17
+ // 检查缓存是否有效
18
+ if (stockListCache && Date.now() - stockListCache.timestamp < CACHE_TTL) {
19
+ return stockListCache.data;
20
+ }
21
+ try {
22
+ const response = await axios.get(STOCK_LIST_URL, {
23
+ timeout: 30000,
24
+ headers: {
25
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
26
+ }
27
+ });
28
+ const stockList = response.data.stockList || [];
29
+ // 更新缓存
30
+ stockListCache = {
31
+ data: stockList,
32
+ timestamp: Date.now()
33
+ };
34
+ return stockList;
35
+ }
36
+ catch (error) {
37
+ throw new Error(`加载股票列表失败: ${error.message}`);
38
+ }
39
+ }
40
+ /**
41
+ * 根据股票代码获取 orgId
42
+ *
43
+ * @param stockCode 6位股票代码(如 "600519")
44
+ * @returns orgId(如 "gssh0600519")
45
+ */
46
+ export async function getOrgId(stockCode) {
47
+ const stockList = await loadStockList();
48
+ const stock = stockList.find(s => s.code === stockCode);
49
+ if (!stock) {
50
+ throw new Error(`未在巨潮股票列表中找到代码: ${stockCode}`);
51
+ }
52
+ return stock.orgId;
53
+ }
54
+ /**
55
+ * 获取股票完整信息
56
+ *
57
+ * @param stockCode 6位股票代码
58
+ * @returns 股票信息对象
59
+ */
60
+ export async function getStockInfo(stockCode) {
61
+ const stockList = await loadStockList();
62
+ const stock = stockList.find(s => s.code === stockCode);
63
+ if (!stock) {
64
+ throw new Error(`未找到股票: ${stockCode}`);
65
+ }
66
+ return stock;
67
+ }
68
+ /**
69
+ * 批量获取多个股票的 orgId
70
+ *
71
+ * @param stockCodes 股票代码数组
72
+ * @returns orgId 映射表
73
+ */
74
+ export async function getOrgIdBatch(stockCodes) {
75
+ const stockList = await loadStockList();
76
+ const result = new Map();
77
+ for (const code of stockCodes) {
78
+ const stock = stockList.find(s => s.code === code);
79
+ if (stock) {
80
+ result.set(code, stock.orgId);
81
+ }
82
+ }
83
+ return result;
84
+ }
85
+ /**
86
+ * 验证股票代码是否存在
87
+ *
88
+ * @param stockCode 股票代码
89
+ * @returns 是否存在
90
+ */
91
+ export async function isValidStockCode(stockCode) {
92
+ try {
93
+ await getOrgId(stockCode);
94
+ return true;
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
100
+ /**
101
+ * 清除缓存(用于测试或强制刷新)
102
+ */
103
+ export function clearCache() {
104
+ stockListCache = null;
105
+ }
106
+ //# sourceMappingURL=orgid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orgid.js","sourceRoot":"","sources":["../../src/datasources/orgid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * 核心类型定义
3
+ */
4
+ export type MarketType = 'shanghai' | 'shenzhen';
5
+ export type ReportType = 'annual' | 'semiannual' | 'quarterly';
6
+ export interface StockInfo {
7
+ code: string;
8
+ name: string;
9
+ market: MarketType;
10
+ exchange: 'sse' | 'szse';
11
+ listed?: boolean;
12
+ listedDate?: string;
13
+ }
14
+ export interface Report {
15
+ secCode: string;
16
+ secName: string;
17
+ announcementId: string;
18
+ announcementTitle: string;
19
+ announcementTime: number;
20
+ adjunctUrl: string;
21
+ adjunctSize: number;
22
+ adjunctType: 'PDF' | 'HTML';
23
+ reportType?: ReportType;
24
+ year?: number;
25
+ quarter?: number;
26
+ }
27
+ export interface DownloadOptions {
28
+ stockCode: string;
29
+ years?: number;
30
+ outputPath?: string;
31
+ concurrency?: number;
32
+ timeout?: number;
33
+ retryCount?: number;
34
+ verbose?: boolean;
35
+ }
36
+ export interface DownloadResult {
37
+ success: boolean;
38
+ downloaded: number;
39
+ failed: number;
40
+ skipped: number;
41
+ warnings: string[];
42
+ errors: DownloadError[];
43
+ totalTime: number;
44
+ }
45
+ export interface DownloadError {
46
+ report: Report;
47
+ reason: string;
48
+ attempts: number;
49
+ }
50
+ export interface DataSourceConfig {
51
+ name: string;
52
+ priority: number;
53
+ enabled: boolean;
54
+ }
55
+ export interface Config {
56
+ dataSource: {
57
+ primary: string;
58
+ fallback: string[];
59
+ };
60
+ download: {
61
+ defaultYears: number;
62
+ outputPath: string;
63
+ concurrency: number;
64
+ timeout: number;
65
+ retryCount: number;
66
+ retryDelay: number[];
67
+ };
68
+ logging: {
69
+ level: 'debug' | 'info' | 'warn' | 'error';
70
+ fileOutput: boolean;
71
+ logPath: string;
72
+ };
73
+ validation: {
74
+ checkFileSize: boolean;
75
+ checkPdfHeader: boolean;
76
+ };
77
+ network: {
78
+ preflightCheck: boolean;
79
+ rateLimitDelay: number;
80
+ adaptiveRateLimit: boolean;
81
+ };
82
+ }
83
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AACjD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,KAAK,GAAG,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,KAAK,GAAG,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE;QACV,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,QAAQ,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;IACF,OAAO,EAAE;QACP,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;QAC3C,UAAU,EAAE,OAAO,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE;QACV,aAAa,EAAE,OAAO,CAAC;QACvB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,OAAO,EAAE;QACP,cAAc,EAAE,OAAO,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 核心类型定义
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 文件工具函数
3
+ */
4
+ export declare class FileUtils {
5
+ /**
6
+ * 清洗文件名中的特殊字符
7
+ */
8
+ static sanitizeFileName(name: string): string;
9
+ /**
10
+ * 验证 PDF 文件
11
+ */
12
+ static validatePdf(filePath: string): Promise<boolean>;
13
+ /**
14
+ * 格式化文件大小
15
+ */
16
+ static formatFileSize(bytes: number): string;
17
+ /**
18
+ * 格式化时间
19
+ */
20
+ static formatDuration(ms: number): string;
21
+ }
22
+ export default FileUtils;
23
+ //# sourceMappingURL=fileUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileUtils.d.ts","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,qBAAa,SAAS;IACpB;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI7C;;OAEG;WACU,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAe5D;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAM5C;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;CAQ1C;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 文件工具函数
3
+ */
4
+ import fs from 'fs-extra';
5
+ export class FileUtils {
6
+ /**
7
+ * 清洗文件名中的特殊字符
8
+ */
9
+ static sanitizeFileName(name) {
10
+ return name.replace(/[*?"<>|:/\\]/g, '_').replace(/\s+/g, '_');
11
+ }
12
+ /**
13
+ * 验证 PDF 文件
14
+ */
15
+ static async validatePdf(filePath) {
16
+ try {
17
+ const buffer = await fs.readFile(filePath);
18
+ if (buffer.length < 1024) {
19
+ return false;
20
+ }
21
+ const header = buffer.toString('utf-8', 0, 10);
22
+ return header.startsWith('%PDF-');
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ /**
29
+ * 格式化文件大小
30
+ */
31
+ static formatFileSize(bytes) {
32
+ if (bytes < 1024)
33
+ return `${bytes} B`;
34
+ if (bytes < 1024 * 1024)
35
+ return `${(bytes / 1024).toFixed(2)} KB`;
36
+ return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
37
+ }
38
+ /**
39
+ * 格式化时间
40
+ */
41
+ static formatDuration(ms) {
42
+ const seconds = Math.floor(ms / 1000);
43
+ if (seconds < 60)
44
+ return `${seconds}秒`;
45
+ const minutes = Math.floor(seconds / 60);
46
+ const remainingSeconds = seconds % 60;
47
+ return `${minutes}分${remainingSeconds}秒`;
48
+ }
49
+ }
50
+ export default FileUtils;
51
+ //# sourceMappingURL=fileUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileUtils.js","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,UAAU,CAAC;AAE1B,MAAM,OAAO,SAAS;IACpB;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAY;QAClC,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,QAAgB;QACvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAE3C,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;gBACzB,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/C,OAAO,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,KAAa;QACjC,IAAI,KAAK,GAAG,IAAI;YAAE,OAAO,GAAG,KAAK,IAAI,CAAC;QACtC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;YAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAClE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,EAAU;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACtC,IAAI,OAAO,GAAG,EAAE;YAAE,OAAO,GAAG,OAAO,GAAG,CAAC;QAEvC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;QACzC,MAAM,gBAAgB,GAAG,OAAO,GAAG,EAAE,CAAC;QACtC,OAAO,GAAG,OAAO,IAAI,gBAAgB,GAAG,CAAC;IAC3C,CAAC;CACF;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 日志系统
3
+ * 支持控制台和文件双路输出
4
+ */
5
+ export declare class Logger {
6
+ private logger;
7
+ constructor(logFilePath?: string, level?: string);
8
+ info(message: string): void;
9
+ warn(message: string): void;
10
+ error(message: string): void;
11
+ debug(message: string): void;
12
+ }
13
+ export default Logger;
14
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAiB;gBAEnB,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAe;IA8BxD,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAG7B;AAED,eAAe,MAAM,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 日志系统
3
+ * 支持控制台和文件双路输出
4
+ */
5
+ import winston from 'winston';
6
+ export class Logger {
7
+ constructor(logFilePath, level = 'info') {
8
+ const transports = [
9
+ new winston.transports.Console({
10
+ format: winston.format.combine(winston.format.colorize(), winston.format.simple())
11
+ })
12
+ ];
13
+ if (logFilePath) {
14
+ transports.push(new winston.transports.File({
15
+ filename: logFilePath,
16
+ format: winston.format.combine(winston.format.timestamp(), winston.format.printf(({ timestamp, level, message }) => {
17
+ return `[${timestamp}] [${level.toUpperCase()}] ${message}`;
18
+ }))
19
+ }));
20
+ }
21
+ this.logger = winston.createLogger({
22
+ level,
23
+ transports
24
+ });
25
+ }
26
+ info(message) {
27
+ this.logger.info(message);
28
+ }
29
+ warn(message) {
30
+ this.logger.warn(message);
31
+ }
32
+ error(message) {
33
+ this.logger.error(message);
34
+ }
35
+ debug(message) {
36
+ this.logger.debug(message);
37
+ }
38
+ }
39
+ export default Logger;
40
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,MAAM,OAAO,MAAM;IAGjB,YAAY,WAAoB,EAAE,QAAgB,MAAM;QACtD,MAAM,UAAU,GAAwB;YACtC,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC7B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CACxB;aACF,CAAC;SACH,CAAC;QAEF,IAAI,WAAW,EAAE,CAAC;YAChB,UAAU,CAAC,IAAI,CACb,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;gBAC1B,QAAQ,EAAE,WAAW;gBACrB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;oBACtD,OAAO,IAAI,SAAS,MAAM,KAAK,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;gBAC9D,CAAC,CAAC,CACH;aACF,CAAC,CACH,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;YACjC,KAAK;YACL,UAAU;SACX,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,eAAe,MAAM,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 进度条显示
3
+ */
4
+ export declare class ProgressBar {
5
+ private bar;
6
+ start(total: number): void;
7
+ update(current: number, fileName: string): void;
8
+ stop(): void;
9
+ }
10
+ export default ProgressBar;
11
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/utils/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAAsC;IAEjD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAW1B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAM/C,IAAI,IAAI,IAAI;CAMb;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 进度条显示
3
+ */
4
+ import cliProgress from 'cli-progress';
5
+ export class ProgressBar {
6
+ constructor() {
7
+ this.bar = null;
8
+ }
9
+ start(total) {
10
+ this.bar = new cliProgress.SingleBar({
11
+ format: '下载中: [{bar}] {percentage}% | {value}/{total} | {filename}',
12
+ barCompleteChar: '\u2588',
13
+ barIncompleteChar: '\u2591',
14
+ hideCursor: true
15
+ });
16
+ this.bar.start(total, 0, { filename: '准备中...' });
17
+ }
18
+ update(current, fileName) {
19
+ if (this.bar) {
20
+ this.bar.update(current, { filename: fileName });
21
+ }
22
+ }
23
+ stop() {
24
+ if (this.bar) {
25
+ this.bar.stop();
26
+ this.bar = null;
27
+ }
28
+ }
29
+ }
30
+ export default ProgressBar;
31
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/utils/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,WAAW,MAAM,cAAc,CAAC;AAEvC,MAAM,OAAO,WAAW;IAAxB;QACU,QAAG,GAAiC,IAAI,CAAC;IAyBnD,CAAC;IAvBC,KAAK,CAAC,KAAa;QACjB,IAAI,CAAC,GAAG,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC;YACnC,MAAM,EAAE,2DAA2D;YACnE,eAAe,EAAE,QAAQ;YACzB,iBAAiB,EAAE,QAAQ;YAC3B,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,QAAgB;QACtC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAChB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC;CACF;AAED,eAAe,WAAW,CAAC"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@litou.cjs/stock-data-downloader",
3
+ "version": "0.1.0",
4
+ "description": "Download Chinese A-share financial reports (annual, semi-annual, quarterly) | 下载 A 股上市公司财务报告",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "stock-data-downloader": "dist/cli/index.js"
10
+ },
11
+ "directories": {
12
+ "doc": "docs"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsx src/cli/index.ts",
17
+ "start": "node dist/cli/index.js",
18
+ "test": "jest",
19
+ "test:watch": "jest --watch",
20
+ "lint": "eslint src/**/*.ts",
21
+ "clean": "rm -rf dist",
22
+ "prepublishOnly": "npm run clean && npm run build"
23
+ },
24
+ "keywords": [
25
+ "stock",
26
+ "financial",
27
+ "reports",
28
+ "china",
29
+ "a-share",
30
+ "cninfo",
31
+ "下载",
32
+ "财务报告",
33
+ "cli"
34
+ ],
35
+ "author": "陈建生",
36
+ "license": "MIT",
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "CHANGELOG.md"
44
+ ],
45
+ "publishConfig": {
46
+ "registry": "https://registry.npmjs.org/",
47
+ "access": "public"
48
+ },
49
+ "dependencies": {
50
+ "@types/inquirer": "^9.0.9",
51
+ "axios": "^1.13.6",
52
+ "cli-progress": "^3.12.0",
53
+ "commander": "^11.1.0",
54
+ "fs-extra": "^11.3.4",
55
+ "inquirer": "^9.3.8",
56
+ "p-queue": "^7.4.1",
57
+ "pinyin-pro": "^3.28.0",
58
+ "winston": "^3.19.0"
59
+ },
60
+ "devDependencies": {
61
+ "@types/cli-progress": "^3.11.6",
62
+ "@types/fs-extra": "^11.0.4",
63
+ "@types/jest": "^30.0.0",
64
+ "@types/node": "^25.5.0",
65
+ "@typescript-eslint/eslint-plugin": "^8.57.2",
66
+ "@typescript-eslint/parser": "^8.57.2",
67
+ "eslint": "^10.1.0",
68
+ "jest": "^30.3.0",
69
+ "ts-jest": "^29.4.6",
70
+ "ts-node": "^10.9.2",
71
+ "tsx": "^4.19.2",
72
+ "typescript": "^5.9.3"
73
+ }
74
+ }