diff --git a/tushare/src/api.py b/tushare/src/api.py
index 5d6d6b0..4fb72f6 100644
--- a/tushare/src/api.py
+++ b/tushare/src/api.py
@@ -193,3 +193,10 @@ def list_candles(
return [dict(zip(cols, row)) for row in cur.fetchall()]
finally:
conn.close()
+
+
+@app.post("/api/v1/admin/reset-data")
+def reset_data():
+ """清空所有行情数据(仅限管理员调用)。"""
+ storage.truncate_all()
+ return {"status": "ok", "message": "已清空所有行情数据"}
diff --git a/tushare/src/storage.py b/tushare/src/storage.py
index 0363824..c3ca757 100644
--- a/tushare/src/storage.py
+++ b/tushare/src/storage.py
@@ -152,3 +152,14 @@ def get_latest_score(ts_code: str, db_url: str = DEFAULT_DB_URL) -> Optional[dic
return dict(row) if row else None
finally:
conn.close()
+
+
+def truncate_all(db_url: str = DEFAULT_DB_URL):
+ """清空所有行情数据(candles + scores),保留用户表。"""
+ conn = _get_conn(db_url)
+ try:
+ with conn.cursor() as cur:
+ cur.execute("TRUNCATE TABLE candles, scores")
+ conn.commit()
+ finally:
+ conn.close()
diff --git a/web/backend/internal/handlers/reset.go b/web/backend/internal/handlers/reset.go
new file mode 100644
index 0000000..caadabb
--- /dev/null
+++ b/web/backend/internal/handlers/reset.go
@@ -0,0 +1,22 @@
+package handlers
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "time"
+)
+
+func (d *Deps) AdminResetData(w http.ResponseWriter, r *http.Request) {
+ client := &http.Client{Timeout: 10 * time.Second}
+ resp, err := client.Post(d.TushareURL+"/api/v1/admin/reset-data", "application/json", nil)
+ if err != nil {
+ writeErr(w, http.StatusBadGateway, fmt.Sprintf("tushare service unavailable: %v", err))
+ return
+ }
+ defer resp.Body.Close()
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(resp.StatusCode)
+ _, _ = io.Copy(w, resp.Body)
+}
diff --git a/web/backend/internal/router/router.go b/web/backend/internal/router/router.go
index e109c27..8fd5dbc 100644
--- a/web/backend/internal/router/router.go
+++ b/web/backend/internal/router/router.go
@@ -38,6 +38,7 @@ func New(d *handlers.Deps, dist fs.FS) http.Handler {
r.Post("/admin/users", d.AdminCreateUser)
r.Patch("/admin/users/{id}", d.AdminPatchUser)
r.Delete("/admin/users/{id}", d.AdminDeleteUser)
+ r.Post("/admin/reset-data", d.AdminResetData)
})
})
})
diff --git a/web/frontend/src/App.vue b/web/frontend/src/App.vue
index 9a92b7c..2ef8a9c 100644
--- a/web/frontend/src/App.vue
+++ b/web/frontend/src/App.vue
@@ -1,9 +1,11 @@
@@ -47,6 +70,9 @@ function closeDrawer() {
K 线 / 持仓
手动打分
用户管理
+
+ 数据重置
+
diff --git a/web/frontend/src/api/admin.ts b/web/frontend/src/api/admin.ts
new file mode 100644
index 0000000..e30f81c
--- /dev/null
+++ b/web/frontend/src/api/admin.ts
@@ -0,0 +1,5 @@
+import client from './client'
+
+export function resetAllData() {
+ return client.post('/admin/reset-data').then((r) => r.data)
+}