diff --git a/frontend/dashboard.html b/frontend/dashboard.html
index 2a00e07..13f600b 100644
--- a/frontend/dashboard.html
+++ b/frontend/dashboard.html
@@ -162,7 +162,6 @@
}
function renderPromptCard(prompt, index) {
- const preview = truncateText(prompt.prompt, 150);
return `
-
结构视图暂不可用(需要关联构建会话数据)
+
加载中...
@@ -227,7 +226,7 @@
return parts.length > 0 ? parts.join('') : `${escapeHtml(text)}
`;
}
- function togglePromptView(index) {
+ async function togglePromptView(index) {
const textView = document.getElementById(`prompt-text-${index}`);
const structView = document.getElementById(`prompt-struct-${index}`);
const btn = document.getElementById(`view-btn-${index}`);
@@ -240,6 +239,58 @@
textView.classList.add('hidden');
structView.classList.remove('hidden');
btn.textContent = '切换到文本视图';
+ // Load structure view data
+ await loadStructureView(index);
+ }
+ }
+
+ async function loadStructureView(index) {
+ const container = document.getElementById(`struct-content-${index}`);
+ if (container.dataset.loaded) return;
+
+ try {
+ const data = await API.get(`/api/claude/sessions/${currentSession}/builder`);
+ if (!data || !data.session) {
+ container.innerHTML = '未找到关联的构建会话
';
+ container.dataset.loaded = 'true';
+ return;
+ }
+
+ let html = '';
+
+ // Tags
+ if (data.tags && data.tags.length > 0) {
+ html += '🏷️ 标签选择
';
+ html += data.tags.map(t => `
+
+
${escapeHtml(t.tag_name)}
+
选项: ${escapeHtml(t.option_label)}
+
${escapeHtml(t.constraint_text)}
+
+ `).join('');
+ html += '
';
+ }
+
+ // Snippets
+ if (data.snippets && data.snippets.length > 0) {
+ html += '📄 片段
';
+ html += data.snippets.map(s => `
+
+
${escapeHtml(s.name)} (${escapeHtml(s.category)})
+
${escapeHtml(s.content.substring(0, 200))}${s.content.length > 200 ? '...' : ''}
+
+ `).join('');
+ html += '
';
+ }
+
+ if (!html) {
+ html = '无标签和片段数据
';
+ }
+
+ container.innerHTML = html;
+ container.dataset.loaded = 'true';
+ } catch (e) {
+ container.innerHTML = '加载结构视图失败
';
}
}
diff --git a/internal/db/db.go b/internal/db/db.go
index 5fafec4..9f1f298 100644
--- a/internal/db/db.go
+++ b/internal/db/db.go
@@ -109,6 +109,36 @@ func AutoMigrate() error {
return nil
}
+// LoadSettings loads persisted settings from the database into config
+func LoadSettings(cfg *config.Config) {
+ // Create settings table if not exists
+ DB.Exec(`CREATE TABLE IF NOT EXISTS settings (
+ key_name varchar(64) NOT NULL PRIMARY KEY,
+ value text NOT NULL
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`)
+
+ rows, err := DB.Query("SELECT key_name, value FROM settings")
+ if err != nil {
+ return
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var key, value string
+ if err := rows.Scan(&key, &value); err != nil {
+ continue
+ }
+ switch key {
+ case "llm_api_base_url":
+ cfg.LLMAPIBaseURL = value
+ case "llm_api_key":
+ cfg.LLMAPIKey = value
+ case "llm_model_name":
+ cfg.LLMModelName = value
+ }
+ }
+}
+
// EnsurePromptsTable creates the prompts table if it doesn't exist (for dev environments)
func EnsurePromptsTable() {
_, err := DB.Exec(`CREATE TABLE IF NOT EXISTS prompts (
diff --git a/internal/handlers/claude.go b/internal/handlers/claude.go
index 8e3ee0c..4273e71 100644
--- a/internal/handlers/claude.go
+++ b/internal/handlers/claude.go
@@ -4,6 +4,7 @@ import (
"net/http"
"prompt-generator/internal/db"
+ "prompt-generator/internal/models"
)
func GetClaudeSessions(w http.ResponseWriter, r *http.Request) {
@@ -47,6 +48,81 @@ func GetClaudeSessions(w http.ResponseWriter, r *http.Request) {
success(w, sessions)
}
+// GetBuilderSessionByClaudeSession looks up a builder session by claude_session_id
+func GetBuilderSessionByClaudeSession(w http.ResponseWriter, r *http.Request) {
+ sessionID := r.PathValue("session_id")
+ if sessionID == "" {
+ fail(w, 400, "session_id 必填")
+ return
+ }
+
+ var s models.BuilderSession
+ err := db.DB.QueryRow("SELECT id, title, project_name, final_prompt, claude_session_id, created_at, updated_at FROM builder_sessions WHERE claude_session_id=?", sessionID).
+ Scan(&s.ID, &s.Title, &s.ProjectName, &s.FinalPrompt, &s.ClaudeSessionID, &s.CreatedAt, &s.UpdatedAt)
+ if err != nil {
+ // Not found is not an error
+ success(w, nil)
+ return
+ }
+
+ // Load tags with details
+ tags := []map[string]interface{}{}
+ tagRows, err := db.DB.Query(`
+ SELECT bst.tag_id, bst.tag_option_id, t.name, to2.label, to2.constraint_text
+ FROM builder_session_tags bst
+ JOIN tags t ON t.id = bst.tag_id
+ JOIN tag_options to2 ON to2.id = bst.tag_option_id
+ WHERE bst.builder_session_id=?`, s.ID)
+ if err == nil {
+ for tagRows.Next() {
+ var tagID, optionID int64
+ var tagName, optionLabel, constraintText string
+ if err := tagRows.Scan(&tagID, &optionID, &tagName, &optionLabel, &constraintText); err == nil {
+ tags = append(tags, map[string]interface{}{
+ "tag_id": tagID,
+ "tag_option_id": optionID,
+ "tag_name": tagName,
+ "option_label": optionLabel,
+ "constraint_text": constraintText,
+ })
+ }
+ }
+ tagRows.Close()
+ }
+
+ // Load snippets with details
+ snippets := []map[string]interface{}{}
+ snippetRows, err := db.DB.Query(`
+ SELECT bss.snippet_id, s.name, s.content, s.category, bss.sort_order
+ FROM builder_session_snippets bss
+ JOIN snippets s ON s.id = bss.snippet_id
+ WHERE bss.builder_session_id=?
+ ORDER BY bss.sort_order`, s.ID)
+ if err == nil {
+ for snippetRows.Next() {
+ var snippetID int64
+ var name, content, category string
+ var sortOrder int
+ if err := snippetRows.Scan(&snippetID, &name, &content, &category, &sortOrder); err == nil {
+ snippets = append(snippets, map[string]interface{}{
+ "snippet_id": snippetID,
+ "name": name,
+ "content": content,
+ "category": category,
+ "sort_order": sortOrder,
+ })
+ }
+ }
+ snippetRows.Close()
+ }
+
+ success(w, map[string]interface{}{
+ "session": s,
+ "tags": tags,
+ "snippets": snippets,
+ })
+}
+
func GetClaudePrompts(w http.ResponseWriter, r *http.Request) {
sessionID := r.PathValue("session_id")
if sessionID == "" {
diff --git a/main.go b/main.go
index 367f3e7..c86548b 100644
--- a/main.go
+++ b/main.go
@@ -41,6 +41,9 @@ func main() {
// Ensure prompts table exists for dev environments
db.EnsurePromptsTable()
+ // Load persisted settings from database
+ db.LoadSettings(cfg)
+
// Setup routes
mux := http.NewServeMux()
@@ -89,6 +92,7 @@ func main() {
apiMux.HandleFunc("GET /api/claude/sessions", handlers.GetClaudeSessions)
apiMux.HandleFunc("GET /api/claude/sessions/{session_id}/prompts", handlers.GetClaudePrompts)
+ apiMux.HandleFunc("GET /api/claude/sessions/{session_id}/builder", handlers.GetBuilderSessionByClaudeSession)
apiMux.HandleFunc("GET /api/dashboard/projects", handlers.GetDashboardProjects)
apiMux.HandleFunc("GET /api/dashboard/projects/{name}/sessions", handlers.GetDashboardSessions)