主要技術棧
Java
Mysql
SpringBoot
Tomcat
HTML CSS JavaScript
該課設必備環境配置教程:(參考給出的鏈接和給出的關鍵鏈接)
JAVA課設必備環境配置 教程 JDK Tomcat配置 IDEA開發環境配置 項目部署參考視頻 若依框架 鏈接數據庫格式注意事項
在Java中連接MySQL數據庫
application配置文件
# 項目相關配置
ruoyi:# 名稱name: 2022項目實踐題庫管理系統# 版本version: 4.7.3# 版權年份copyrightYear: 2022# 實例演示開關demoEnabled: false# 文件路徑 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)profile: D:/ruoyi/uploadPath# 獲取ip地址開關addressEnabled: false# 開發環境配置
server:# 服務器的HTTP端口,默認為80port: 801servlet:# 應用的訪問路徑context-path: /tomcat:# tomcat的URI編碼uri-encoding: UTF-8# 連接數滿后的排隊數,默認為100accept-count: 1000threads:# tomcat最大線程數,默認為200max: 800# Tomcat啟動初始化的線程數,默認值10min-spare: 100# 日志配置
logging:level:com.ruoyi: debugorg.springframework: warn# 用戶配置
user:password:# 密碼錯誤{maxRetryCount}次鎖定10分鐘maxRetryCount: 5# Spring配置
spring:# 模板引擎thymeleaf:mode: HTMLencoding: utf-8# 禁用緩存cache: false# 資源信息messages:# 國際化資源文件路徑basename: static/i18n/messagesjackson:time-zone: GMT+8date-format: yyyy-MM-dd HH:mm:ssprofiles: active: druid# 文件上傳servlet:multipart:# 單個文件大小max-file-size: 10MB# 設置總上傳的文件大小max-request-size: 20MB# 服務模塊devtools:restart:# 熱部署開關enabled: true# MyBatis
mybatis:# 搜索指定包別名typeAliasesPackage: com.ruoyi.project.**.domain# 配置mapper的掃描,找到所有的mapper.xml映射文件mapperLocations: classpath:mybatis/**/*Mapper.xml# 加載全局的配置文件configLocation: classpath:mybatis/mybatis-config.xml# PageHelper分頁插件
pagehelper: helperDialect: mysqlsupportMethodsArguments: trueparams: count=countSql # Shiro
shiro:user:# 登錄地址loginUrl: /login# 權限認證失敗地址unauthorizedUrl: /unauth# 首頁地址indexUrl: /index# 驗證碼開關captchaEnabled: true# 驗證碼類型 math 數組計算 char 字符captchaType: mathcookie:# 設置Cookie的域名 默認空,即當前訪問的域名domain: # 設置cookie的有效訪問路徑path: /# 設置HttpOnly屬性httpOnly: true# 設置Cookie的過期時間,天為單位maxAge: 30# 設置密鑰,務必保持唯一性(生成方式,直接拷貝到main運行即可)Base64.encodeToString(CipherUtils.generateNewKey(128, "AES").getEncoded()) (默認啟動生成隨機秘鑰,隨機秘鑰會導致之前客戶端RememberMe Cookie無效,如設置固定秘鑰RememberMe Cookie則有效)cipherKey: session:# Session超時時間,-1代表永不過期(默認30分鐘)expireTime: 30# 同步session到數據庫的周期(默認1分鐘)dbSyncPeriod: 1# 相隔多久檢查一次session的有效性,默認就是10分鐘validationInterval: 10# 同一個用戶最大會話數,比如2的意思是同一個賬號允許最多同時兩個人登錄(默認-1不限制)maxSession: -1# 踢出之前登錄的/之后登錄的用戶,默認踢出之前登錄的用戶kickoutAfter: falserememberMe:# 是否開啟記住我enabled: true# 防止XSS攻擊
xss: # 過濾開關enabled: true# 排除鏈接(多個用逗號分隔)excludes: /system/notice/*# 匹配鏈接urlPatterns: /system/*,/monitor/*,/tool/*# Swagger配置
swagger:# 是否開啟swaggerenabled: true# 代碼生成
gen: # 作者author: ruoyi# 默認生成包路徑 system 需改成自己的模塊名稱 如 system monitor toolpackageName: com.ruoyi.project.system# 自動去除表前綴,默認是trueautoRemovePre: true# 表前綴(生成類名不會包含表前綴,多個用逗號分隔)tablePrefix: sys_
application-druid.yml
文件配置解析
application-druid.yml
文件是Spring Boot項目中用來配置Druid數據源及其相關特性的一個關鍵配置文件。Druid是一個高性能且功能豐富的數據庫連接池組件,主要用于Java應用程序,它可以提供數據庫連接管理、SQL執行監控、數據源狀態監控等功能。下面詳細解析該配置文件的作用:
1. Spring Data Source Configuration
這部分配置定義了Spring框架中數據源的詳細信息,包括:
- Type: 數據源的類型,這里指定為Druid數據源。
- Driver-Class-Name: 數據庫驅動的全限定類名,這里用于MySQL數據庫。
- URL: 數據庫的連接URL,包含數據庫服務器地址、端口、數據庫名以及一些連接參數。
- Username: 連接數據庫的用戶名。
- Password: 連接數據庫的密碼。
2. Druid DataSource Specific Configuration
這部分是Druid數據源特有的配置,主要包括:
- InitialSize: 初始創建的連接數。
- MinIdle: 最小空閑連接數。
- MaxActive: 最大連接數。
- MaxWait: 獲取連接的最大等待時間。
- TimeBetweenEvictionRunsMillis: 連接有效性檢測的時間間隔。
- MinEvictableIdleTimeMillis: 連接空閑時間超過此值則會被回收。
- ValidationQuery: 用于驗證連接有效性的SQL語句。
- TestWhileIdle: 是否在連接空閑時進行有效性檢測。
- TestOnBorrow: 是否在獲取連接時進行有效性檢測。
- TestOnReturn: 是否在歸還連接時進行有效性檢測。
- PoolPreparedStatements: 是否開啟PreparedStatement緩存。
- MaxPoolPreparedStatementPerConnectionSize: 每個連接的PreparedStatement緩存的最大數量。
- Filters: 啟用的過濾器,如監控統計(stat)、SQL防注入(wall)、日志記錄(log4j)。
3. Druid Web Monitoring Configuration
這部分配置了Druid的Web監控頁面,包括:
- Stat-View-Servlet: 監控頁面的啟用、URL模式、登錄信息、允許和拒絕訪問的IP地址。
- Web-Stat-Filter: 監控頁面的過濾規則,定義哪些URL請求會被監控,哪些會被排除。
通過這些配置,Druid不僅可以高效地管理數據庫連接,還可以提供詳細的SQL執行情況統計、數據源狀態監控等信息,對于開發和運維人員來說,是非常有價值的工具,可以幫助他們更好地理解和優化數據庫操作。同時,Druid的Web監控界面也提供了直觀的圖形化展示,使得監控和調試更加方便。
在Spring Boot項目中,application.yml
或 application.properties
是非常核心的配置文件,它們用于配置應用的各種屬性,包括但不限于數據源、日志、安全設置、外部服務的URL等。下面我將分別介紹這兩種文件的基本結構和用途:
application.yml配置
1. application.yml
application.yml
是使用YAML(YAML Ain’t Markup Language)格式的配置文件,它是一種人類可讀的數據序列化格式,特別適合用于配置文件,因為它結構清晰,易于閱讀和編寫。
示例內容:
server:port: 8080servlet:context-path: /myappspring:datasource:url: jdbc:mysql://localhost:3306/mydatabaseusername: myuserpassword: mypassworddriver-class-name: com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto: updateshow-sql: truelogging:level:root: INFOcom.example.myapp: DEBUG
在這個例子中,我們配置了服務器端口、數據源信息、JPA的Hibernate設置以及日志級別。
2. application.properties
application.properties
是使用傳統屬性文件格式的配置文件,每一行都是鍵值對的形式,以等號(=)或冒號(:)分隔。
示例內容:
server.port=8080
server.servlet.context-path=/myappspring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=myuser
spring.datasource.password=mypassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=truelogging.level.root=INFO
logging.level.com.example.myapp=DEBUG
同樣的,這個例子中包含了服務器端口、數據源信息、JPA設置和日志級別的配置。
選擇哪種格式?
- YAML 更加結構化,適合復雜嵌套的配置,且更易讀。
- Properties 更加簡單直接,適合簡單的鍵值對配置。
在Spring Boot中,你可以自由選擇使用application.yml
或application.properties
,Spring Boot會自動識別并加載配置。在大型項目中,使用application.yml
可能會更加直觀和便于維護。
無論選擇哪一種,這些配置文件都扮演著極其重要的角色,它們幫助Spring Boot應用動態地配置和適應不同的運行環境,比如開發、測試和生產環境。
java連接mysql(jdbk)文字詳細說明
使用Java連接MySQL數據庫通常涉及JDBC(Java Database Connectivity),這是一個Java API,允許客戶端應用程序連接到各種類型的數據庫。以下是使用JDBC連接到MySQL數據庫的詳細步驟:
步驟一:準備MySQL JDBC驅動程序
首先,你需要下載適用于MySQL的JDBC驅動程序,即MySQL Connector/J
。你可以從MySQL官方網站或Maven中央倉庫下載這個驅動程序。如果你使用的是Maven或Gradle等構建工具,你只需要在你的pom.xml
或build.gradle
文件中添加相應的依賴。
Maven依賴示例:
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version> <!-- 確保使用最新版本 -->
</dependency>
Gradle依賴示例:
implementation 'mysql:mysql-connector-java:8.0.30'
步驟二:導入必要的類
在你的Java代碼中,你需要導入JDBC API中的必要類。典型的導入語句如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
步驟三:建立數據庫連接
使用DriverManager
類的getConnection()
方法建立與MySQL數據庫的連接。你將需要提供數據庫的URL、用戶名和密碼。URL通常格式如下:
String url = "jdbc:mysql://hostname:port/databaseName";
String username = "yourUsername";
String password = "yourPassword";
然后,你可以使用以下代碼建立連接:
Connection conn = DriverManager.getConnection(url, username, password);
步驟四:執行SQL語句
一旦你有了連接,你可以創建Statement
或PreparedStatement
對象來執行SQL語句。例如:
Statement stmt = conn.createStatement();
String sql = "SELECT * FROM tableName";
ResultSet rs = stmt.executeQuery(sql);
步驟五:處理結果集
你可以遍歷ResultSet
對象來處理查詢結果。例如:
while(rs.next()) {System.out.println(rs.getString("columnName"));
}
步驟六:關閉連接
在完成所有數據庫操作后,確保關閉ResultSet
、Statement
和Connection
以釋放資源:
rs.close();
stmt.close();
conn.close();
注意事項:
- 確保在處理異常時使用
try-catch
塊。 - 使用
PreparedStatement
而不是Statement
可以防止SQL注入攻擊,并且在處理參數化查詢時更加高效。 - 在實際生產環境中,考慮使用連接池來管理數據庫連接,以提高性能和資源利用效率。
以上步驟將幫助你使用Java和JDBC成功連接到MySQL數據庫。
Tomcat 說明
Apache Tomcat 是一個開源的輕量級Web應用服務器,主要功能是運行基于Java的Web應用,尤其是那些使用Servlet和JavaServer Pages (JSP)技術的應用。Tomcat由Apache Software Foundation (ASF)維護,是Jakarta項目的一部分。以下是Tomcat的主要作用和特點:
-
Servlet和JSP容器:
Tomcat本質上是一個Servlet容器,能夠執行Java Servlet規范中的Servlet和JSP頁面。Servlet是一種運行在服務器端的小程序,用于處理HTTP請求并生成響應。JSP則是用于生成動態HTML頁面的一種技術。 -
HTTP服務器:
盡管Tomcat主要作為Servlet容器運行,但它也具備基本的HTTP服務器功能,能夠處理靜態HTML、圖像和其他非Java內容。然而,它的靜態文件處理能力通常不如專門的Web服務器如Apache HTTP Server或Nginx。 -
輕量級和靈活:
Tomcat設計為輕量級,適用于開發和小型至中型規模的生產環境。它易于安裝和配置,非常適合快速搭建Java Web應用的開發和測試環境。 -
支持現代Web技術:
Tomcat支持最新的Servlet和JSP規范,這使得開發者能夠使用最新的Java Web開發技術。它還支持WebSocket、HTTP/2等現代Web通信協議。 -
擴展性和插件支持:
可以通過添加各種連接器和Valves來擴展Tomcat的功能,如SSL支持、負載均衡、身份驗證等。這使得Tomcat可以根據具體需求進行定制。 -
開源和免費:
Tomcat是開源軟件,遵循Apache License 2.0許可,這意味著它是免費的,且其源代碼可以自由地查看、修改和分發。 -
監控和管理:
提供了管理控制臺,允許管理員遠程管理服務器,包括啟動、停止應用,配置服務器屬性,以及監控服務器狀態和性能。 -
跨平臺:
Tomcat可以在多種操作系統上運行,包括Windows、Linux、macOS等,這得益于它基于Java的架構。 -
集群和高可用性:
支持集群部署,可以將多個Tomcat實例配置為一個集群,以提高性能和實現故障轉移。 -
易于集成:
Tomcat可以與其他Web服務器如Apache HTTP Server或IIS一起使用,通過配置適當的代理或反向代理設置,可以讓這些Web服務器前端處理靜態內容,而后端的Tomcat專注于動態內容的生成。
總之,Tomcat是一個強大的、廣泛使用的Java Web應用服務器,它不僅適合開發階段的快速迭代,也適合在生產環境中部署和運行各種規模的Java Web應用。
詳細解釋驗證碼模塊(題庫管理系統)
這段代碼是Spring框架下對驗證碼生成模塊的配置,主要使用了Google的Kaptcha庫來生成圖形驗證碼。下面是對代碼中各個部分的詳細解釋:
1. 類定義和注解
@Configuration
public class CaptchaConfig {
@Configuration
注解表明這是一個配置類,Spring會掃描并讀取此類中的@Bean注解來初始化bean。
2. 創建默認驗證碼生成器
@Bean(name = "captchaProducer")
public DefaultKaptcha getKaptchaBean() {
@Bean
注解用于聲明一個由Spring管理的bean。這里初始化了一個DefaultKaptcha
實例,用來生成普通的驗證碼。properties
是一個Properties
對象,用于存儲Kaptcha的各種配置參數,如邊框、顏色、尺寸、樣式等。KAPTCHA_BORDER
,KAPTCHA_TEXTPRODUCER_FONT_COLOR
,KAPTCHA_IMAGE_WIDTH
,KAPTCHA_IMAGE_HEIGHT
,KAPTCHA_TEXTPRODUCER_FONT_SIZE
,KAPTCHA_SESSION_CONFIG_KEY
,KAPTCHA_TEXTPRODUCER_CHAR_LENGTH
,KAPTCHA_TEXTPRODUCER_FONT_NAMES
,KAPTCHA_OBSCURIFICATOR_IMPL
等都是Kaptcha庫的配置鍵,用于控制驗證碼的外觀和行為。
3. 創建數學表達式驗證碼生成器
@Bean(name = "captchaProducerMath")
public DefaultKaptcha getKaptchaBeanMath() {
- 這個bean用于生成包含數學表達式的驗證碼,通常用于增加破解難度。
- 除了基本的外觀配置外,這里還設置了數學表達式的文本生成器
KAPTCHA_TEXTPRODUCER_IMPL
,指向自定義的類com.ruoyi.framework.config.KaptchaTextCreator
,這應該是一個實現了Kaptcha的文本生產者接口的類,負責生成數學表達式字符串。
4. 設置配置并返回bean
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
Config
類用于將Properties
對象轉換成Kaptcha可識別的配置,然后設置給DefaultKaptcha
實例。
總結
這段代碼展示了如何通過Spring框架配置Kaptcha庫來生成兩種不同類型的驗證碼:一種是普通的字符驗證碼,另一種是包含數學表達式的驗證碼。通過不同的@Bean
定義,可以根據需要靈活地在應用中使用這些驗證碼生成器。此外,通過自定義KaptchaTextCreator
類,還可以進一步定制驗證碼的內容生成邏輯,以滿足特定的安全需求。
Java實現驗證碼模塊
/*** 驗證碼配置* * @author ruoyi*/
package com.ruoyi.framework.config;import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import static com.google.code.kaptcha.Constants.*;
@Configuration
public class CaptchaConfig
{@Bean(name = "captchaProducer")public DefaultKaptcha getKaptchaBean(){DefaultKaptcha defaultKaptcha = new DefaultKaptcha();Properties properties = new Properties();// 是否有邊框 默認為true 我們可以自己設置yes,noproperties.setProperty(KAPTCHA_BORDER, "yes");// 驗證碼文本字符顏色 默認為Color.BLACKproperties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");// 驗證碼圖片寬度 默認為200properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");// 驗證碼圖片高度 默認為50properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");// 驗證碼文本字符大小 默認為40properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");// KAPTCHA_SESSION_KEYproperties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");// 驗證碼文本字符長度 默認為5properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");// 驗證碼文本字體樣式 默認為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");// 圖片樣式 水紋com.google.code.kaptcha.impl.WaterRipple 魚眼com.google.code.kaptcha.impl.FishEyeGimpy 陰影com.google.code.kaptcha.impl.ShadowGimpyproperties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");Config config = new Config(properties);defaultKaptcha.setConfig(config);return defaultKaptcha;}@Bean(name = "captchaProducerMath")public DefaultKaptcha getKaptchaBeanMath(){DefaultKaptcha defaultKaptcha = new DefaultKaptcha();Properties properties = new Properties();// 是否有邊框 默認為true 我們可以自己設置yes,noproperties.setProperty(KAPTCHA_BORDER, "yes");// 邊框顏色 默認為Color.BLACKproperties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");// 驗證碼文本字符顏色 默認為Color.BLACKproperties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");// 驗證碼圖片寬度 默認為200properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");// 驗證碼圖片高度 默認為50properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");// 驗證碼文本字符大小 默認為40properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");// KAPTCHA_SESSION_KEYproperties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");// 驗證碼文本生成器properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator");// 驗證碼文本字符間距 默認為2properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");// 驗證碼文本字符長度 默認為5properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");// 驗證碼文本字體樣式 默認為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");// 驗證碼噪點顏色 默認為Color.BLACKproperties.setProperty(KAPTCHA_NOISE_COLOR, "white");// 干擾實現類properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");// 圖片樣式 水紋com.google.code.kaptcha.impl.WaterRipple 魚眼com.google.code.kaptcha.impl.FishEyeGimpy 陰影com.google.code.kaptcha.impl.ShadowGimpyproperties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");Config config = new Config(properties);defaultKaptcha.setConfig(config);return defaultKaptcha;}
}
提供的代碼是使用Java中的Kaptcha庫設置驗證碼生成的Spring配置類。以下是代碼的詳細分解:
類定義和注解
CaptchaConfig
類被 @Configuration
注解標記,表明這是一個配置類,Spring將從這個類中讀取bean的定義并注冊到應用上下文中。
Bean定義
1. captchaProducer
這個方法定義了一個名為 captchaProducer
的bean,該bean是一個 DefaultKaptcha
實例。它通過一系列屬性來定制驗證碼的外觀和行為,包括邊框、文本顏色、圖片尺寸、字體大小、session key、字符長度、字體樣式、背景樣式等。這些屬性被封裝在 Properties
對象中,并通過 Config
類設置給 DefaultKaptcha
。
2. captchaProducerMath
另一個方法定義了名為 captchaProducerMath
的bean,同樣是 DefaultKaptcha
的實例。這個bean與前一個類似,但是它有一些不同的配置,比如邊框顏色、文本顏色、文本生成器(這里指定為自定義的 KaptchaTextCreator
類)、字符間距、噪點顏色和噪點實現類。這通常用于數學運算型的驗證碼生成。
總結
這段代碼的作用是為應用提供兩個不同配置的驗證碼生成器,可以根據場景選擇使用。例如,一個可能用于簡單的文本驗證碼,而另一個可能用于需要數學計算的驗證碼場景。這樣的配置靈活性有助于提高網站的安全性和用戶體驗。
package com.ruoyi.framework.config;import java.security.SecureRandom;
import java.util.Random;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;/*** 驗證碼文本生成器* * @author ruoyi*/
public class KaptchaTextCreator extends DefaultTextCreator
{private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");// 生成驗證碼文本的方法@Overridepublic String getText(){Integer result = 0;Random random = new SecureRandom();int x = random.nextInt(10);int y = random.nextInt(10);StringBuilder suChinese = new StringBuilder();int randomoperands = (int) Math.round(Math.random() * 2);if (randomoperands == 0){result = x * y;suChinese.append(CNUMBERS[x]);suChinese.append("*");suChinese.append(CNUMBERS[y]);}else if (randomoperands == 1){if (!(x == 0) && y % x == 0){result = y / x;suChinese.append(CNUMBERS[y]);suChinese.append("/");suChinese.append(CNUMBERS[x]);}else{result = x + y;suChinese.append(CNUMBERS[x]);suChinese.append("+");suChinese.append(CNUMBERS[y]);}}else if (randomoperands == 2){if (x >= y){result = x - y;suChinese.append(CNUMBERS[x]);suChinese.append("-");suChinese.append(CNUMBERS[y]);}else{result = y - x;suChinese.append(CNUMBERS[y]);suChinese.append("-");suChinese.append(CNUMBERS[x]);}}else{result = x + y;suChinese.append(CNUMBERS[x]);suChinese.append("+");suChinese.append(CNUMBERS[y]);}suChinese.append("=?@" + result);return suChinese.toString();}
}
這段代碼定義了一個名為 KaptchaTextCreator
的類,它繼承自 DefaultTextCreator
,主要功能是生成數學運算符形式的驗證碼文本。這個類主要用于配合Kaptcha庫生成包含簡單算術問題的驗證碼圖片,以增加驗證碼的復雜度和安全性。下面是代碼的詳細解釋:
類定義
- 包名:
com.ruoyi.framework.config
表示這是RuoYi框架的一部分,位于其配置目錄下。 - 類名:
KaptchaTextCreator
繼承自DefaultTextCreator
,意味著它重寫了父類的一些方法,特別是getText()
方法,用于生成驗證碼文本。
屬性
- CNUMBERS:一個字符串數組,包含了0到10的數字字符串,用于生成隨機數和構建算術表達式。
方法
-
getText():此方法是核心,它負責生成具體的驗證碼文本。方法內部首先生成兩個介于0至9之間的隨機整數
x
和y
,然后基于隨機選擇的運算類型(乘法、除法或加減法),構建一個算術表達式,并計算出結果。具體過程如下:- 如果運算符類型是0,則生成乘法表達式。
- 如果是1,嘗試生成除法表達式,如果
y
不是x
的倍數則退化為加法表達式。 - 如果是2,則根據
x
和y
的大小生成加法或減法表達式。 - 最后,如果沒有以上情況匹配,則默認生成加法表達式。
-
在生成表達式的同時,將結果也拼接到表達式后面,并以特殊符號
=?@
分隔,以便在用戶完成計算后可以驗證答案是否正確。
總結
通過這種方式,KaptchaTextCreator
類可以生成如 2*3=?@6
或者 5+4=?@9
這樣的驗證碼文本,這比單純的字母或數字驗證碼更難被自動破解程序識別,提高了網站的安全性。同時,由于包含了運算過程,這種類型的驗證碼對真實用戶來說仍然相對友好且易于解決。
用戶權限設置
sys_user
sys_post
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head><th:block th:include="include :: header('用戶分配角色')" />
</head>
<body><div class="main-content"><form id="form-user-add" class="form-horizontal"><input type="hidden" id="userId" name="userId" th:value="${user.userId}"><h4 class="form-header h4">基本信息</h4><div class="row"><div class="col-sm-6"><div class="form-group"><label class="col-sm-4 control-label is-required">用戶名稱:</label><div class="col-sm-8"><input name="userName" class="form-control" type="text" disabled th:value="${user.userName}"></div></div></div><div class="col-sm-6"><div class="form-group"><label class="col-sm-4 control-label is-required">登錄賬號:</label><div class="col-sm-8"><input name="loginName" class="form-control" type="text" disabled th:value="${user.loginName}"></div></div></div> </div><h4 class="form-header h4">分配角色</h4><div class="row"><div class="col-sm-12"><div class="col-sm-12 select-table table-striped"><table id="bootstrap-table"></table></div></div></div></form></div><div class="row"><div class="col-sm-offset-5 col-sm-10"><button type="button" class="btn btn-sm btn-primary" onclick="submitHandler()"><i class="fa fa-check"></i>保 存</button> <button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>關 閉 </button></div></div><th:block th:include="include :: footer" /><script th:inline="javascript">var prefix = ctx + "system/user/authRole";var roles = [[${roles}]]$(function() {var options = {data: roles,sidePagination: "client",sortName: "roleSort",showSearch: false,showRefresh: false,showToggle: false,showColumns: false,clickToSelect: true,maintainSelected: true,columns: [{checkbox: true,formatter:function (value, row, index) {if($.common.isEmpty(value)) {return { checked: row.flag };} else {return { checked: value }}}},{field: 'roleId',title: '角色編號'},{field: 'roleSort',title: '排序',sortable: true,visible: false},{field: 'roleName',title: '角色名稱'},{field: 'roleKey',title: '權限字符',sortable: true},{field: 'createTime',title: '創建時間',sortable: true}]};$.table.init(options);});/* 添加角色-提交 */function submitHandler(index, layero){var rows = $.table.selectFirstColumns();var data = { "userId": $("#userId").val(), "roleIds": rows.join() };$.operate.saveTab(prefix + "/insertAuthRole", data);}</script>
</body>
</html>
Java實現具體頁面表格分頁處理器
package com.ruoyi.framework.web.page;import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.text.Convert;/*** 表格數據處理* * @author ruoyi*/
public class TableSupport
{/*** 當前記錄起始索引*/public static final String PAGE_NUM = "pageNum";/*** 每頁顯示記錄數*/public static final String PAGE_SIZE = "pageSize";/*** 排序列*/public static final String ORDER_BY_COLUMN = "orderByColumn";/*** 排序的方向 "desc" 或者 "asc".*/public static final String IS_ASC = "isAsc";/*** 分頁參數合理化*/public static final String REASONABLE = "reasonable";/*** 封裝分頁對象*/public static PageDomain getPageDomain(){PageDomain pageDomain = new PageDomain();pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 1));pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));return pageDomain;}public static PageDomain buildPageRequest(){return getPageDomain();}
}
這段代碼展示了如何在Java Web應用中實現分頁功能,特別是在處理表格數據時。TableSupport
類提供了一系列靜態方法來封裝和解析與分頁相關的請求參數,從而簡化了前端和后端之間分頁數據的交互。下面是對代碼的具體分析:
類定義和屬性
-
類名:
TableSupport
,這是一個公共的工具類,用于支持表格數據的分頁處理。 -
常量:定義了多個字符串常量,用于標識HTTP請求中的分頁參數:
PAGE_NUM
:當前頁碼PAGE_SIZE
:每頁顯示的記錄數ORDER_BY_COLUMN
:排序依據的列名IS_ASC
:排序方向,true
代表升序,false
代表降序REASONABLE
:是否啟用分頁參數的合理性檢查,例如防止非法的頁碼或每頁記錄數
方法
-
getPageDomain():該方法從HTTP請求中讀取分頁相關的參數,并使用這些參數初始化并返回一個
PageDomain
對象。PageDomain
是一個自定義的類,用于封裝分頁參數。Convert.toInt()
方法用于將字符串轉換成整數,如果轉換失敗或參數不存在,則使用默認值(通常是1)。 -
buildPageRequest():這是
getPageDomain()
的別名,提供了相同的邏輯和功能,用于構建分頁請求。
使用場景
當用戶請求帶有分頁參數的表格數據時,前端會將上述參數(如頁碼、每頁數量、排序字段等)作為查詢字符串發送給服務器。TableSupport
類中的方法會被調用來解析這些參數,并創建一個 PageDomain
對象。這個對象隨后會被傳遞給數據查詢邏輯,用于限制數據庫查詢的結果集大小以及指定查詢的排序方式。
總結
TableSupport
類通過提供統一的接口來處理分頁參數,使得開發者無需在每個涉及分頁的數據處理方法中重復編寫解析參數的代碼。這不僅減少了代碼的冗余,也提高了代碼的可維護性和可讀性。在實際應用中,這有助于快速構建高效且用戶友好的數據展示界面。
登陸校驗方法
package com.ruoyi.framework.shiro.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.UserBlockedException;
import com.ruoyi.common.exception.user.UserDeleteException;
import com.ruoyi.common.exception.user.UserNotExistsException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.project.system.user.domain.User;
import com.ruoyi.project.system.user.domain.UserStatus;
import com.ruoyi.project.system.user.service.IUserService;/*** 登錄校驗方法* * @author ruoyi*/
@Component
public class LoginService
{@Autowiredprivate PasswordService passwordService;@Autowiredprivate IUserService userService;/*** 登錄*/public User login(String username, String password){// 驗證碼校驗if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));throw new CaptchaException();}// 用戶名或密碼為空 錯誤if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));throw new UserNotExistsException();}// 密碼如果不在指定范圍內 錯誤if (password.length() < UserConstants.PASSWORD_MIN_LENGTH|| password.length() > UserConstants.PASSWORD_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}// 用戶名不在指定范圍內 錯誤if (username.length() < UserConstants.USERNAME_MIN_LENGTH|| username.length() > UserConstants.USERNAME_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}// 查詢用戶信息User user = userService.selectUserByLoginName(username);/** if (user == null && maybeMobilePhoneNumber(username)){user = userService.selectUserByPhoneNumber(username);}if (user == null && maybeEmail(username)){user = userService.selectUserByEmail(username);}*/if (user == null){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));throw new UserNotExistsException();}if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));throw new UserDeleteException();}if (UserStatus.DISABLE.getCode().equals(user.getStatus())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark())));throw new UserBlockedException();}passwordService.validate(user, password);AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));recordLoginInfo(user.getUserId());return user;}/**private boolean maybeEmail(String username){if (!username.matches(UserConstants.EMAIL_PATTERN)){return false;}return true;}private boolean maybeMobilePhoneNumber(String username){if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN)){return false;}return true;}*//*** 記錄登錄信息** @param userId 用戶ID*/public void recordLoginInfo(Long userId){User user = new User();user.setUserId(userId);user.setLoginIp(ShiroUtils.getIp());user.setLoginDate(DateUtils.getNowDate());userService.updateUserInfo(user);}
}
網頁圖標登錄頁面標題設置
這段代碼展示了如何在Java Web應用中實現分頁功能,特別是在處理表格數據時。TableSupport
類提供了一系列靜態方法來封裝和解析與分頁相關的請求參數,從而簡化了前端和后端之間分頁數據的交互。下面是對代碼的具體分析:
類定義和屬性
-
類名:
TableSupport
,這是一個公共的工具類,用于支持表格數據的分頁處理。 -
常量:定義了多個字符串常量,用于標識HTTP請求中的分頁參數:
PAGE_NUM
:當前頁碼PAGE_SIZE
:每頁顯示的記錄數ORDER_BY_COLUMN
:排序依據的列名IS_ASC
:排序方向,true
代表升序,false
代表降序REASONABLE
:是否啟用分頁參數的合理性檢查,例如防止非法的頁碼或每頁記錄數
方法
-
getPageDomain():該方法從HTTP請求中讀取分頁相關的參數,并使用這些參數初始化并返回一個
PageDomain
對象。PageDomain
是一個自定義的類,用于封裝分頁參數。Convert.toInt()
方法用于將字符串轉換成整數,如果轉換失敗或參數不存在,則使用默認值(通常是1)。 -
buildPageRequest():這是
getPageDomain()
的別名,提供了相同的邏輯和功能,用于構建分頁請求。
使用場景
當用戶請求帶有分頁參數的表格數據時,前端會將上述參數(如頁碼、每頁數量、排序字段等)作為查詢字符串發送給服務器。TableSupport
類中的方法會被調用來解析這些參數,并創建一個 PageDomain
對象。這個對象隨后會被傳遞給數據查詢邏輯,用于限制數據庫查詢的結果集大小以及指定查詢的排序方式。
總結
TableSupport
類通過提供統一的接口來處理分頁參數,使得開發者無需在每個涉及分頁的數據處理方法中重復編寫解析參數的代碼。這不僅減少了代碼的冗余,也提高了代碼的可維護性和可讀性。在實際應用中,這有助于快速構建高效且用戶友好的數據展示界面。
增刪改查通用變量函數設置
package com.ruoyi.common.constant;
/**
-
權限通用常量
-
@author ruoyi
/
public class PermissionConstants
{
/* 新增權限 */
public static final String ADD_PERMISSION = “add”;/** 修改權限 */
public static final String EDIT_PERMISSION = “edit”;/** 刪除權限 */
public static final String REMOVE_PERMISSION = “remove”;/** 導出權限 */
public static final String EXPORT_PERMISSION = “export”;/** 顯示權限 */
public static final String VIEW_PERMISSION = “view”;/** 查詢權限 */
public static final String LIST_PERMISSION = “list”;
}
文件組件
這個文件夾結構看起來像是一個Java項目中的工具類集合,其中包含了許多實用工具類,分別針對不同的任務進行優化。以下是對每個子文件夾及其內容的簡要說明:
- bean: 可能包含一些通用的Java Bean輔助類或者容器相關的工具類。
- file: 文件操作相關的工具類,可能用于讀寫文件、文件路徑處理等功能。
- html: HTML相關工具類,可能用于HTML解析、生成或者其他HTML相關的操作。
- http: HTTP相關的工具類,可能包含HTTP請求、響應處理等。
- poi: Apache POI庫的擴展或輔助類,用于處理Excel、Word等Office文檔格式。
- reflect: 反射相關的工具類,可能用于動態獲取或修改類的信息、屬性、方法等。
- security: 安全相關的工具類,可能包含加密解密、權限校驗等功能。
- spring: Spring框架相關的工具類,可能包含Spring Bean的輔助操作或其他Spring生態系統的集成。
- sql: SQL相關的工具類,可能包含數據庫連接、SQL語句生成、執行等功能。
- text: 文本處理相關的工具類,可能包含字符串操作、正則表達式處理等。
- uuid: UUID相關的工具類,可能用于生成唯一ID。
- AddressUtils: 地址相關的工具類,可能用于地址解析、編碼、地理定位等功能。
這些工具類通常是為了復用和簡化開發工作而設計的,它們可以幫助開發者避免重復編寫常見的代碼片段,提高代碼質量和效率。
JAVA中sql語句檢查代碼
package com.ruoyi.common.utils.sql;import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.StringUtils;/*** sql操作工具類* * @author ruoyi*/
public class SqlUtil
{/*** 定義常用的 sql關鍵字*/public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";/*** 僅支持字母、數字、下劃線、空格、逗號、小數點(支持多個字段排序)*/public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";/*** 檢查字符,防止注入繞過*/public static String escapeOrderBySql(String value){if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)){throw new UtilException("參數不符合規范,不能進行查詢");}return value;}/*** 驗證 order by 語法是否符合規范*/public static boolean isValidOrderBySql(String value){return value.matches(SQL_PATTERN);}/*** SQL關鍵字檢查*/public static void filterKeyword(String value){if (StringUtils.isEmpty(value)){return;}String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");for (String sqlKeyword : sqlKeywords){if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1){throw new UtilException("參數存在SQL注入風險");}}}
}
SqlUtil
類是設計用于Java應用程序中SQL操作的工具類,它包含防止SQL注入攻擊和驗證SQL相關字符串的方法。下面是其功能的詳細解析:
-
SQL_KEYWORDS: 一個定義了常見SQL關鍵詞的正則表達式字符串。這個列表可以用來檢查任何用戶提供的字符串是否包含這些關鍵詞,如果有,可能表示存在SQL注入的風險。
-
SQL_PATTERN: 一個正則表達式模式,指定了在order-by子句或類似的SQL語法中允許的字符。它允許字母、數字、下劃線、空格、逗號和句點。這個模式用于在將其用于SQL查詢之前驗證用戶輸入。
-
escapeOrderBySql(String value): 這個方法檢查提供的字符串(
value
)是否符合SQL_PATTERN
。如果不符,將拋出UtilException
,指示參數不符合規范。這個方法對于清洗打算用于order-by子句的輸入很有用。 -
isValidOrderBySql(String value): 驗證提供的字符串(
value
)是否匹配SQL_PATTERN
。如果匹配則返回true
,否則返回false
。這是一個輔助方法,被escapeOrderBySql
所使用。 -
filterKeyword(String value): 檢查給定的字符串(
value
)是否包含任何定義的SQL關鍵詞。如果包含,將拋出UtilException
,表明存在潛在的SQL注入風險。這個方法可以在將輸入包含在SQL語句之前用于清洗輸入。
這些方法通過驗證和清洗輸入提供了基本的SQL注入防護。然而,值得注意的是,這種驗證不應是抵御SQL注入的唯一防線。使用預編譯語句或參數化查詢是處理動態數據時更安全的做法,因為它們可以有效防止SQL注入攻擊。在實際應用中,結合使用這些方法和其他安全實踐,如輸入驗證和輸出編碼,能夠提供更全面的安全保障。
這個文件夾結構似乎包含了一些HTML文件,可能是某個Web項目的視圖層(View)部分。這些文件可能對應著不同的用戶管理功能,例如:
- avatar.html: 用戶頭像編輯或上傳頁面。
- profile.html: 用戶個人資料頁面。
- resetPwd.html: 用戶密碼重置頁面。
- add.html: 新增用戶頁面。
- authRole.html: 用戶角色授權頁面。
- edit.html: 用戶編輯頁面。
- user.html: 用戶列表或概覽頁面。
這些HTML文件可能與后端控制器(Controller)關聯,用于呈現用戶管理的相關功能。例如,avatar.html
可能用于讓用戶上傳或更改他們的頭像,profile.html
顯示用戶的個人信息,resetPwd.html
允許用戶重設密碼,等等。這些文件通常與后端服務一起工作,接收來自控制器的數據,并將其渲染為用戶可見的界面。
課程管理 課程信息
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><th:block th:include="include :: header('學生信息列表')" />
</head>
<body class="gray-bg"><div class="container-div"><div class="row"><div class="col-sm-12 search-collapse"><form id="formId"><div class="select-list"><ul><li><label>課程名稱:</label><input type="text" name="sname"/></li><li><label>章節:</label><input type="text" name="sid"/></li><li><label>關鍵字:</label><select name="ssex" th:with="type=${@dict.getType('sys_user_sex')}"><option value="">所有</option><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option></select></li><li><a class="btn btn-primary btn-rounded btn-sm" onclick="$.treeTable.search()"><i class="fa fa-search"></i> 搜索</a><a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a></li></ul></div></form></div><div class="btn-group-sm" id="toolbar" role="group"><a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="system:student:add"><i class="fa fa-plus"></i> 新增</a><a class="btn btn-primary" onclick="$.operate.edit()" shiro:hasPermission="system:student:edit"><i class="fa fa-edit"></i> 修改</a><a class="btn btn-info" id="expandAllBtn"><i class="fa fa-exchange"></i> 展開/折疊</a></div><div class="col-sm-12 select-table table-striped"><table id="bootstrap-tree-table"></table></div></div></div><th:block th:include="include :: footer" /><script th:inline="javascript">var addFlag = [[${@permission.hasPermi('system:student:add')}]];var editFlag = [[${@permission.hasPermi('system:student:edit')}]];var removeFlag = [[${@permission.hasPermi('system:student:remove')}]];var ssexDatas = [[${@dict.getType('sys_user_sex')}]];var prefix = ctx + "system/student";$(function() {var options = {code: "id",parentCode: "parentId",expandColumn: "1",uniqueId: "id",url: prefix + "/list",createUrl: prefix + "/add/{id}",updateUrl: prefix + "/edit/{id}",removeUrl: prefix + "/remove/{id}",modalName: "學生信息",columns: [{field: 'selectItem',radio: true},{field: 'sname',title: '課程名稱',align: 'left'},{field: 'sid',title: '章節號',align: 'left'},{field: 'ssex',title: '課程編號',align: 'left',formatter: function(value, row, index) {return $.table.selectDictLabel(ssexDatas, value);}},{title: '',align: 'center',align: 'left',formatter: function(value, row, index) {var actions = [];return actions.join('');}}]};$.treeTable.init(options);});</script>
</body>
</html>
這段HTML代碼是一個使用Thymeleaf模板引擎和Shiro權限管理框架的Web頁面,主要功能是展示和管理學生信息列表。下面是對各個部分的詳細解釋:
頁面頭部
<!DOCTYPE html>
:聲明文檔類型。<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
:定義HTML文檔語言為中文,并引入Thymeleaf和Shiro的命名空間,用于模板表達式和權限控制。<th:block th:include="include :: header('學生信息列表')"/>
:使用Thymeleaf的th:include
指令來包含其他模板文件中的頭部區域,標題設置為“學生信息列表”。
主體部分
<body class="gray-bg">
:定義頁面主體的背景色。<div class="container-div">
:包含整個頁面內容的容器。<div class="col-sm-12 search-collapse">
:搜索表單區域,包含課程名稱、章節、關鍵字的輸入框和搜索、重置按鈕。<div class="btn-group-sm" id="toolbar" role="group">
:操作按鈕區域,有新增、修改、展開/折疊功能的按鈕,且按鈕的顯示受Shiro權限控制。<div class="col-sm-12 select-table table-striped">
:包含一個表格ID為bootstrap-tree-table
,用于展示學生信息。
JavaScript腳本
<script th:inline="javascript">
:內聯JavaScript腳本,使用Thymeleaf表達式來動態獲取權限和數據。var addFlag = [[${@permission.hasPermi('system:student:add')}]];
:檢查是否有添加學生的權限。var editFlag = [[${@permission.hasPermi('system:student:edit')}]];
:檢查是否有編輯學生的權限。var removeFlag = [[${@permission.hasPermi('system:student:remove')}]];
:檢查是否有刪除學生的權限。var ssexDatas = [[${@dict.getType('sys_user_sex')}]];
:獲取性別字典數據。$(function() { ... });
:jQuery文檔加載完成后執行的函數,初始化樹形表格組件,設置URLs和表格列信息,包括課程名稱、章節號、課程編號等,其中課程編號列使用字典數據進行格式化顯示。
結構與功能
- 頁面布局清晰,包括搜索表單、操作按鈕和數據展示表格。
- 使用Thymeleaf表達式動態填充數據,如權限判斷和字典數據獲取。
- 通過JavaScript腳本初始化表格組件,設置URLs和列信息,實現動態數據加載和權限控制下的操作功能。
整體而言,這個頁面設計用于學生信息管理,提供了搜索、新增、編輯功能,并且通過Shiro框架實現了細粒度的權限控制,確保只有具有相應權限的用戶才能執行特定的操作。
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><th:block th:include="include :: header('學生信息列表')" />
</head>
<body class="gray-bg"><div class="container-div"><div class="row"><div class="col-sm-12 search-collapse"><form id="formId"><div class="select-list"><ul><li><label>姓名:</label><input type="text" name="sname"/></li><li><label>學號:</label><input type="text" name="sid"/></li><li><label>性別:</label><select name="ssex" th:with="type=${@dict.getType('sys_user_sex')}"><option value="">所有</option><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option></select></li><li><a class="btn btn-primary btn-rounded btn-sm" onclick="$.treeTable.search()"><i class="fa fa-search"></i> 搜索</a><a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a></li></ul></div></form></div><div class="btn-group-sm" id="toolbar" role="group"><a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="system:student:add"><i class="fa fa-plus"></i> 新增</a><a class="btn btn-primary" onclick="$.operate.edit()" shiro:hasPermission="system:student:edit"><i class="fa fa-edit"></i> 修改</a><a class="btn btn-info" id="expandAllBtn"><i class="fa fa-exchange"></i> 展開/折疊</a></div><div class="col-sm-12 select-table table-striped"><table id="bootstrap-tree-table"></table></div></div></div><th:block th:include="include :: footer" /><script th:inline="javascript">var addFlag = [[${@permission.hasPermi('system:student:add')}]];var editFlag = [[${@permission.hasPermi('system:student:edit')}]];var removeFlag = [[${@permission.hasPermi('system:student:remove')}]];var ssexDatas = [[${@dict.getType('sys_user_sex')}]];var prefix = ctx + "system/student";$(function() {var options = {code: "id",parentCode: "parentId",expandColumn: "1",uniqueId: "id",url: prefix + "/list",createUrl: prefix + "/add/{id}",updateUrl: prefix + "/edit/{id}",removeUrl: prefix + "/remove/{id}",modalName: "學生信息",columns: [{field: 'selectItem',radio: true},{field: 'sname',title: '姓名',align: 'left'},{field: 'sid',title: '學號',align: 'left'},{field: 'ssex',title: '性別',align: 'left',formatter: function(value, row, index) {return $.table.selectDictLabel(ssexDatas, value);}},{title: '操作',align: 'center',align: 'left',formatter: function(value, row, index) {var actions = [];actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" οnclick="$.operate.edit(\'' + row.id + '\')"><i class="fa fa-edit"></i>編輯</a> ');actions.push('<a class="btn btn-info btn-xs ' + addFlag + '" href="javascript:void(0)" οnclick="$.operate.add(\'' + row.id + '\')"><i class="fa fa-plus"></i>新增</a> ');actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" οnclick="$.operate.remove(\'' + row.id + '\')"><i class="fa fa-remove"></i>刪除</a>');return actions.join('');}}]};$.treeTable.init(options);});</script>
</body>
</html>
這段HTML代碼展示了一個使用Thymeleaf模板引擎和Shiro權限框架的Web頁面,主要功能是展示學生信息列表并提供搜索、編輯、添加和刪除操作。下面對代碼進行詳細的中文解釋:
頁面結構
<!DOCTYPE html>
:定義文檔類型為HTML5。<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
:聲明頁面語言為中文,并引入Thymeleaf和Shiro的命名空間。<head>
:頁面頭部區域,通過<th:block th:include="include :: header('學生信息列表')"/>
包含頭部信息,設置標題為“學生信息列表”。
頁面主體
<body class="gray-bg">
:設置頁面背景顏色。<div class="container-div">
:容器div,包裹整個頁面內容。<div class="row">
:行div,用于布局。<div class="col-sm-12 search-collapse">
:搜索欄,包含姓名、學號輸入框及性別下拉選擇,其中性別選項通過Thymeleaf表達式th:each
循環填充。<div class="btn-group-sm" id="toolbar" role="group">
:工具欄,包含新增、修改、展開/折疊按鈕,其中新增和修改按鈕的顯示受Shiro權限控制。<div class="col-sm-12 select-table table-striped">
:表格容器,其中<table id="bootstrap-tree-table"></table>
為數據展示表格。
動態腳本
<script th:inline="javascript">
:內嵌JavaScript腳本,通過Thymeleaf表達式獲取權限標志和性別數據。- 初始化變量
addFlag
、editFlag
、removeFlag
用于檢查用戶是否有添加、編輯、刪除的權限,ssexDatas
用于存儲性別數據。 - 設置URL前綴
prefix
,用于構建不同的操作URL。 - 使用jQuery和自定義的
$.treeTable.init(options)
函數初始化表格,配置表格列信息,包括姓名、學號、性別和操作列。其中性別列通過formatter
函數使用性別數據進行格式化,操作列根據用戶權限顯示編輯、新增、刪除按鈕。
功能描述
- 搜索功能:用戶可以通過輸入姓名、學號和選擇性別來搜索學生信息。
- 權限控制:新增和修改按鈕的顯示取決于用戶是否擁有相應的權限。
- 數據展示:表格展示學生信息,包括姓名、學號和性別,其中性別列通過性別數據映射展示。
- 操作功能:表格的每行提供編輯、新增和刪除操作,這些操作的可用性同樣受到權限控制。
總結
此頁面是一個典型的學生信息管理系統頁面,結合了Thymeleaf模板引擎和Shiro權限管理框架,實現了數據展示、搜索和基于權限的操作功能。
題庫管理 課程信息
題庫管理 課程信息
添加頁面前端設計
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head><th:block th:include="include :: header('新增題目信息')" /><th:block th:include="include :: summernote-css" />
</head>
<body class="white-bg"><div class="wrapper wrapper-content animated fadeInRight ibox-content"><form class="form-horizontal m" id="form-question-add"><div class="form-group"> <label class="col-sm-3 control-label is-required">課程名:</label><div class="col-sm-8"><select name="classId" class="form-control m-b" th:with="type=${@dict.getType('sys_class')}" required><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option></select></div></div><div class="form-group"> <label class="col-sm-3 control-label is-required">題目類型:</label><div class="col-sm-8"><select name="typeId" class="form-control m-b" th:with="type=${@dict.getType('sys_question_type')}" required><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option></select></div></div><div class="form-group"> <label class="col-sm-3 control-label is-required">關鍵字:</label><div class="col-sm-8"><input name="questionKey" class="form-control" type="text" required></div></div><div class="form-group"> <label class="col-sm-3 control-label">題目描述:</label><div class="col-sm-8"><input type="hidden" class="form-control" name="questionDescription"><div class="summernote" id="questionDescription"></div></div></div><div class="form-group"> <label class="col-sm-3 control-label">答案:</label><div class="col-sm-8"><input type="hidden" class="form-control" name="questionAnswer"><div class="summernote" id="questionAnswer"></div></div></div></form></div><th:block th:include="include :: footer" /><th:block th:include="include :: summernote-js" /><script th:inline="javascript">var prefix = ctx + "system/question"$("#form-question-add").validate({focusCleanup: true});function submitHandler() {if ($.validate.form()) {$.operate.save(prefix + "/add", $('#form-question-add').serialize());}}$(function() {$('.summernote').summernote({lang: 'zh-CN',dialogsInBody: true,callbacks: {onChange: function(contents, $edittable) {$("input[name='" + this.id + "']").val(contents);},onImageUpload: function(files) {var obj = this;var data = new FormData();data.append("file", files[0]);$.ajax({type: "post",url: ctx + "common/upload",data: data,cache: false,contentType: false,processData: false,dataType: 'json',success: function(result) {if (result.code == web_status.SUCCESS) {$('#' + obj.id).summernote('insertImage', result.url);} else {$.modal.alertError(result.msg);}},error: function(error) {$.modal.alertWarning("圖片上傳失敗。");}});}}});});</script>
</body>
</html>
修改題目信息前端設計
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head><th:block th:include="include :: header('修改題目信息')" /><th:block th:include="include :: summernote-css" />
</head>
<body class="white-bg"><div class="wrapper wrapper-content animated fadeInRight ibox-content"><form class="form-horizontal m" id="form-question-edit" th:object="${question}"><input name="id" th:field="*{id}" type="hidden"><div class="form-group"> <label class="col-sm-3 control-label is-required">課程名:</label><div class="col-sm-8"><select name="classId" class="form-control m-b" th:with="type=${@dict.getType('sys_class')}" required><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{classId}"></option></select></div></div><div class="form-group"> <label class="col-sm-3 control-label is-required">題目類型:</label><div class="col-sm-8"><select name="typeId" class="form-control m-b" th:with="type=${@dict.getType('sys_question_type')}" required><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{typeId}"></option></select></div></div><div class="form-group"> <label class="col-sm-3 control-label is-required">關鍵字:</label><div class="col-sm-8"><input name="questionKey" th:field="*{questionKey}" class="form-control" type="text" required></div></div><div class="form-group"> <label class="col-sm-3 control-label">題目描述:</label><div class="col-sm-8"><input type="hidden" class="form-control" th:field="*{questionDescription}"><div class="summernote" id="questionDescription"></div></div></div><div class="form-group"> <label class="col-sm-3 control-label">答案:</label><div class="col-sm-8"><input type="hidden" class="form-control" th:field="*{questionAnswer}"><div class="summernote" id="questionAnswer"></div></div></div></form></div><th:block th:include="include :: footer" /><th:block th:include="include :: summernote-js" /><script th:inline="javascript">var prefix = ctx + "system/question";$("#form-question-edit").validate({focusCleanup: true});function submitHandler() {if ($.validate.form()) {$.operate.save(prefix + "/edit", $('#form-question-edit').serialize());}}$(function() {$('.summernote').each(function(i) {$('#' + this.id).summernote({lang: 'zh-CN',dialogsInBody: true,callbacks: {onChange: function(contents, $edittable) {$("input[name='" + this.id + "']").val(contents);},onImageUpload: function(files) {var obj = this;var data = new FormData();data.append("file", files[0]);$.ajax({type: "post",url: ctx + "common/upload",data: data,cache: false,contentType: false,processData: false,dataType: 'json',success: function(result) {if (result.code == web_status.SUCCESS) {$('#' + obj.id).summernote('insertImage', result.url);} else {$.modal.alertError(result.msg);}},error: function(error) {$.modal.alertWarning("圖片上傳失敗。");}});}}});var content = $("input[name='" + this.id + "']").val();$('#' + this.id).summernote('code', content);})});</script>
</body>
</html>問題頁面總覽設計
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><th:block th:include="include :: header('題目信息列表')" />
</head>
<body class="gray-bg"><div class="container-div"><div class="row"><div class="col-sm-12 search-collapse"><form id="formId"><div class="select-list"><ul><li><label>課程名:</label><select name="classId" th:with="type=${@dict.getType('sys_class')}"><option value="">所有</option><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option></select></li><li><label>題目類型:</label><select name="typeId" th:with="type=${@dict.getType('sys_question_type')}"><option value="">所有</option><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option></select></li><li><label>關鍵字:</label><input type="text" name="questionKey"/></li><li><a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i> 搜索</a><a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a></li></ul></div></form></div><div class="btn-group-sm" id="toolbar" role="group"><a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="system:question:add"><i class="fa fa-plus"></i> 添加</a><a class="btn btn-primary single disabled" onclick="$.operate.edit()" shiro:hasPermission="system:question:edit"><i class="fa fa-edit"></i> 修改</a><a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="system:question:remove"><i class="fa fa-remove"></i> 刪除</a><a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="system:question:export"><i class="fa fa-download"></i> 導出</a></div><div class="col-sm-12 select-table table-striped"><table id="bootstrap-table"></table></div></div></div><th:block th:include="include :: footer" /><script th:inline="javascript">var editFlag = [[${@permission.hasPermi('system:question:edit')}]];var removeFlag = [[${@permission.hasPermi('system:question:remove')}]];var classIdDatas = [[${@dict.getType('sys_class')}]];var typeIdDatas = [[${@dict.getType('sys_question_type')}]];var prefix = ctx + "system/question";$(function() {var options = {url: prefix + "/list",createUrl: prefix + "/add",updateUrl: prefix + "/edit/{id}",removeUrl: prefix + "/remove",exportUrl: prefix + "/export",modalName: "題目信息",columns: [{checkbox: true},{field: 'id',title: '題號',visible: false},{field: 'classId',title: '課程名',formatter: function(value, row, index) {return $.table.selectDictLabel(classIdDatas, value);}},{field: 'typeId',title: '題目類型',formatter: function(value, row, index) {return $.table.selectDictLabel(typeIdDatas, value);}},{field: 'questionKey',title: '關鍵字'},{field: 'questionDescription',title: '題目描述'},{field: 'questionAnswer',title: '答案'},{title: '操作',align: 'center',formatter: function(value, row, index) {var actions = [];actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" οnclick="$.operate.edit(\'' + row.id + '\')"><i class="fa fa-edit"></i>編輯</a> ');actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" οnclick="$.operate.remove(\'' + row.id + '\')"><i class="fa fa-remove"></i>刪除</a>');return actions.join('');}}]};$.table.init(options);});</script>
</body>
</html>
這個HTML頁面是利用Thymeleaf模板引擎和Shiro權限管理框架構建的,主要用于學生信息列表的展示與管理。下面將詳細解釋頁面各部分的作用:
頁面結構與布局
- DOCTYPE聲明: 聲明文檔類型為HTML5。
- HTML標簽: 定義了頁面的基本結構,使用了Thymeleaf和Shiro的命名空間。
- Head部分: 包含頁面的頭部信息,通過
th:include
指令引用了外部的頭部模板,預設了頁面標題為“學生信息列表”。 - Body部分: 頁面的主要內容區域,設置了背景顏色,并組織了不同的功能區塊。
搜索功能
- 搜索表單: 包含三個字段——課程名稱、章節、關鍵字——用于過濾學生信息列表。使用了Thymeleaf的
th:with
指令來處理表單中的下拉列表選項,這些選項來源于后端的數據字典。 - 搜索與重置按鈕: 分別用于提交搜索條件和清除已有的搜索條件,以刷新數據列表。
操作按鈕
- 工具欄: 包含“新增”、“修改”和“展開/折疊”按鈕。其中,“新增”和“修改”按鈕使用Shiro的
shiro:hasPermission
指令來控制按鈕的顯示,確保只有具備相應權限的用戶才能看到這些按鈕。
數據表格
- 學生信息表格: 使用
id="bootstrap-tree-table"
的表格元素,用于展示學生信息。盡管具體的數據填充和格式化邏輯在JavaScript腳本中實現,但表格的設計和樣式已經預先設定好。
JavaScript腳本
- 動態腳本: 利用Thymeleaf的
th:inline="javascript"
屬性,腳本能夠直接讀取服務器端的狀態,如權限標識和數據字典。這使得腳本可以根據后端返回的權限狀態動態生成操作按鈕的可見性,并能正確地處理數據字典的值。 - 初始化表格: 腳本中定義了表格的初始化邏輯,包括設置表格的列名、列寬、數據來源等。特別地,對于“性別”列,使用了
formatter
函數來根據value
查找對應的字典標簽進行顯示。 - 操作按鈕綁定: 腳本還綁定了操作按鈕的功能,如“新增”和“修改”,并且這些功能的可用性會根據用戶的權限進行調整。
綜合功能
- 權限控制: 頁面上多個元素的可見性和功能,如操作按鈕,都受到Shiro權限管理框架的控制,確保用戶只能訪問其權限范圍內的功能。
- 數據展示與交互: 頁面不僅展示了學生信息列表,還提供了搜索、添加、修改等功能,使得管理員能夠高效地管理和維護學生數據。
總之,這個頁面集成了用戶界面設計、數據展示、權限控制和動態腳本處理,形成了一個功能完善的學生信息管理系統前端界面。
這段代碼使用Thymeleaf模板引擎來構建一個用于添加題目信息的表單,結合了表單驗證、富文本編輯器和Ajax通信功能。以下是各個部分的詳細解析:
HTML結構和命名空間
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
- DOCTYPE聲明:指定文檔類型為HTML5。
- HTML語言屬性:設置頁面的語言為中文。
- Thymeleaf命名空間:允許在HTML中使用Thymeleaf表達式和指令。
頭部和腳部模板包含
<th:block th:include="include :: header('新增題目信息')" />
<th:block th:include="include :: summernote-css" />
...
<th:block th:include="include :: footer" />
<th:block th:include="include :: summernote-js" />
- header:包含頁面頭部,如標題和CSS樣式。
- summernote-css:包含Summernote編輯器所需的CSS。
- footer:包含頁面底部,可能包括腳本和其他HTML尾部元素。
- summernote-js:包含Summernote編輯器所需的JS。
表單元素
<form class="form-horizontal m" id="form-question-add">
...
</form>
- 表單類:
form-horizontal
為Bootstrap樣式,m
可能是自定義樣式。 - 表單ID:
form-question-add
用于識別和操作表單。
表單項
<div class="form-group"> <label>課程名:</label><div><select name="classId" th:with="type=${@dict.getType('sys_class')}" required><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option></select></div>
</div>
- 選擇框:使用Thymeleaf的
th:with
和th:each
來填充下拉菜單的選項,required
屬性確保必填。 - 文本輸入:如
<input name="questionKey" ...>
用于輸入關鍵字,同樣帶有required
屬性。
Summernote編輯器
<input type="hidden" class="form-control" name="questionDescription">
<div class="summernote" id="questionDescription"></div>
- 隱藏輸入框:用于存儲編輯器的內容。
- Summernote容器:
<div>
元素,由Summernote編輯器填充。
JavaScript腳本
<script th:inline="javascript">
...
</script>
- 表單驗證:使用
$("#form-question-add").validate()
初始化驗證。 - 提交處理程序:
submitHandler
函數用于處理表單提交事件。 - Summernote配置:設置編輯器的選項,如語言、回調函數等。
- Ajax上傳圖片:當用戶在編輯器中嘗試上傳圖片時,執行Ajax請求上傳圖片到服務器。
整體來看,這段代碼構建了一個功能完整的題目信息添加表單,集成了數據驗證、富文本編輯和Ajax通信,確保了良好的用戶體驗和數據處理能力。
下拉菜單動態讀取數據庫
在Thymeleaf模板中,你所展示的代碼片段用于生成一個下拉選擇框(<select>
),并填充基于后端提供的數據字典中的選項。具體分析如下:
-
th:with
指令: 這個指令用于在當前作用域內定義一個變量。在這個場景中,type=${@dict.getType('sys_class')}
意味著它調用了名為dict
的服務對象的一個方法getType(String typeCode)
,傳入參數sys_class
。假設這個方法返回一個列表或集合,里面包含了課程分類的信息,每個元素都有dictLabel
和dictValue
屬性,分別對應下拉菜單的顯示文本和值。 -
th:each
指令: 這個指令用于遍歷一個集合或數組。在<option>
標簽中,th:each="dict : ${type}"
表示對于type
集合中的每一個元素(這里命名為dict
),都會生成一個<option>
標簽。 -
th:text
和th:value
指令: 這兩個指令分別用于設置<option>
標簽的顯示文本和值。在<option>
中,th:text="${dict.dictLabel}"
和th:value="${dict.dictValue}"
意味著每一個生成的<option>
標簽的文本和值將分別從遍歷的dict
對象中獲取dictLabel
和dictValue
屬性的值。 -
required
屬性: 這個HTML5屬性用于表示該字段是必須填寫的。如果用戶提交表單時沒有選擇任何選項,瀏覽器將阻止表單提交并顯示錯誤信息。
綜上所述,這段代碼的作用是動態生成一個下拉菜單,菜單中的每個選項都是從后端服務調用中獲取的課程分類數據,保證了數據的實時性和準確性,同時增加了表單的交互性和用戶體驗。
修改題目信息代碼
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head><th:block th:include="include :: header('修改題目信息')" /><th:block th:include="include :: summernote-css" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content"><form class="form-horizontal m" id="form-question-edit" th:object="${question}"><input name="id" th:field="*{id}" type="hidden"><div class="form-group"><label class="col-sm-3 control-label is-required">課程名:</label><div class="col-sm-8"><select name="classId" class="form-control m-b" th:with="type=${@dict.getType('sys_class')}" required><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{classId}"></option></select></div></div><div class="form-group"><label class="col-sm-3 control-label is-required">題目類型:</label><div class="col-sm-8"><select name="typeId" class="form-control m-b" th:with="type=${@dict.getType('sys_question_type')}" required><option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{typeId}"></option></select></div></div><div class="form-group"><label class="col-sm-3 control-label is-required">關鍵字:</label><div class="col-sm-8"><input name="questionKey" th:field="*{questionKey}" class="form-control" type="text" required></div></div><div class="form-group"><label class="col-sm-3 control-label">題目描述:</label><div class="col-sm-8"><input type="hidden" class="form-control" th:field="*{questionDescription}"><div class="summernote" id="questionDescription"></div></div></div><div class="form-group"><label class="col-sm-3 control-label">答案:</label><div class="col-sm-8"><input type="hidden" class="form-control" th:field="*{questionAnswer}"><div class="summernote" id="questionAnswer"></div></div></div></form>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: summernote-js" />
<script th:inline="javascript">var prefix = ctx + "system/question";$("#form-question-edit").validate({focusCleanup: true});function submitHandler() {if ($.validate.form()) {$.operate.save(prefix + "/edit", $('#form-question-edit').serialize());}}$(function() {$('.summernote').each(function(i) {$('#' + this.id).summernote({lang: 'zh-CN',dialogsInBody: true,callbacks: {onChange: function(contents, $edittable) {$("input[name='" + this.id + "']").val(contents);},onImageUpload: function(files) {var obj = this;var data = new FormData();data.append("file", files[0]);$.ajax({type: "post",url: ctx + "common/upload",data: data,cache: false,contentType: false,processData: false,dataType: 'json',success: function(result) {if (result.code == web_status.SUCCESS) {$('#' + obj.id).summernote('insertImage', result.url);} else {$.modal.alertError(result.msg);}},error: function(error) {$.modal.alertWarning("圖片上傳失敗。");}});}}});var content = $("input[name='" + this.id + "']").val();$('#' + this.id).summernote('code', content);})});
</script>
</body>
</html>
這段代碼構建了一個用于修改題目信息的HTML頁面,它使用了Thymeleaf模板引擎,jQuery庫,以及Summernote富文本編輯器。下面是每個部分的詳細解析:
HTML與Thymeleaf
1. DOCTYPE聲明與語言屬性
<!DOCTYPE html>
:聲明這是一個HTML5文檔。lang="zh"
:指明文檔語言為中文。
2. Thymeleaf命名空間
xmlns:th="http://www.thymeleaf.org"
:引入Thymeleaf的命名空間,使模板能使用Thymeleaf指令。
3. Header與Footer包含
<th:block th:include="include :: header('修改題目信息')">
:通過th:include
指令引入頭部模板,標題為“修改題目信息”。<th:block th:include="include :: summernote-css">
:引入Summernote編輯器所需的CSS樣式。<th:block th:include="include :: footer">
:引入頁面底部模板。<th:block th:include="include :: summernote-js">
:引入Summernote編輯器所需的JavaScript腳本。
Body內容
4. 表單結構
<form class="form-horizontal m" id="form-question-edit" th:object="${question}">
:定義一個水平布局的表單,使用th:object
綁定表單到question
對象。- 輸入字段如
<input name="questionKey" th:field="*{questionKey}"...
:使用th:field
指令雙向綁定輸入字段與question
對象的屬性。
5. 下拉菜單
<select name="classId" th:with="type=${@dict.getType('sys_class')}">
:定義一個下拉菜單,使用th:with
指令獲取課程分類數據。<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{classId}">
:使用th:each
遍歷type
列表,生成下拉選項。
JavaScript腳本
6. 表單驗證
$("#form-question-edit").validate({ focusCleanup: true })
:使用jQuery Validate插件初始化表單驗證。
7. 提交處理
function submitHandler() { ... }
:定義提交處理函數,檢查表單驗證通過后,使用Ajax發送表單數據。
8. Summernote配置
$(function() { ... })
:文檔加載完成后執行的代碼。.summernote({ ... })
:初始化Summernote編輯器,設置語言、對話框顯示方式、回調函數等。onChange
:內容變化時更新隱藏輸入框的值。onImageUpload
:處理圖片上傳,使用Ajax上傳圖片并插入到編輯器中。
9. 動態加載內容
var content = $("input[name='" + this.id + "']").val();
:獲取隱藏輸入框的值。$('#' + this.id).summernote('code', content);
:將值加載到編輯器中。
整體而言,這段代碼展示了如何使用Thymeleaf進行動態數據綁定,結合jQuery與Summernote編輯器實現富文本編輯功能,以及如何處理表單驗證和Ajax數據提交。
SQL代碼解析
實現課程題型的基本信息管理
-- Table structure for class
-- ----------------------------
CREATE TABLE `class` (`class_id` int(0) NOT NULL COMMENT '課程號',`class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '課程名',PRIMARY KEY
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
2. CREATE TABLE `class` (...) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; - 這部分代碼是用來創建一個名為`class`的表。表`class`中包含了兩個字段:`class_id`是整數類型的課程號,`class_name`是可變長度字符串類型的課程名。其中,`class_id`是主鍵字段。指定了該表使用InnoDB存儲引擎,字符集為utf8mb4,排序規則為utf8mb4_0900_ai_ci,行格式為Dynamic。總的來說,上述代碼的含義是創建一個名為`class`的表,表結構包含了課程號和課程名兩個字段,其中`class_id`是主鍵字段,使用InnoDB存儲引擎,字符集為utf8mb4,排序規則為utf8mb4_0900_ai_ci,行格式為Dynamic。- Records of class
-- ----------------------------
INSERT INTO `class` VALUES (1, '計算機組成原理');
INSERT INTO `class` VALUES (2, '數據庫系統原理');
INSERT INTO `class` VALUES (3, '操作系統原理');
INSERT INTO `class` VALUES (4, '算法設計與分析');
INSERT INTO `class` VALUES (5, 'c++');
NSERT INTO是用于向數據庫表中插入新記錄的SQL語句。在這里,我們使用INSERT INTO語句將課程記錄插入到名為"class"的表中。每條記錄包括課程編號和課程名稱。
Table structure for type
-- ----------------------------
CREATE TABLE `type` (`type_id` int(0) NOT NULL COMMENT '類型號',`type_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '類型名',PRIMARY KEY (`type_id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
表中有兩個字段: type_id 是一個整數類型且不為空,并有注釋說明是“類型號”,被設置為主鍵,并使用了 BTREE 索引。 type_name 是一個可變長度的字符類型(最大長度 255),字符集為 utf8mb4 ,排序規則為 utf8mb4_0900_ai_ci ,不為空,并有注釋說明是“類型名”。表使用的存儲引擎是 InnoDB ,字符集是 utf8mb4 ,行格式是 Dynamic 。-- ----------------------------
-- Records of type
-- ----------------------------
INSERT INTO `type` VALUES (1, '填空題');
INSERT INTO `type` VALUES (2, '選擇題');
INSERT INTO `type` VALUES (3, '編程題');
INSERT INTO `type` VALUES (4, '綜合題');SET FOREIGN_KEY_CHECKS = 1;在 MySQL 中,默認情況下外鍵約束檢查是關閉的( SET FOREIGN_KEY_CHECKS = 0 )。當執行 SET FOREIGN_KEY_CHECKS = 1 后,數據庫在進行數據操作時會檢查外鍵約束,以確保數據的完整性和一致性。
實現習題信息的管理,能按照題型或章節錄入每門課的習題
- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',`parent_id` bigint NULL DEFAULT NULL COMMENT '',`sname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '課程名稱',`sid` int NULL DEFAULT NULL COMMENT '章節號',`ssex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '課程編號',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (100, 0, '西安石油大學 題庫管理系統', NULL, '');
INSERT INTO `student` VALUES (101, 100, '計算機組成原理', NULL, '第一');
INSERT INTO `student` VALUES (102, 100, '數據庫系統原理', NULL, '第二');
INSERT INTO `student` VALUES (103, 100, '操作系統原理', NULL, '第三');
INSERT INTO `student` VALUES (104, 101, '第一章節', 1, '');
INSERT INTO `student` VALUES (105, 101, '第二章節', 2, '');
INSERT INTO `student` VALUES (106, 102, '第一章節', 1, '');
INSERT INTO `student` VALUES (107, 103, '第一章節', 1, '');
INSERT INTO `student` VALUES (108, 103, '第二章節', 2, '');SET FOREIGN_KEY_CHECKS = 1;-- ----------------------------
管理課程的題型和章節
CREATE TABLE `gen_table_column` (`column_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '編號',`column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列名稱',`column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列描述',`column_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列類型',`java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA類型',`java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA字段名',
這部分代碼是在一個SQL語句中定義了兩個字段,分別是`java_type`和`java_field`。具體含義如下:1. `java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA類型' - 這個字段用來存儲Java類型,類型為可變長度字符串,最大長度為500個字符。設置了字符集為utf8mb4,排序規則為utf8mb4_0900_ai_ci,允許存儲NULL值,默認值為NULL。并且添加了注釋說明該字段存儲的是Java類型。2. `java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA字段名' - 這個字段用來存儲Java字段名,類型為可變長度字符串,最大長度為200個字符。同樣設置了字符集為utf8mb4,排序規則為utf8mb4_0900_ai_ci,允許存儲NULL值,默認值為NULL。并且添加了注釋說明該字段存儲的是Java字段名。這些字段的定義表明,這個表可能用于記錄Java類型和字段名的對應關系,或者其他與Java相關的信息。字符集和排序規則的設置可以確保存儲的數據能夠正確地處理多字節字符,同時注釋提供了字段用途的說明。`is_pk` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否主鍵(1是)',
- Records of gen_table_columnINSERT INTO `gen_table_column` VALUES (68, 'id', 'ID', 'bigint', 'Long', 'id', '1', '1',
INSERT INTO `gen_table_column` VALUES (69, 'parent_id', '所屬班級', 'bigint', 'Long', 'parentId', '0', '0'
INSERT INTO `gen_table_column` VALUES (71, 'sname', '課程名稱', 'varchar(255)', 'String', 'sname', '0', '0',
INSERT INTO `gen_table_column` VALUES (72, 'sid', '章節號', 'int', 'Long', 'sid', '0', '0',
INSERT INTO `gen_table_column` VALUES (73, '11', 'ssex', '課程編號', 'varchar(255)', 'String', 'ssex', '0', '0',
INSERT INTO `gen_table_column` VALUES (92, '16', 'id', '', 'int', 'Long', 'id', '1', '1',
INSERT INTO `gen_table_column` VALUES (99, '18', 'id', '題號', 'int', 'Long', 'id', '1', '1',
INSERT INTO `gen_table_column` VALUES (100, '18', 'class_id', '課程名', 'int', 'Long', 'classId', '0', '0',
INSERT INTO `gen_table_column` VALUES (101, '18', 'type_id', '題目類型', 'int', 'Long', 'typeId', '0', '0',
INSERT INTO `gen_table_column` VALUES (102, '18', 'question_key', '關鍵字', 'longtext', 'String', 'questionKey', '0', '0',
INSERT INTO `gen_table_column` VALUES (103, '18', 'question_description', '題目描述', 'longtext', 'String', 'questionDescription', '0', '0',
INSERT INTO `gen_table_column` VALUES (104, '18', 'question_answer', '答案', 'longtext', 'String', 'questionAnswer',
創建表:
CREATE TABLE class (
class_id INT PRIMARY KEY,
class_type VARCHAR(255)
);
CREATE TABLE type (
type_id INT PRIMARY KEY,
type_name VARCHAR(255)
);
CREATE TABLE chapters (
chapter_id INT PRIMARY KEY,
class_id INT,
chapter_name VARCHAR(255),
FOREIGN KEY (class_id) REFERENCES classes(class_id)
);
CREATE TABLE questions (
question_id INT AUTO_INCREMENT PRIMARY KEY,
class_id INT NOT NULL,
question_text TEXT NOT NULL,
type_id INT NOT NULL,
FOREIGN KEY (class_id) REFERENCES classes(class_id),
FOREIGN KEY (type_id) REFERENCES types(type_id));
問題四:
定義一個存儲過程,以查詢指定課程的所有章節及其對應的題型數量:
CREATE PROCEDURE GetQuestionStats(IN course_id INT)
BEGIN
SELECT c.chapter_name, t.type_name, COUNT(q.question_id) AS question_count
FROM questions q
JOIN chapters c ON q.chapter_id = c.chapter_id
JOIN types t ON q.type_id = t.type_id
WHERE q.class_id = course_id
GROUP BY c.chapter_name, t.type_name;
問題五:
CREATE VIEW CourseQuestionTypesWithCounts AS
SELECT c.class_id, c.class_type, t.type_name, COUNT(q.question_id) AS question_count
FROM classes c
JOIN questions q ON c.class_id = q.class_id
JOIN types t ON q.type_id = t.type_id
GROUP BY c.class_id, t.type_name;
查詢:
SELECT * FROM CourseQuestionTypesWithCounts;
問題六:
questions表的創建
INSERT INTO questions (class_id, question_text, type_id)
VALUES (1, ‘這是一個示例問題。’, 1);
下面就無需手動增加序號
INSERT INTO questions (class_id, question_text, type_id)
VALUES (2, ‘這是另一個示例問題。’, 2);
問題七:
CREATE PROCEDURE GetQuestionCountsByClassAndType()
BEGIN
– 創建臨時表用于存儲結果
CREATE TEMPORARY TABLE IF NOT EXISTS temp_question_counts (
class_id INT,
type_id INT,
count INT
);
-- 清空臨時表
TRUNCATE TABLE temp_question_counts;-- 插入統計數據到臨時表
INSERT INTO temp_question_counts (class_id, type_id, count)
SELECT q.class_id, q.type_id, COUNT(q.question_id)
FROM questions q
GROUP BY q.class_id, q.type_id;-- 顯示結果
SELECT c.class_name, t.type_name, ttc.count AS question_count
FROM temp_question_counts ttc
JOIN classes c ON c.class_id = ttc.class_id
JOIN types t ON t.type_id = ttc.type_id;
這個存儲過程做了以下幾件事:
- 定義了一個臨時表
temp_question_counts
,用于存儲課程ID、題型ID以及對應的習題數量。 - 清空臨時表,確保每次運行存儲過程時,結果都是新的。
- 使用
INSERT INTO ... SELECT
語句從questions
表中計算每門課程每種題型的習題數量,并插入到臨時表中。 - 最后,從臨時表中讀取數據,并通過JOIN操作與
classes
和types
表連接,以便獲取課程名和題型名,然后輸出結果。
要調用這個存儲過程,只需執行以下命令:
CALL GetQuestionCountsByClassAndType();