@hedgehog-finance/hedgehog-plugin 1.0.19 → 1.0.21
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.
- package/dist/index.d.ts +1 -1
- package/dist/src/channel.js +143 -4
- package/dist/src/channel.js.map +1 -1
- package/dist/src/core/database.js +156 -0
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/features/index.js +9 -1
- package/dist/src/features/index.js.map +1 -1
- package/dist/src/features/notes/schema.d.ts +84 -0
- package/dist/src/features/notes/schema.js +40 -0
- package/dist/src/features/notes/schema.js.map +1 -0
- package/dist/src/features/notes/tools.d.ts +11 -0
- package/dist/src/features/notes/tools.js +297 -0
- package/dist/src/features/notes/tools.js.map +1 -0
- package/dist/src/features/pluginInfo/tools.d.ts +11 -0
- package/dist/src/features/pluginInfo/tools.js +49 -0
- package/dist/src/features/pluginInfo/tools.js.map +1 -0
- package/dist/src/features/profileLibrary/schema.d.ts +29 -0
- package/dist/src/features/profileLibrary/schema.js +21 -0
- package/dist/src/features/profileLibrary/schema.js.map +1 -0
- package/dist/src/features/profileLibrary/tools.d.ts +11 -0
- package/dist/src/features/profileLibrary/tools.js +163 -0
- package/dist/src/features/profileLibrary/tools.js.map +1 -0
- package/dist/src/features/stockAnalysis/schema.d.ts +61 -0
- package/dist/src/features/stockAnalysis/schema.js +30 -0
- package/dist/src/features/stockAnalysis/schema.js.map +1 -0
- package/dist/src/features/stockAnalysis/tools.d.ts +20 -0
- package/dist/src/features/stockAnalysis/tools.js +138 -0
- package/dist/src/features/stockAnalysis/tools.js.map +1 -0
- package/dist/src/features/watchlist/logic.js +7 -60
- package/dist/src/features/watchlist/logic.js.map +1 -1
- package/dist/src/features/watchlist/tools.js +61 -26
- package/dist/src/features/watchlist/tools.js.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/package.json +5 -5
- package/src/channel.ts +150 -4
- package/src/core/database.ts +155 -0
- package/src/features/index.ts +9 -1
- package/src/features/notes/schema.ts +75 -0
- package/src/features/notes/tools.ts +352 -0
- package/src/features/pluginInfo/tools.ts +63 -0
- package/src/features/profileLibrary/schema.ts +35 -0
- package/src/features/profileLibrary/tools.ts +194 -0
- package/src/features/stockAnalysis/schema.ts +60 -0
- package/src/features/stockAnalysis/tools.ts +192 -0
- package/src/features/watchlist/logic.ts +7 -63
- package/src/features/watchlist/tools.ts +83 -48
|
@@ -135,6 +135,11 @@ export const watchlistTools = {
|
|
|
135
135
|
return JSON.stringify({ success: true, skipped: true, reason: "duplicate", id: existingBeforeClassify.id });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
const currentCount = (db.prepare("SELECT COUNT(*) as count FROM watchlist WHERE userId = ? AND isDeleted = 0").get(uId) as { count: number }).count;
|
|
139
|
+
if (currentCount >= 30) {
|
|
140
|
+
return JSON.stringify({ success: false, error: "自选股数量已达 30 只上限,请先移除部分股票" });
|
|
141
|
+
}
|
|
142
|
+
|
|
138
143
|
let classification: Awaited<ReturnType<typeof watchlistLogic.getStockClassification>>;
|
|
139
144
|
try {
|
|
140
145
|
classification = await watchlistLogic.getStockClassification(ctx.runtime, stock.stockName, stock.stockCode, stock.exchange, uId);
|
|
@@ -227,6 +232,14 @@ export const watchlistTools = {
|
|
|
227
232
|
return JSON.stringify({ success: true, ids: [], skipped });
|
|
228
233
|
}
|
|
229
234
|
|
|
235
|
+
const currentCount = (db.prepare("SELECT COUNT(*) as count FROM watchlist WHERE userId = ? AND isDeleted = 0").get(uId) as { count: number }).count;
|
|
236
|
+
if (currentCount + stocksToAdd.length > 30) {
|
|
237
|
+
return JSON.stringify({
|
|
238
|
+
success: false,
|
|
239
|
+
error: `自选股数量已达 30 只上限(当前已有 ${currentCount} 只,本次尝试添加 ${stocksToAdd.length} 只)`
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
230
243
|
let batchResults: Awaited<ReturnType<typeof watchlistLogic.classifyStocksTogether>>;
|
|
231
244
|
try {
|
|
232
245
|
batchResults = await watchlistLogic.classifyStocksTogether(ctx.runtime, stocksToAdd, uId);
|
|
@@ -325,6 +338,14 @@ export const watchlistTools = {
|
|
|
325
338
|
if (info.changes > 0) {
|
|
326
339
|
db.prepare("DELETE FROM watchlist_industry_items WHERE watchlistId = ? AND userId = ?").run(args.id, uId);
|
|
327
340
|
db.prepare("DELETE FROM watchlist_theme_items WHERE watchlistId = ? AND userId = ?").run(args.id, uId);
|
|
341
|
+
|
|
342
|
+
// 同时删除该自选股关联的所有投研笔记及笔记内保存的资料库关联记录
|
|
343
|
+
db.prepare(`
|
|
344
|
+
DELETE FROM stock_note_profile_libraries
|
|
345
|
+
WHERE userId = ?
|
|
346
|
+
AND noteId IN (SELECT id FROM stock_notes WHERE watchlistId = ? AND userId = ?)
|
|
347
|
+
`).run(uId, args.id, uId);
|
|
348
|
+
db.prepare("DELETE FROM stock_notes WHERE watchlistId = ? AND userId = ?").run(args.id, uId);
|
|
328
349
|
}
|
|
329
350
|
|
|
330
351
|
db.exec("COMMIT");
|
|
@@ -356,23 +377,15 @@ export const watchlistTools = {
|
|
|
356
377
|
FROM watchlist w
|
|
357
378
|
JOIN ${table} ci ON w.id = ci.watchlistId
|
|
358
379
|
WHERE w.userId = ? AND w.isDeleted = 0 AND ci.categoryId = ?
|
|
359
|
-
ORDER BY
|
|
380
|
+
ORDER BY w.sortOrder ASC
|
|
360
381
|
`;
|
|
361
382
|
params.push(args.categoryId);
|
|
362
383
|
} else {
|
|
363
384
|
query = `
|
|
364
|
-
SELECT w
|
|
365
|
-
SELECT MAX(total.weight) FROM (
|
|
366
|
-
SELECT weight FROM watchlist_industry_items WHERE watchlistId = w.id
|
|
367
|
-
UNION ALL
|
|
368
|
-
SELECT weight FROM watchlist_theme_items WHERE watchlistId = w.id
|
|
369
|
-
UNION ALL
|
|
370
|
-
SELECT 0 as weight
|
|
371
|
-
) total
|
|
372
|
-
) as globalWeight
|
|
385
|
+
SELECT w.*
|
|
373
386
|
FROM watchlist w
|
|
374
387
|
WHERE w.userId = ? AND w.isDeleted = 0
|
|
375
|
-
ORDER BY
|
|
388
|
+
ORDER BY w.sortOrder ASC
|
|
376
389
|
`;
|
|
377
390
|
}
|
|
378
391
|
|
|
@@ -498,7 +511,7 @@ export const watchlistTools = {
|
|
|
498
511
|
WHERE userId = ? AND stockCode = ? AND isDeleted = 0
|
|
499
512
|
`);
|
|
500
513
|
sortedResults.forEach((item: any, i: number) => {
|
|
501
|
-
const currentOrder = i * 10;
|
|
514
|
+
const currentOrder = i * 10;
|
|
502
515
|
stmt.run(currentOrder, uId, item.code);
|
|
503
516
|
});
|
|
504
517
|
db.exec("COMMIT");
|
|
@@ -584,48 +597,70 @@ export const watchlistTools = {
|
|
|
584
597
|
if (!ctx.runtime) return JSON.stringify({ success: false, error: "Runtime not available" });
|
|
585
598
|
const db = getDB();
|
|
586
599
|
const uId = String(ctx.userId);
|
|
587
|
-
db.exec("BEGIN TRANSACTION");
|
|
588
600
|
try {
|
|
589
|
-
db.prepare("DELETE FROM watchlist_industry_items WHERE userId = ?").run(uId);
|
|
590
|
-
db.prepare("DELETE FROM watchlist_theme_items WHERE userId = ?").run(uId);
|
|
591
601
|
const stocks = db.prepare("SELECT stockName, stockCode, exchange, market FROM watchlist WHERE userId = ? AND isDeleted = 0").all(uId) as any[];
|
|
602
|
+
if (stocks.length > 30) {
|
|
603
|
+
return JSON.stringify({ success: false, error: "自选股数量超过 30 只,暂不支持批量重置分类" });
|
|
604
|
+
}
|
|
592
605
|
if (stocks.length > 0) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
})
|
|
619
|
-
|
|
606
|
+
// 1. 强制等待 AI 批量分类结果(在事务外部执行耗时 API 请求,避免锁库)
|
|
607
|
+
const batchResults = await watchlistLogic.getBatchStockClassification(ctx.runtime, stocks, uId, {
|
|
608
|
+
forceRefresh: true,
|
|
609
|
+
requireComplete: true
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// 2. 开启数据库事务,清空并更新分类
|
|
613
|
+
db.exec("BEGIN TRANSACTION");
|
|
614
|
+
try {
|
|
615
|
+
db.prepare("DELETE FROM watchlist_industry_items WHERE userId = ?").run(uId);
|
|
616
|
+
db.prepare("DELETE FROM watchlist_theme_items WHERE userId = ?").run(uId);
|
|
617
|
+
|
|
618
|
+
const userStocks = db.prepare("SELECT id, stockCode, exchange FROM watchlist WHERE userId = ? AND isDeleted = 0").all(uId) as any[];
|
|
619
|
+
batchResults.forEach((res, i) => {
|
|
620
|
+
if (res) {
|
|
621
|
+
const s = stocks[i];
|
|
622
|
+
const match = userStocks.find(us => us.stockCode === s.stockCode && us.exchange === s.exchange);
|
|
623
|
+
if (match) {
|
|
624
|
+
upsertStockClassificationCache(db, {
|
|
625
|
+
stockName: s.stockName,
|
|
626
|
+
stockCode: s.stockCode,
|
|
627
|
+
exchange: s.exchange,
|
|
628
|
+
market: s.market
|
|
629
|
+
}, res);
|
|
630
|
+
const cats = [
|
|
631
|
+
...(res.industry ? [{ name: res.industry.name, type: 'industry' as const, weight: res.industry.weight }] : []),
|
|
632
|
+
...res.theme.map((t: any) => ({ name: t.name, type: 'theme' as const, weight: t.weight }))
|
|
633
|
+
];
|
|
634
|
+
cats.forEach(c => {
|
|
635
|
+
const catId = watchlistLogic._ensureCategory(db, c.name, c.type, uId);
|
|
636
|
+
if (catId) {
|
|
637
|
+
const table = c.type === 'industry' ? 'watchlist_industry_items' : 'watchlist_theme_items';
|
|
638
|
+
db.prepare(`INSERT OR IGNORE INTO ${table} (id, watchlistId, userId, categoryId, weight) VALUES (?, ?, ?, ?, ?)`).run(randomUUID(), match.id, uId, catId, c.weight);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
620
641
|
}
|
|
621
|
-
}
|
|
622
|
-
})
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
db.exec("COMMIT");
|
|
645
|
+
} catch (transactionError: any) {
|
|
646
|
+
if (db.inTransaction) db.exec("ROLLBACK");
|
|
647
|
+
throw transactionError;
|
|
648
|
+
}
|
|
649
|
+
} else {
|
|
650
|
+
// 如果没有自选股,也正常开启事务清空旧的分类数据
|
|
651
|
+
db.exec("BEGIN TRANSACTION");
|
|
652
|
+
try {
|
|
653
|
+
db.prepare("DELETE FROM watchlist_industry_items WHERE userId = ?").run(uId);
|
|
654
|
+
db.prepare("DELETE FROM watchlist_theme_items WHERE userId = ?").run(uId);
|
|
655
|
+
db.exec("COMMIT");
|
|
656
|
+
} catch (transactionError: any) {
|
|
657
|
+
if (db.inTransaction) db.exec("ROLLBACK");
|
|
658
|
+
throw transactionError;
|
|
659
|
+
}
|
|
623
660
|
}
|
|
624
|
-
|
|
625
|
-
return JSON.stringify({ success: true, message: "重置分类请求已提交" });
|
|
661
|
+
return JSON.stringify({ success: true, message: "智能分类已完成重置" });
|
|
626
662
|
} catch (e: any) {
|
|
627
|
-
|
|
628
|
-
return JSON.stringify({ success: false, error: e.message });
|
|
663
|
+
return JSON.stringify({ success: false, error: e.message || "重置分类失败" });
|
|
629
664
|
}
|
|
630
665
|
}
|
|
631
666
|
}
|