本地和jenkins打包不一致

main
SLMS Development Team 4 months ago
parent 803c4d3c29
commit 07f9f5efd9

9
.gitignore vendored

@ -2,6 +2,12 @@
.gradle/
build/
# 本地构建号文件
.build_number
# 本地敏感配置文件
local.gradle.properties
# jpackage 临时目录
jpackage-input/
jpackage-output/
@ -60,10 +66,11 @@ Thumbs.db
ehthumbs.db
Desktop.ini
# Database
# Database - 保留 core/library.db 作为唯一数据源
*.db
*.sqlite
*.sqlite3
!core/library.db
data/*.db
!data/.gitkeep

39
Jenkinsfile vendored

@ -352,34 +352,19 @@ pipeline {
echo ⚠️ 缺少 MCSLMS.exe 启动器
)
REM 复制数据库文件优先从core目录复制否则使用根目录的
REM 复制数据库和配置文件 - 统一从 core/ 目录
if exist "core\\library.db" (
copy /Y "core\\library.db" "dist\\library.db" >nul
echo ✓ library.db
)
if not exist "dist\\library.db" (
if exist "library.db" (
copy /Y "library.db" "dist\\library.db" >nul
echo ✓ library.db
)
)
if not exist "dist\\library.db" (
echo ⚠️ 缺少 library.db 数据库
) else (
echo ⚠️ 缺少 core\\library.db 数据库
)
REM 复制数据源配置文件
if exist "datasource.properties" (
copy /Y "datasource.properties" "dist\\datasource.properties" >nul
if exist "core\\datasource.properties" (
copy /Y "core\\datasource.properties" "dist\\datasource.properties" >nul
echo ✓ datasource.properties
)
if not exist "dist\\datasource.properties" (
if exist "core\\src\\main\\resources\\database.properties" (
copy /Y "core\\src\\main\\resources\\database.properties" "dist\\datasource.properties" >nul
echo ✓ datasource.properties
)
)
if not exist "dist\\datasource.properties" (
echo ⚠️ 缺少 datasource.properties 配置
) else (
echo ⚠️ 缺少 core\\datasource.properties 配置
)
echo.
@ -498,13 +483,13 @@ pipeline {
)
echo.
REM 复制数据库文件
REM 复制数据库文件 - 统一使用 core/library.db
echo [5/6] 复制数据库文件...
if exist "data\\library.db" (
copy /Y "data\\library.db" "${tomcatBin}\\library.db" >nul
echo ✓ 复制数据库: data\\library.db
if exist "core\\library.db" (
copy /Y "core\\library.db" "${tomcatBin}\\library.db" >nul
echo ✓ 复制数据库: core\\library.db
) else (
echo ⚠️ 警告: 未找到 data\\library.db
echo ⚠️ 警告: 未找到 core\\library.db
)
echo.

@ -1,10 +0,0 @@
#MCSLMS DataSource Configuration - v1.7.0
#Mon Dec 15 23:27:00 CST 2025
database.host=localhost
database.name=slms
database.password=
database.port=5432
database.type=SQLITE
database.url=jdbc\:sqlite\:E\:\\2025-2026\\\u8F6F\u4EF6\u5DE5\u7A0B\u57FA\u7840\\\u5B9E\u9A8C\\MCSLMS\\core\\library.db
database.username=
environment=DEV

@ -1,6 +1,8 @@
package com.smartlibrary.web.controller;
import com.smartlibrary.voice.XunFeiConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import javax.crypto.Mac;
@ -23,11 +25,17 @@ import java.util.concurrent.TimeUnit;
@RequestMapping("/api/voice")
public class VoiceController {
private static final Logger logger = LoggerFactory.getLogger(VoiceController.class);
// 常量定义
private static final String KEY_SUCCESS = "success";
private static final String KEY_ERROR = "error";
private final XunFeiConfig config;
public VoiceController() {
this.config = XunFeiConfig.loadFromFile();
System.out.println("✓ 讯飞语音服务已初始化");
logger.info("✓ 讯飞语音服务已初始化");
}
/**
@ -38,12 +46,12 @@ public class VoiceController {
Map<String, Object> result = new HashMap<>();
try {
String url = buildAuthUrl(XunFeiConfig.STT_API_URL);
result.put("success", true);
result.put(KEY_SUCCESS, true);
result.put("url", url);
result.put("appId", config.getAppId());
} catch (Exception e) {
result.put("success", false);
result.put("error", e.getMessage());
result.put(KEY_SUCCESS, false);
result.put(KEY_ERROR, e.getMessage());
}
return result;
}
@ -56,12 +64,12 @@ public class VoiceController {
Map<String, Object> result = new HashMap<>();
try {
String url = buildAuthUrl(XunFeiConfig.TTS_API_URL);
result.put("success", true);
result.put(KEY_SUCCESS, true);
result.put("url", url);
result.put("appId", config.getAppId());
} catch (Exception e) {
result.put("success", false);
result.put("error", e.getMessage());
result.put(KEY_SUCCESS, false);
result.put(KEY_ERROR, e.getMessage());
}
return result;
}
@ -75,18 +83,18 @@ public class VoiceController {
String audioBase64 = request.get("audio");
if (audioBase64 == null || audioBase64.isEmpty()) {
result.put("success", false);
result.put("error", "音频数据为空");
result.put(KEY_SUCCESS, false);
result.put(KEY_ERROR, "音频数据为空");
return result;
}
try {
String text = recognizeWithXunFei(audioBase64);
result.put("success", true);
result.put(KEY_SUCCESS, true);
result.put("text", text);
} catch (Exception e) {
result.put("success", false);
result.put("error", "识别失败: " + e.getMessage());
result.put(KEY_SUCCESS, false);
result.put(KEY_ERROR, "识别失败: " + e.getMessage());
}
return result;
}
@ -278,7 +286,10 @@ public class VoiceController {
if (snEnd > snStart) {
try {
sn = Integer.parseInt(json.substring(snStart + 5, snEnd).trim());
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException e) {
// 解析失败时使用默认值0
logger.debug("解析sn失败: {}", e.getMessage());
}
}
}
@ -307,8 +318,9 @@ public class VoiceController {
// 替换该句子的结果
sentenceResults.set(sn, text.toString());
} catch (Exception ignored) {
// 静默处理解析异常
} catch (Exception e) {
// 解析异常时记录日志,不影响主流程
logger.debug("解析语音识别结果异常: {}", e.getMessage());
}
}

@ -1,48 +1,137 @@
@echo off
title MCSLMS 打包工具
setlocal EnableDelayedExpansion
echo ========================================
echo MCSLMS 图书管理系统 - 打包发布
echo (与 Jenkins 流水线保持一致)
echo ========================================
echo.
cd /d "%~dp0"
echo [1/5] 创建 dist 目录...
if not exist dist mkdir dist
REM 设置版本号 (与Jenkins保持一致的格式: v1.80.0.{构建次数})
set "BASE_VERSION=1.80.0"
REM 从文件读取上次构建号,自动递增
set BUILD_NUM=0
if exist ".build_number" (
set /p BUILD_NUM=<.build_number
)
set /a BUILD_NUM=%BUILD_NUM%+1
echo %BUILD_NUM%>.build_number
set "VERSION_TAG=v%BASE_VERSION%.%BUILD_NUM%"
echo.
echo 版本号: %VERSION_TAG%
echo.
echo [1/8] 清理并创建 dist 目录...
if exist dist rmdir /S /Q dist
mkdir dist
echo [2/8] 构建所有模块 (clean build)...
call gradlew.bat clean build -x test --no-daemon -q
if errorlevel 1 (
echo ✗ 构建失败
goto :error
)
echo [3/8] 打包 CLI (fatJar)...
call gradlew.bat :cli:fatJar -x test -PbuildNumber=%BUILD_NUM% --no-daemon -q
for %%f in (cli\build\libs\*-all.jar) do (
copy /Y "%%f" dist\cli.jar >nul
echo ✓ cli.jar
)
echo [4/8] 打包 GUI (fatJar)...
call gradlew.bat :gui:fatJar -x test -PbuildNumber=%BUILD_NUM% --no-daemon -q
for %%f in (gui\build\libs\*-all.jar) do (
copy /Y "%%f" dist\gui.jar >nul
echo ✓ gui.jar
)
echo [5/8] 打包 Backend (bootJar)...
call gradlew.bat :backend:bootJar -x test -PbuildNumber=%BUILD_NUM% --no-daemon -q
for %%f in (backend\build\libs\mcslms-backend-*.jar) do (
echo %%f | findstr /i "plain" >nul || (
copy /Y "%%f" dist\backend.jar >nul
echo ✓ backend.jar
)
)
echo [2/5] 打包 CLI (fat jar)...
call gradlew.bat :cli:fatJar -q
for %%f in (cli\build\libs\*-all.jar) do copy /Y "%%f" dist\cli.jar >nul
echo [6/8] 打包 Launcher (MCSLMS.exe)...
call gradlew.bat :launcher:build :launcher:createExe --no-daemon -q
if exist launcher\build\launch4j\MCSLMS.exe (
copy /Y launcher\build\launch4j\MCSLMS.exe dist\MCSLMS.exe >nul
echo ✓ MCSLMS.exe
) else (
echo ⚠ MCSLMS.exe 创建失败
)
echo [3/5] 打包 GUI (fat jar)...
call gradlew.bat :gui:fatJar -q
for %%f in (gui\build\libs\*-all.jar) do copy /Y "%%f" dist\gui.jar >nul
REM 复制 launcher.jar
for %%f in (launcher\build\libs\*.jar) do (
copy /Y "%%f" dist\launcher.jar >nul
echo ✓ launcher.jar
)
echo [4/5] 打包 Backend (boot jar)...
call gradlew.bat :backend:bootJar -q
for %%f in (backend\build\libs\*.jar) do (
echo %%f | findstr /i "plain" >nul || copy /Y "%%f" dist\backend.jar >nul
echo [7/8] 打包 Android APK (可选)...
call gradlew.bat :android:assembleDebug --no-daemon -q 2>nul
if exist android\build\outputs\apk\debug\*.apk (
for %%f in (android\build\outputs\apk\debug\*.apk) do (
copy /Y "%%f" dist\android.apk >nul
echo ✓ android.apk
)
) else (
echo ⚠ Android APK 跳过 (需要 Android SDK)
)
echo [5/5] 打包 Launcher (MCSLMS.exe)...
call gradlew.bat :launcher:createExe -q
copy /Y launcher\build\launch4j\MCSLMS.exe dist\MCSLMS.exe >nul
echo [8/8] 复制数据库和配置文件 (统一从 core/ 目录)...
REM 数据库和配置文件统一放在 core/ 目录
if exist core\library.db (
copy /Y core\library.db dist\library.db >nul
echo ✓ library.db
) else (
echo ⚠ core\library.db 未找到
)
echo [6/6] 复制数据库和配置文件...
copy /Y cli\library.db dist\library.db >nul
copy /Y cli\datasource.properties dist\datasource.properties >nul
if exist core\datasource.properties (
copy /Y core\datasource.properties dist\datasource.properties >nul
echo ✓ datasource.properties
) else (
echo ⚠ core\datasource.properties 未找到
)
echo.
echo ========================================
echo 打包完成! 文件位于 dist 目录:
echo - MCSLMS.exe (启动器 - 双击运行)
echo - cli.jar (命令行端)
echo - gui.jar (桌面端)
echo - backend.jar (服务端)
echo - library.db (数据库)
echo - datasource.properties (配置)
echo 打包完成! 版本: %VERSION_TAG%
echo ========================================
echo.
echo dist 目录内容:
dir /B dist
echo.
echo ----------------------------------------
echo 文件说明:
echo - MCSLMS.exe 启动器 (双击运行)
echo - cli.jar 命令行端
echo - gui.jar 桌面端
echo - backend.jar 服务端
echo - launcher.jar 启动器JAR
echo - android.apk Android端
echo - library.db 数据库
echo - datasource.properties 配置
echo ----------------------------------------
echo.
echo 运行方式: 双击 dist\MCSLMS.exe
echo.
goto :end
:error
echo.
echo ========================================
echo ✗ 打包失败,请检查错误信息
echo ========================================
echo.
:end
endlocal
pause

@ -5,6 +5,13 @@ plugins {
id 'org.sonarqube' version '4.4.1.3373'
}
//
def localPropsFile = rootProject.file('local.gradle.properties')
def localProps = new Properties()
if (localPropsFile.exists()) {
localProps.load(new FileInputStream(localPropsFile))
}
sonar {
properties {
property 'sonar.projectKey', 'mcslms'
@ -12,7 +19,8 @@ sonar {
property 'sonar.sourceEncoding', 'UTF-8'
property 'sonar.java.source', '21'
property 'sonar.host.url', 'http://localhost:9000'
property 'sonar.token', 'sqp_28dee20a416dd020466799252e09228a1799e2be' // 使
// Token
property 'sonar.token', System.getenv('SONAR_TOKEN') ?: localProps.getProperty('sonar.token') ?: ''
// JaCoCo
property 'sonar.coverage.jacoco.xmlReportPaths',
'core/build/reports/jacoco/test/jacocoTestReport.xml,' +

@ -1,10 +0,0 @@
#MCSLMS DataSource Configuration - v1.7.0
#Mon Dec 15 20:46:24 CST 2025
database.host=localhost
database.name=slms
database.password=
database.port=5432
database.type=SQLITE
database.url=jdbc\:sqlite\:E\:\\2025-2026\\\u8F6F\u4EF6\u5DE5\u7A0B\u57FA\u7840\\\u5B9E\u9A8C\\MCSLMS\\core\\library.db
database.username=
environment=DEV

@ -65,7 +65,7 @@ public class CLIApplication {
}
public void run() {
scanner = new Scanner(System.in);
// Scanner已在构造函数中初始化无需重复创建
logger.info("欢迎使用智能图书馆系统!");
// 尝试自动登录

@ -1,10 +1,12 @@
#MCSLMS DataSource Configuration - v1.7.0
#Mon Dec 22 03:41:13 CST 2025
database.host=127.0.0.1
database.name=testdb
database.password=
database.port=5433
#MCSLMS DataSource Configuration - v2.0.0
#统一配置文件,与 library.db 放在同一目录
#打包时复制到 dist/ 目录,使用相对路径
database.type=SQLITE
database.url=jdbc\:sqlite\:E\:\\2025-2026\\\u8F6F\u4EF6\u5DE5\u7A0B\u57FA\u7840\\\u5B9E\u9A8C\\MCSLMS\\core\\library.db
database.username=
database.url=jdbc\:sqlite\:library.db
database.host=localhost
database.port=5432
database.name=slms
database.username=postgres
database.password=postgres
environment=DEV

Binary file not shown.

@ -166,7 +166,6 @@ public class MockDataGenerator {
} catch (Exception e) {
logger.log(Level.SEVERE, "生成Mock数据失败", e);
logger.log(Level.SEVERE, "生成Mock数据失败", e);
}
}
@ -174,8 +173,8 @@ public class MockDataGenerator {
String sql = "INSERT OR REPLACE INTO books (id, title, author, isbn, publisher, publish_date, category, book_type, available) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
Connection conn = dbConnection.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
try (Connection conn = dbConnection.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
for (Book book : books) {
stmt.setString(1, book.getId());
stmt.setString(2, book.getTitle());
@ -186,23 +185,25 @@ public class MockDataGenerator {
stmt.setString(7, book.getCategory());
stmt.setString(8, book.getBookType());
stmt.setBoolean(9, book.isAvailable());
stmt.executeUpdate();
stmt.addBatch();
}
stmt.executeBatch();
}
}
private void insertUsers(List<User> users) throws SQLException {
String sql = "INSERT OR REPLACE INTO users (id, name, email, phone) VALUES (?, ?, ?, ?)";
Connection conn = dbConnection.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
try (Connection conn = dbConnection.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
for (User user : users) {
stmt.setString(1, user.getId());
stmt.setString(2, user.getName());
stmt.setString(3, user.getEmail());
stmt.setString(4, user.getPhone());
stmt.executeUpdate();
stmt.addBatch();
}
stmt.executeBatch();
}
}
@ -210,8 +211,8 @@ public class MockDataGenerator {
String sql = "INSERT OR REPLACE INTO loans (id, book_id, user_id, borrow_date, due_date, return_date, returned) " +
"VALUES (?, ?, ?, ?, ?, ?, ?)";
Connection conn = dbConnection.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
try (Connection conn = dbConnection.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
for (Loan loan : loans) {
stmt.setString(1, loan.getId());
stmt.setString(2, loan.getBookId());
@ -220,8 +221,9 @@ public class MockDataGenerator {
stmt.setString(5, loan.getDueDate().toString());
stmt.setString(6, loan.getReturnDate() != null ? loan.getReturnDate().toString() : null);
stmt.setBoolean(7, loan.isReturned());
stmt.executeUpdate();
stmt.addBatch();
}
stmt.executeBatch();
}
}

@ -12,6 +12,13 @@ import java.util.logging.Logger;
public class ReaderInteractionService extends BaseService {
private static final Logger LOGGER = Logger.getLogger(ReaderInteractionService.class.getName());
// 常量定义 - 数据库字段名
private static final String COL_USER_ID = "user_id";
private static final String COL_BOOK_ID = "book_id";
private static final String COL_CREATED_AT = "created_at";
private static final String COL_CONTENT = "content";
private static final String TYPE_COMMENT = "COMMENT";
public ReaderInteractionService() {
super();
initializeTables();
@ -130,7 +137,7 @@ public class ReaderInteractionService extends BaseService {
};
if (executeUpdate(sql, params) > 0) {
LOGGER.info("✓ 添加笔记成功: " + id);
LOGGER.log(Level.INFO, "✓ 添加笔记成功: {0}", id);
return id;
}
LOGGER.warning("添加笔记失败");
@ -236,9 +243,7 @@ public class ReaderInteractionService extends BaseService {
String sql = "SELECT COUNT(*) FROM favorites WHERE user_id = ? AND book_id = ?";
Object[] params = {userId, bookId};
try {
return executeQuery(sql, params, rs -> {
return rs.next() && rs.getInt(1) > 0;
});
return executeQuery(sql, params, rs -> rs.next() && rs.getInt(1) > 0);
} catch (Exception e) {
return false;
}
@ -262,11 +267,11 @@ public class ReaderInteractionService extends BaseService {
while (rs.next()) {
favorites.add(new Favorite(
rs.getString("id"),
rs.getString("user_id"),
rs.getString("book_id"),
rs.getString(COL_USER_ID),
rs.getString(COL_BOOK_ID),
rs.getString("book_title"),
rs.getString("book_author"),
rs.getString("created_at")
rs.getString(COL_CREATED_AT)
));
}
return favorites;
@ -309,16 +314,14 @@ public class ReaderInteractionService extends BaseService {
public List<ReadingBookmark> getBookmarksForBook(String userId, String bookId) {
List<ReadingBookmark> bookmarks = new ArrayList<>();
String sql = "SELECT * FROM reading_bookmarks WHERE user_id = ? AND book_id = ? ORDER BY page_number, created_at";
try {
Connection conn = dbConnection.getConnection();
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, userId);
pstmt.setString(2, bookId);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
bookmarks.add(mapResultSetToBookmark(rs));
}
String sql = "SELECT id, user_id, book_id, page_number, chapter, label, highlight_text, created_at FROM reading_bookmarks WHERE user_id = ? AND book_id = ? ORDER BY page_number, created_at";
try (Connection conn = dbConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, userId);
pstmt.setString(2, bookId);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
bookmarks.add(mapResultSetToBookmark(rs));
}
}
} catch (SQLException e) {
@ -351,7 +354,7 @@ public class ReaderInteractionService extends BaseService {
try {
int rowsAffected = executeUpdate(sql, params);
boolean success = rowsAffected > 0;
if (success && "COMMENT".equals(targetType)) {
if (success && TYPE_COMMENT.equals(targetType)) {
updateCommentLikeCount(targetId, 1);
}
return success;
@ -370,7 +373,7 @@ public class ReaderInteractionService extends BaseService {
try {
int rowsAffected = executeUpdate(sql, params);
boolean success = rowsAffected > 0;
if (success && "COMMENT".equals(targetType)) {
if (success && TYPE_COMMENT.equals(targetType)) {
updateCommentLikeCount(targetId, -1);
}
return success;
@ -387,9 +390,7 @@ public class ReaderInteractionService extends BaseService {
String sql = "SELECT COUNT(*) FROM likes WHERE user_id = ? AND target_type = ? AND target_id = ?";
Object[] params = {userId, targetType, targetId};
try {
return executeQuery(sql, params, rs -> {
return rs.next() && rs.getInt(1) > 0;
});
return executeQuery(sql, params, rs -> rs.next() && rs.getInt(1) > 0);
} catch (Exception e) {
return false;
}
@ -434,7 +435,7 @@ public class ReaderInteractionService extends BaseService {
try {
executeUpdate(sql, params);
LOGGER.info("✓ 添加评论成功: " + id);
LOGGER.log(Level.INFO, "✓ 添加评论成功: {0}", id);
return id;
} catch (Exception e) {
LOGGER.log(Level.WARNING, "添加评论失败: {0}", e.getMessage());
@ -521,13 +522,15 @@ public class ReaderInteractionService extends BaseService {
/**
*
/**
* URL
*/
private String generateShareUrl(String contentType, String contentId) {
String baseUrl = "http://localhost:8082";
return switch (contentType) {
case "BOOK" -> baseUrl + "/books/" + contentId;
case "NOTE" -> baseUrl + "/notes/" + contentId;
case "COMMENT" -> baseUrl + "/comments/" + contentId;
case TYPE_COMMENT -> baseUrl + "/comments/" + contentId;
default -> baseUrl;
};
}
@ -565,7 +568,7 @@ public class ReaderInteractionService extends BaseService {
try {
executeUpdate(sql, params);
LOGGER.info("✓ 反馈提交成功: " + id);
LOGGER.log(Level.INFO, "✓ 反馈提交成功: {0}", id);
return id;
} catch (Exception e) {
LOGGER.log(Level.WARNING, "提交反馈失败: {0}", e.getMessage());
@ -613,15 +616,15 @@ public class ReaderInteractionService extends BaseService {
private ReadingNote mapResultSetToNote(ResultSet rs) throws SQLException {
return new ReadingNote(
rs.getString("id"),
rs.getString("user_id"),
rs.getString("book_id"),
rs.getString(COL_USER_ID),
rs.getString(COL_BOOK_ID),
rs.getString("book_title"),
rs.getString("title"),
rs.getString("content"),
rs.getString(COL_CONTENT),
rs.getInt("page_number"),
rs.getString("chapter"),
rs.getInt("is_public") == 1,
rs.getString("created_at"),
rs.getString(COL_CREATED_AT),
rs.getString("updated_at")
);
}
@ -629,41 +632,41 @@ public class ReaderInteractionService extends BaseService {
private Comment mapResultSetToComment(ResultSet rs) throws SQLException {
return new Comment(
rs.getString("id"),
rs.getString("user_id"),
rs.getString(COL_USER_ID),
rs.getString("user_name"),
rs.getString("book_id"),
rs.getString("content"),
rs.getString(COL_BOOK_ID),
rs.getString(COL_CONTENT),
rs.getInt("rating"),
rs.getString("parent_id"),
rs.getInt("like_count"),
rs.getString("created_at")
rs.getString(COL_CREATED_AT)
);
}
private ReadingBookmark mapResultSetToBookmark(ResultSet rs) throws SQLException {
return new ReadingBookmark(
rs.getString("id"),
rs.getString("user_id"),
rs.getString("book_id"),
rs.getString(COL_USER_ID),
rs.getString(COL_BOOK_ID),
rs.getInt("page_number"),
rs.getString("chapter"),
rs.getString("label"),
rs.getString("highlight_text"),
rs.getString("created_at")
rs.getString(COL_CREATED_AT)
);
}
private Feedback mapResultSetToFeedback(ResultSet rs) throws SQLException {
return new Feedback(
rs.getString("id"),
rs.getString("user_id"),
rs.getString(COL_USER_ID),
rs.getString("type"),
rs.getString("title"),
rs.getString("content"),
rs.getString(COL_CONTENT),
rs.getString("contact"),
rs.getString("status"),
rs.getString("reply"),
rs.getString("created_at"),
rs.getString(COL_CREATED_AT),
rs.getString("replied_at")
);
}

@ -124,26 +124,25 @@ public class ReservationService extends BaseService {
ORDER BY queue_position ASC LIMIT 1
""";
try {
Connection conn = dbConnection.getConnection();
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, bookId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
String reservationId = rs.getString("id");
// 更新状态为可借阅设置3天有效期
String updateSql = """
UPDATE reservations SET status = 'AVAILABLE',
available_date = date('now'),
expire_date = date('now', '+3 days')
WHERE id = ?
""";
try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {
updateStmt.setString(1, reservationId);
updateStmt.executeUpdate();
}
return mapResultSetToReservation(rs);
try (Connection conn = dbConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, bookId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
String reservationId = rs.getString("id");
Reservation reservation = mapResultSetToReservation(rs);
// 更新状态为可借阅设置3天有效期
String updateSql = """
UPDATE reservations SET status = 'AVAILABLE',
available_date = date('now'),
expire_date = date('now', '+3 days')
WHERE id = ?
""";
try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {
updateStmt.setString(1, reservationId);
updateStmt.executeUpdate();
}
return reservation;
}
}
} catch (SQLException e) {
@ -159,14 +158,12 @@ public class ReservationService extends BaseService {
List<Reservation> reservations = new ArrayList<>();
String sql = "SELECT * FROM reservations WHERE user_id = ? ORDER BY created_at DESC";
try {
Connection conn = dbConnection.getConnection();
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, userId);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
reservations.add(mapResultSetToReservation(rs));
}
try (Connection conn = dbConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, userId);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
reservations.add(mapResultSetToReservation(rs));
}
}
} catch (SQLException e) {
@ -186,14 +183,12 @@ public class ReservationService extends BaseService {
ORDER BY queue_position ASC
""";
try {
Connection conn = dbConnection.getConnection();
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, bookId);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
queue.add(mapResultSetToReservation(rs));
}
try (Connection conn = dbConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, bookId);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
queue.add(mapResultSetToReservation(rs));
}
}
} catch (SQLException e) {
@ -210,15 +205,13 @@ public class ReservationService extends BaseService {
UPDATE reservations SET status = 'EXPIRED'
WHERE status = 'AVAILABLE' AND expire_date < date('now')
""";
try {
Connection conn = dbConnection.getConnection();
try (Statement stmt = conn.createStatement()) {
int count = stmt.executeUpdate(sql);
if (count > 0) {
LOGGER.info("✓ 处理了 " + count + " 个过期预约");
}
return count;
try (Connection conn = dbConnection.getConnection();
Statement stmt = conn.createStatement()) {
int count = stmt.executeUpdate(sql);
if (count > 0) {
LOGGER.info("✓ 处理了 " + count + " 个过期预约");
}
return count;
} catch (SQLException e) {
LOGGER.log(Level.WARNING, "处理过期预约失败: {0}", e.getMessage());
return 0;
@ -229,14 +222,12 @@ public class ReservationService extends BaseService {
private boolean hasReservation(String bookId, String userId) {
String sql = "SELECT COUNT(*) FROM reservations WHERE book_id = ? AND user_id = ? AND status IN ('WAITING', 'AVAILABLE')";
try {
Connection conn = dbConnection.getConnection();
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, bookId);
pstmt.setString(2, userId);
try (ResultSet rs = pstmt.executeQuery()) {
return rs.next() && rs.getInt(1) > 0;
}
try (Connection conn = dbConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, bookId);
pstmt.setString(2, userId);
try (ResultSet rs = pstmt.executeQuery()) {
return rs.next() && rs.getInt(1) > 0;
}
} catch (SQLException e) {
return false;

@ -1,11 +0,0 @@
#MCSLMS DataSource Configuration - v1.9.0
#Unified database: core/library.db
database.host=localhost
database.name=slms
# PostgreSQL密码演示用
database.password=postgres
database.port=5432
database.type=SQLITE
database.url=jdbc\:sqlite\:core/library.db
database.username=postgres
environment=DEV

@ -1,10 +0,0 @@
#MCSLMS DataSource Configuration - v1.7.0
#Mon Dec 08 21:42:06 CST 2025
database.host=localhost
database.name=slms
database.password=
database.port=5432
database.type=SQLITE
database.url=jdbc\:sqlite\:library.db
database.username=
environment=DEV

@ -1049,7 +1049,7 @@ public class GUIApplication extends Application {
passwordField.setPromptText("留空表示不修改密码");
ComboBox<String> roleCombo = new ComboBox<>();
roleCombo.getItems().addAll(ROLE_READER, "LIBRARIAN", "ADMIN");
roleCombo.setValue(selectedUser.getRole().toString());
roleCombo.setValue(selectedUser.getRole() != null ? selectedUser.getRole().toString() : ROLE_READER);
grid.add(new Label("读者ID"), 0, 0);
grid.add(idField, 1, 0);

@ -1,13 +0,0 @@
@echo off
title MCSLMS 启动器
cd /d "%~dp0"
if exist dist\MCSLMS.jar (
echo 正在启动 MCSLMS...
cd dist
java -jar MCSLMS.jar
) else (
echo dist\MCSLMS.jar 不存在!
echo 请先运行 build-dist.bat 打包应用
pause
)

@ -187,7 +187,12 @@ public class LauncherMain extends JFrame {
}
public static void main(String[] args) {
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ignored) {}
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
// 使用默认外观,不影响程序运行
System.err.println("无法设置系统外观: " + e.getMessage());
}
SwingUtilities.invokeLater(() -> new LauncherMain().setVisible(true));
}
}

@ -1,29 +0,0 @@
@echo off
REM 智能图书馆系统 - CLI端启动脚本
REM 设置编码为UTF-8
chcp 65001 >nul
echo.
echo ========================================
echo 智能图书馆系统 - 命令行界面
echo MCSLMS CLI Application
echo ========================================
echo.
REM 检查JAR文件是否存在
if not exist "cli\build\libs\mcslms-cli-v1.11.0.0-all.jar" (
echo 错误: 找不到JAR文件
echo 请先运行: gradlew :cli:fatJar
pause
exit /b 1
)
REM 启动应用
echo 正在启动应用...
echo.
java -Dfile.encoding=UTF-8 -jar cli/build/libs/mcslms-cli-v1.11.0.0-all.jar
echo.
echo 应用已关闭
pause

@ -1,28 +0,0 @@
# 智能图书馆系统 - CLI端启动脚本
# MCSLMS CLI Application Launcher
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " 智能图书馆系统 - 命令行界面" -ForegroundColor Cyan
Write-Host " MCSLMS CLI Application" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# 检查JAR文件是否存在
$jarPath = "cli/build/libs/mcslms-cli-v1.11.0.0-all.jar"
if (-not (Test-Path $jarPath)) {
Write-Host "错误: 找不到JAR文件" -ForegroundColor Red
Write-Host "请先运行: gradlew :cli:fatJar" -ForegroundColor Yellow
Read-Host "按Enter键退出"
exit 1
}
# 启动应用
Write-Host "正在启动应用..." -ForegroundColor Green
Write-Host ""
& java -Dfile.encoding=UTF-8 -jar $jarPath
Write-Host ""
Write-Host "应用已关闭" -ForegroundColor Yellow
Read-Host "按Enter键退出"

@ -1,43 +0,0 @@
@echo off
set "ROOT_DIR=%~dp0"
cd /d "%ROOT_DIR%"
rem 检查Java是否安装
java -version >nul 2>&1
if %errorlevel% neq 0 (
echo Java未安装或未配置环境变量
echo 请先安装Java 21或更高版本
pause
exit /b 1
)
echo 正在启动MCSLMS图书管理系统...
echo.
echo 系统将在几秒钟后启动,请稍候...
echo.
rem 启动MCSLMS启动器
java -jar "dist/MCSLMS.jar"
rem 如果启动失败,提供手动启动选项
if %errorlevel% neq 0 (
echo 启动器启动失败!
echo 请尝试以下命令手动启动各端:
echo.
echo 1. CLI命令行端
echo java -jar "dist/cli.jar"
echo.
echo 2. GUI图形界面端
echo java -jar "dist/gui.jar"
echo.
echo 3. Web服务端
echo java -jar "dist/backend.jar"
echo.
echo 4. 再次启动启动器:
echo java -jar "dist/MCSLMS.jar"
echo.
pause
exit /b 1
)
exit /b 0

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,11 +0,0 @@
#MCSLMS DataSource Configuration - v1.9.0
#Unified database: core/library.db
database.host=localhost
database.name=slms
# PostgreSQL密码演示用
database.password=postgres
database.port=5432
database.type=SQLITE
database.url=jdbc\:sqlite\:core/library.db
database.username=postgres
environment=DEV

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,10 +0,0 @@
#MCSLMS DataSource Configuration - v1.7.0
#Mon Dec 15 20:46:24 CST 2025
database.host=localhost
database.name=slms
database.password=
database.port=5432
database.type=SQLITE
database.url=jdbc\:sqlite\:E\:\\2025-2026\\\u8F6F\u4EF6\u5DE5\u7A0B\u57FA\u7840\\\u5B9E\u9A8C\\MCSLMS\\core\\library.db
database.username=
environment=DEV

Binary file not shown.

Binary file not shown.

@ -1,36 +0,0 @@
@echo off
setlocal EnableDelayedExpansion
echo ============================================
echo MCSLMS 启动测试
echo ============================================
REM 检查必需的文件
echo 检查必需的文件...
if not exist "test-extract\MCSLMS.exe" (
echo ❌ 缺少 MCSLMS.exe
exit /b 1
)
echo ✓ MCSLMS.exe 存在
if not exist "test-extract\library.db" (
echo ❌ 缺少 library.db
exit /b 1
)
echo ✓ library.db 存在
if not exist "test-extract\datasource.properties" (
echo ❌ 缺少 datasource.properties
exit /b 1
)
echo ✓ datasource.properties 存在
echo.
echo 所有必需的文件都已找到!
echo.
echo 要测试应用程序启动:
echo 1. 进入 test-extract 目录
echo 2. 双击 MCSLMS.exe 或运行以下命令:
echo .\MCSLMS.exe
echo.
echo 如果应用程序正常启动而没有错误,则表示打包成功。

@ -1,88 +0,0 @@
@echo off
setlocal EnableDelayedExpansion
set "VERSION_TAG=v1.80.0.test"
echo ============================================
echo 测试 MCSLMS 完整发布包打包: !VERSION_TAG!
echo ============================================
REM 更新 test-dist 目录中的文件
echo [1/6] 更新 CLI JAR...
for %%f in (cli\build\libs\mcslms-cli-*-all.jar) do (
copy /Y "%%f" "test-dist\cli.jar" >nul
echo ✓ cli.jar
)
echo [2/6] 更新 GUI JAR...
for %%f in (gui\build\libs\mcslms-gui-*-all.jar) do (
copy /Y "%%f" "test-dist\gui.jar" >nul
echo ✓ gui.jar
)
echo [3/6] 更新 Backend JAR...
for %%f in (backend\build\libs\mcslms-backend-*.jar) do (
echo %%f | findstr /V "plain" >nul && (
copy /Y "%%f" "test-dist\backend.jar" >nul
echo ✓ backend.jar
)
)
echo [4/6] 更新 Android APK...
for %%f in (android\build\outputs\apk\debug\*.apk) do (
copy /Y "%%f" "test-dist\android.apk" >nul
echo ✓ android.apk
)
echo [5/6] 更新 Launcher JAR...
for %%f in (launcher\build\libs\*.jar) do (
copy /Y "%%f" "test-dist\launcher.jar" >nul
echo ✓ launcher.jar
)
echo [6/6] 复制缺失的文件...
copy /Y "dist\MCSLMS.exe" "test-dist\MCSLMS.exe" >nul 2>&1
if exist "dist\MCSLMS.exe" (
echo ✓ MCSLMS.exe
) else (
echo ⚠️ 缺少 MCSLMS.exe 启动器
)
REM 复制数据库文件优先从core目录复制否则使用根目录的
if exist "core\library.db" (
copy /Y "core\library.db" "test-dist\library.db" >nul
echo ✓ library.db (from core)
) else if exist "library.db" (
copy /Y "library.db" "test-dist\library.db" >nul
echo ✓ library.db (from root)
) else (
echo ⚠️ 缺少 library.db 数据库
)
copy /Y "datasource.properties" "test-dist\datasource.properties" >nul 2>&1
if exist "datasource.properties" (
echo ✓ datasource.properties
) else (
echo ⚠️ 缺少 datasource.properties 配置
)
echo.
echo test-dist 目录内容:
dir /B test-dist
REM 打包整个 test-dist 目录为 ZIP
echo.
echo 打包 test-dist 目录为 ZIP...
powershell -Command "Compress-Archive -Path 'test-dist\*' -DestinationPath 'artifacts\mcslms-release-!VERSION_TAG!.zip' -Force"
if exist "artifacts\mcslms-release-!VERSION_TAG!.zip" (
echo.
echo ✓ 完整发布包创建成功: mcslms-release-!VERSION_TAG!.zip
for %%A in ("artifacts\mcslms-release-!VERSION_TAG!.zip") do echo 文件大小: %%~zA bytes
echo.
echo 使用说明:
echo 1. 解压 ZIP 到任意目录
echo 2. 双击 MCSLMS.exe 即可启动
) else (
echo ✗ ZIP 打包失败
)
Loading…
Cancel
Save