Преглед изворни кода

feat:数据统计和ai分析整合到一个页面集成到管理菜单中

yangyi пре 1 месец
родитељ
комит
13af8e6b0d

+ 4 - 10
client/src/main/java/space/anyi/client/controller/MainController.java

@@ -46,7 +46,6 @@ public class MainController {
     @FXML private Hyperlink navBrowse;
     @FXML private Hyperlink navTrans;
     @FXML private Hyperlink navSeller;
-    @FXML private Hyperlink navStats;
     @FXML private Hyperlink navAdmin;
 
     // Content panes
@@ -54,7 +53,6 @@ public class MainController {
     @FXML private VBox transPane;
     @FXML private VBox profilePane;
     @FXML private VBox sellerPane;
-    @FXML private ScrollPane statsPane;
     @FXML private SplitPane adminPane;
     @FXML private StackPane adminContentArea;
 
@@ -63,7 +61,7 @@ public class MainController {
     @FXML private VBox adminBookPane;
     @FXML private VBox adminPendingPane;
     @FXML private VBox adminTransPane;
-    @FXML private VBox adminAnalysisPane;
+    @FXML private VBox adminStatsPane;
 
     // Browse tab
     @FXML private TextField searchField;
@@ -170,7 +168,7 @@ public class MainController {
     @FXML private TableColumn<TransactionDetailDTO, java.math.BigDecimal> allTransPriceCol;
     @FXML private TableColumn<TransactionDetailDTO, java.time.LocalDateTime> allTransTimeCol;
     @FXML private Button generateBtn;
-    @FXML private TextArea analysisResult;
+    @FXML private Label analysisResult;
 
     private final BookService bookService = new BookService();
     private final AiService aiService = new AiService();
@@ -206,7 +204,6 @@ public class MainController {
             loadSubPane("/fxml/trans.fxml");
             loadSubPane("/fxml/profile.fxml");
             loadSubPane("/fxml/seller.fxml");
-            loadSubPane("/fxml/stats.fxml");
             loadSubPane("/fxml/admin.fxml");
         } catch (IOException e) {
             throw new RuntimeException("Failed to load sub-pane", e);
@@ -259,13 +256,11 @@ public class MainController {
         navBrowse.getStyleClass().remove("nav-active");
         navTrans.getStyleClass().remove("nav-active");
         navSeller.getStyleClass().remove("nav-active");
-        navStats.getStyleClass().remove("nav-active");
         navAdmin.getStyleClass().remove("nav-active");
         Hyperlink activeNav = null;
         if (pane == browsePane) activeNav = navBrowse;
         else if (pane == transPane) activeNav = navTrans;
         else if (pane == sellerPane) activeNav = navSeller;
-        else if (pane == statsPane) activeNav = navStats;
         else if (pane == adminPane) activeNav = navAdmin;
         if (activeNav != null) {
             activeNav.getStyleClass().add("nav-active");
@@ -285,14 +280,13 @@ public class MainController {
     @FXML private void handleNavTrans() { switchPane(transPane); handleLoadTransactions(); }
     @FXML private void handleNavProfile() { switchPane(profilePane); }
     @FXML private void handleNavSeller() { switchPane(sellerPane); handleLoadMyBooks(); }
-    @FXML private void handleNavStats() { switchPane(statsPane); handleLoadStatistics(); }
     @FXML private void handleNavAdmin() { switchPane(adminPane); switchAdminPane(adminUserPane); handleLoadAllUsers(); }
 
     @FXML private void handleAdminNavUsers() { switchAdminPane(adminUserPane); handleLoadAllUsers(); }
     @FXML private void handleAdminNavBooks() { switchAdminPane(adminBookPane); handleLoadAllBooksAdmin(); }
     @FXML private void handleAdminNavPending() { switchAdminPane(adminPendingPane); handleLoadPendingBooks(); }
     @FXML private void handleAdminNavTransactions() { switchAdminPane(adminTransPane); handleLoadAllTransactions(); }
-    @FXML private void handleAdminNavAnalysis() { switchAdminPane(adminAnalysisPane); }
+    @FXML private void handleAdminNavStats() { switchAdminPane(adminStatsPane); handleLoadStatistics(); }
 
     private void loadSubPane(String path) throws IOException {
         FXMLLoader loader = new FXMLLoader(getClass().getResource(path));
@@ -1273,7 +1267,7 @@ public class MainController {
                             || "AI 服务不可用,正在尝试本地模式...".equals(analysisResult.getText())) {
                         analysisResult.setText("");
                     }
-                    analysisResult.appendText(token);
+                    analysisResult.setText(analysisResult.getText() + token);
                 }),
                 () -> Platform.runLater(() -> {
                     generateBtn.setDisable(false);

+ 84 - 10
client/src/main/resources/fxml/admin.fxml

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <?import javafx.geometry.*?>
+<?import javafx.scene.chart.*?>
 <?import javafx.scene.control.*?>
 <?import javafx.scene.layout.*?>
 
@@ -14,7 +15,7 @@
             <Button text="📚 书籍管理" onAction="#handleAdminNavBooks" maxWidth="Infinity"/>
             <Button text="⏳ 待审核书籍" onAction="#handleAdminNavPending" maxWidth="Infinity"/>
             <Button text="💳 交易管理" onAction="#handleAdminNavTransactions" maxWidth="Infinity"/>
-            <Button text="🤖 AI 分析" onAction="#handleAdminNavAnalysis" maxWidth="Infinity"/>
+            <Button text="📊 数据统计" onAction="#handleAdminNavStats" maxWidth="Infinity"/>
         </VBox>
         <StackPane fx:id="adminContentArea">
             <!-- ── 用户管理 ── -->
@@ -121,16 +122,89 @@
                 </VBox>
             </VBox>
 
-            <!-- ── AI 分析 ── -->
-            <VBox fx:id="adminAnalysisPane" spacing="16" visible="false" managed="false" maxHeight="Infinity">
+            <!-- ── 数据统计 ── -->
+            <VBox fx:id="adminStatsPane" spacing="16" visible="false" managed="false" maxHeight="Infinity">
                 <padding><Insets top="16" right="24" bottom="16" left="24"/></padding>
-                <VBox VBox.vgrow="ALWAYS" styleClass="well" spacing="12">
-                    <HBox spacing="10" alignment="CENTER_LEFT">
-                        <Label text="🤖 AI 分析" style="-fx-font-size: 16px; -fx-font-weight: bold;" HBox.hgrow="ALWAYS"/>
-                        <Button fx:id="generateBtn" text="生成分析报告" onAction="#handleGenerateAnalysis" styleClass="btn,btn-primary"/>
-                    </HBox>
-                    <TextArea fx:id="analysisResult" editable="false" wrapText="true" VBox.vgrow="ALWAYS" styleClass="form-control"/>
-                </VBox>
+                <ScrollPane fitToWidth="true" VBox.vgrow="ALWAYS" style="-fx-background-color: transparent;">
+                    <VBox spacing="16">
+                        <HBox spacing="10" alignment="CENTER_LEFT">
+                            <Label text="数据洞察" style="-fx-font-size: 18px; -fx-font-weight: bold;" HBox.hgrow="ALWAYS"/>
+                            <Button text="刷新数据" onAction="#handleLoadStatistics" styleClass="btn,btn-secondary"/>
+                        </HBox>
+                        <HBox spacing="16">
+                            <VBox styleClass="kpi-card" HBox.hgrow="ALWAYS">
+                                <Label text="交易总金额" styleClass="kpi-label"/>
+                                <Label fx:id="statTotalAmount" text="-" styleClass="kpi-value" style="-fx-text-fill: -fx-success;"/>
+                            </VBox>
+                            <VBox styleClass="kpi-card" HBox.hgrow="ALWAYS">
+                                <Label text="交易总笔数" styleClass="kpi-label"/>
+                                <Label fx:id="statTotalCount" text="-" styleClass="kpi-value"/>
+                            </VBox>
+                        </HBox>
+                        <VBox styleClass="well" spacing="12">
+                            <Label text="🔥 热门院系 TOP 3" style="-fx-font-size: 16px; -fx-font-weight: bold;"/>
+                            <SplitPane dividerPositions="0.65" prefHeight="220">
+                                <TableView fx:id="deptTable" minWidth="200">
+                                    <columns>
+                                        <TableColumn fx:id="deptRankCol" text="排名" prefWidth="60"/>
+                                        <TableColumn fx:id="deptNameCol" text="院系" prefWidth="200"/>
+                                        <TableColumn fx:id="deptSalesCol" text="销售额" prefWidth="150"/>
+                                    </columns>
+                                </TableView>
+                                <PieChart fx:id="deptPieChart" minWidth="200" labelsVisible="true" legendVisible="false"/>
+                            </SplitPane>
+                        </VBox>
+                        <VBox styleClass="well" spacing="12">
+                            <Label text="📚 畅销书籍 TOP 5" style="-fx-font-size: 16px; -fx-font-weight: bold;"/>
+                            <SplitPane dividerPositions="0.65" prefHeight="220">
+                                <TableView fx:id="hotBookTable" minWidth="250">
+                                    <columns>
+                                        <TableColumn fx:id="hotBookRankCol" text="排名" prefWidth="60"/>
+                                        <TableColumn fx:id="hotBookIdCol" text="编号" prefWidth="50"/>
+                                        <TableColumn fx:id="hotBookTitleCol" text="书名" prefWidth="200"/>
+                                        <TableColumn fx:id="hotBookAuthorCol" text="作者" prefWidth="120"/>
+                                        <TableColumn fx:id="hotBookSoldCol" text="销量" prefWidth="80"/>
+                                    </columns>
+                                </TableView>
+                                <BarChart fx:id="hotBookChart" minWidth="200" animated="false" legendVisible="false">
+                                    <xAxis>
+                                        <CategoryAxis side="BOTTOM" tickLabelRotation="-30"/>
+                                    </xAxis>
+                                    <yAxis>
+                                        <NumberAxis side="LEFT" label="销量"/>
+                                    </yAxis>
+                                </BarChart>
+                            </SplitPane>
+                        </VBox>
+                        <VBox styleClass="well" spacing="12">
+                            <Label text="📊 各院系平均折扣率" style="-fx-font-size: 16px; -fx-font-weight: bold;"/>
+                            <SplitPane dividerPositions="0.65" prefHeight="220">
+                                <TableView fx:id="discountTable" minWidth="250">
+                                    <columns>
+                                        <TableColumn fx:id="discountDeptCol" text="院系" prefWidth="200"/>
+                                        <TableColumn fx:id="discountRateCol" text="平均折扣率" prefWidth="150"/>
+                                    </columns>
+                                </TableView>
+                                <LineChart fx:id="discountChart" minWidth="200" animated="false" legendVisible="false" createSymbols="true">
+                                    <xAxis>
+                                        <CategoryAxis side="BOTTOM"/>
+                                    </xAxis>
+                                    <yAxis>
+                                        <NumberAxis side="LEFT" label="折扣率(%)"/>
+                                    </yAxis>
+                                </LineChart>
+                            </SplitPane>
+                        </VBox>
+                        <!-- ── AI 分析 ── -->
+                        <VBox styleClass="well" spacing="12">
+                            <HBox spacing="10" alignment="CENTER_LEFT">
+                                <Label text="🤖 AI 分析" style="-fx-font-size: 16px; -fx-font-weight: bold;" HBox.hgrow="ALWAYS"/>
+                                <Button fx:id="generateBtn" text="生成分析报告" onAction="#handleGenerateAnalysis" styleClass="btn,btn-primary"/>
+                            </HBox>
+                            <Label fx:id="analysisResult" wrapText="true" maxWidth="Infinity" style="-fx-padding: 8px; -fx-background-color: #f5f5f5; -fx-background-radius: 4px;"/>
+                        </VBox>
+                    </VBox>
+                </ScrollPane>
             </VBox>
         </StackPane>
     </items>

+ 0 - 1
client/src/main/resources/fxml/main.fxml

@@ -11,7 +11,6 @@
             <Hyperlink fx:id="navBrowse" text="📖 浏览书籍" onAction="#handleNavBrowse" style="-fx-padding: 16px 18px; -fx-font-size: 14px;"/>
             <Hyperlink fx:id="navTrans" text="📄 我的交易" onAction="#handleNavTrans" style="-fx-padding: 16px 18px; -fx-font-size: 14px;"/>
             <Hyperlink fx:id="navSeller" text="🏪 卖家中心" onAction="#handleNavSeller" style="-fx-padding: 16px 18px; -fx-font-size: 14px;"/>
-            <Hyperlink fx:id="navStats" text="📊 数据统计" onAction="#handleNavStats" style="-fx-padding: 16px 18px; -fx-font-size: 14px;"/>
             <Hyperlink fx:id="navAdmin" text="⚙️ 管理" onAction="#handleNavAdmin" style="-fx-padding: 16px 18px; -fx-font-size: 14px;"/>
             <Region HBox.hgrow="ALWAYS"/>
             <MenuButton fx:id="userMenu" style="-fx-font-size: 13px; -fx-padding: 0 12px;">