Compare commits

...

8 Commits

Author SHA1 Message Date
lzx
5a0c9b06ee README.md fixed 2026-01-29 17:11:34 +08:00
lzx
5557ee0d3e README.md fixed 2026-01-29 17:10:12 +08:00
lzx
50477ccf5d remove builded files 2026-01-29 17:04:07 +08:00
lzx
8778141154 update 2026-01-29 17:00:58 +08:00
lzx
e9c4582e0d drop sqlite3 and use .log, optimize some code 2026-01-29 16:51:06 +08:00
lzx
194b062bb9 linux client 2026-01-29 10:46:11 +08:00
lzx
7f86327a96 Revert "lunux client"
This reverts commit 8142887644.
2026-01-29 10:45:20 +08:00
lzx
8142887644 lunux client 2026-01-29 10:44:29 +08:00
9 changed files with 416 additions and 209 deletions

2
.gitignore vendored
View File

@@ -10,6 +10,8 @@
.history
.svn/
.swiftpm/
linux_app/
windows_app/
migrate_working_dir/
# IntelliJ related

View File

@@ -1,4 +1,4 @@
# 云算力平台客户端 - Windows 桌面应用
# 云算力平台客户端 - Windows / Linux 桌面应用
基于 Flutter 开发的 Windows 桌面客户端应用,实现与云算力平台的通信、挖矿管理等功能。
@@ -13,10 +13,11 @@
- **GPU 信息**:通过 `nvidia-smi` 命令自动检测(启动时获取一次)
- 显示 GPU索引、品牌、型号、显存大小
- **硬盘身份码**:通过 `wmic diskdrive get serialnumber` 命令获取
- **当前状态**:实时显示客户端连接状态
- 🔴 **离线**心跳异常,红色指示
- 🟢 **在线**心跳正常,绿色指示
- 🟡 **挖矿中**挖矿程序运行中,黄色指示
- **当前状态**:实时显示客户端连接状态
- 🔴 **离线**未连接服务器,红色指示
- 🟢 **在线**已连接服务器,绿色指示
- 🟡 **挖矿中**租约挖矿进行中,黄色指示
- 🔵 **持续挖矿中**:持续挖矿任务进行中,蓝色指示
### 2. 版本更新功能
@@ -79,15 +80,15 @@
- **SystemInfoService**系统信息获取GPU、硬盘序列号等
- **ConfigService**配置文件管理INI 格式)
- **UpdateService**:版本检查和更新管理
- **DatabaseService**SQLite 数据库,存储挖矿任务历史
- **LogService**:日志文件管理
- **DatabaseService**挖矿任务日志管理(基于 `bin/mining_tasks.log` 的 JSON 行记录,用于保存 / 恢复当前或最近一次挖矿任务
- **LogService**客户端日志文件管理
### 数据存储
- **配置文件**`bin/mining.windows.conf`INI 格式)
- **身份文件**`bin/auth`
- **版本文件**`bin/version`
- **数据库**`bin/mining_task.db`SQLite
- **挖矿任务日志**`bin/mining_tasks.log`JSON 行格式,仅记录当前 / 最近任务,用于断线恢复
- **日志文件**`bin/logs/client.log`
### 通信协议

View File

@@ -4,6 +4,25 @@
---
### 2026-01-29
- **挖矿任务持久化重构(替换 SQLite 为本地日志文件)**
- 移除 `sqflite_common_ffi` 及 SQLite 依赖,避免在 Windows / Linux 环境下对系统 `libsqlite3` 的安装要求。
- `DatabaseService` 改为基于 `bin/mining_tasks.log` 的 JSON 行存储:
- 新挖矿任务创建时追加写入 `.log`
- 挖矿任务完成后,从 `.log` 中删除对应记录;
- 客户端启动时读取 `.log`,仅保留未过期任务,并自动恢复最新一条未完成的挖矿任务。
- **退出流程优化**
- 新增 `ClientProvider.shutdown()`,在点击“退出程序”时:
- 停止与服务器的连接和心跳;
- 停止当前挖矿进程和持续挖矿任务;
- 关闭自动刷新定时器,确保退出后不会残留后台矿工进程。
- **文档与多平台说明**
- README 中补充了“持续挖矿中”状态标识(蓝色指示灯)及 `bin/mining_tasks.log` 的作用说明。
- 增加 Linux 构建脚本(`build_linux.sh`)和运行脚本(`start_linux_app.sh`)的使用说明,支持在 Linux 环境下一键安装依赖并运行客户端。
### 2026-01-23
- **网络与构建相关**

View File

@@ -1,9 +1,9 @@
#请确认您的主机上安装了下列挖矿软件,确认后可以打开注释,并修改其路径,如果没有安装,请勿打开注释
#请使用双\\,否则可能无法解析出准确的路径
[client]
server_url=18.183.240.108:2345
update_url=https://test.m2pool.com/api/lease
# [client]
# server_url=18.183.240.108:2345
# update_url=https://test.m2pool.com/api/lease
#请确认您的主机上安装了下列挖矿软件,确认后可以打开注释,并修改其路径,如果没有安装,请勿打开注释
#请使用双\\,否则可能无法解析出准确的路径

193
build_linux.sh Normal file
View File

@@ -0,0 +1,193 @@
#!/usr/bin/env bash
set -e
#############################################
# 配置:修改为你的项目路径
#############################################
# 方式1手动指定路径如果脚本不在项目根目录
# PROJECT_DIR="/home/lizixuan/linux_client/windows-application"
# 方式2自动检测如果脚本放在项目根目录推荐
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
#############################################
# 0. 打印环境信息
#############################################
echo "==== Linux Flutter 桌面环境初始化 & 构建脚本 ===="
echo "项目路径: $PROJECT_DIR"
echo "当前用户: $(whoami)"
echo "当前目录: $(pwd)"
echo
#############################################
# 1. 安装系统依赖Ubuntu / Debian
#############################################
echo "==== 1. 安装系统依赖(需要 sudo ===="
sudo apt update
# 基本编译工具
sudo apt install -y build-essential
# CMake / NinjaFlutter 构建需要)
sudo apt install -y cmake ninja-build
# GTK3 及相关开发包Flutter Linux GUI 需要)
sudo apt install -y libgtk-3-dev libblkid-dev liblzma-dev
# SQLite 库sqflite_common_ffi 需要)
sudo apt install -y libsqlite3-dev
# 常用工具
sudo apt install -y git curl unzip
echo "系统依赖安装完成"
echo
#############################################
# 2. 安装 Flutter SDK如果还没有
#############################################
echo "==== 2. 检查 Flutter 是否已安装 ===="
if ! command -v flutter >/dev/null 2>&1; then
echo "未检测到 flutter 命令,开始安装 Flutter SDK..."
FLUTTER_VERSION="3.16.0" # 可按需要调整版本
FLUTTER_TAR="flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"
FLUTTER_URL="https://storage.flutter-io.cn/flutter_infra_release/releases/stable/linux/${FLUTTER_TAR}"
cd "$HOME"
echo "从中国镜像下载 Flutter: $FLUTTER_URL"
curl -O "$FLUTTER_URL"
tar xf "$FLUTTER_TAR"
# 写入 PATH如果已经写入过不会有问题
if ! grep -q "flutter/bin" "$HOME/.bashrc"; then
echo 'export PATH="$PATH:$HOME/flutter/bin"' >> "$HOME/.bashrc"
fi
# 立刻生效当前 shell
export PATH="$PATH:$HOME/flutter/bin"
echo "Flutter 安装完成,版本:$(flutter --version)"
else
echo "已检测到 Flutter$(flutter --version)"
fi
echo
#############################################
# 3. 配置 Flutter 国内镜像
#############################################
echo "==== 3. 配置 Flutter 国内镜像环境变量 ===="
export PUB_HOSTED_URL="https://pub.flutter-io.cn"
export FLUTTER_STORAGE_BASE_URL="https://storage.flutter-io.cn"
# 写入 ~/.bashrc 方便下次登录继续使用
if ! grep -q "PUB_HOSTED_URL" "$HOME/.bashrc"; then
cat >> "$HOME/.bashrc" << 'EOF'
# Flutter China mirrors
export PUB_HOSTED_URL="https://pub.flutter-io.cn"
export FLUTTER_STORAGE_BASE_URL="https://storage.flutter-io.cn"
EOF
fi
echo "国内镜像已配置(当前 shell 已生效)"
echo
#############################################
# 4. 启用 Linux 桌面支持
#############################################
echo "==== 4. 启用 Linux 桌面支持 ===="
flutter config --enable-linux-desktop
echo "flutter doctor 检查环境:"
flutter doctor -v || true
echo
#############################################
# 5. 进入项目,生成 linux 工程(如未生成)
#############################################
echo "==== 5. 切换到项目目录,并生成 linux 工程(如需要) ===="
if [ ! -d "$PROJECT_DIR" ]; then
echo "错误:项目目录不存在:$PROJECT_DIR"
exit 1
fi
cd "$PROJECT_DIR"
# 如果没有 linux 目录,则创建
if [ ! -d "linux" ]; then
echo "未检测到 linux 目录,执行 flutter create --platforms=linux ."
# 从 pubspec.yaml 读取项目名(如果存在)
PROJECT_NAME="cloud_client_gui"
if [ -f "pubspec.yaml" ]; then
# 尝试从 pubspec.yaml 提取 name 字段
EXTRACTED_NAME=$(grep -E "^name:" pubspec.yaml | head -1 | sed 's/name:[[:space:]]*//' | sed 's/[[:space:]]*$//')
if [ -n "$EXTRACTED_NAME" ]; then
PROJECT_NAME="$EXTRACTED_NAME"
echo "从 pubspec.yaml 读取到项目名: $PROJECT_NAME"
fi
fi
# 使用 --project-name 参数明确指定项目名,避免目录名问题
echo "使用项目名: $PROJECT_NAME 创建 linux 平台..."
if flutter create --platforms=linux . --project-name "$PROJECT_NAME"; then
echo "linux 平台创建成功"
else
echo "警告: flutter create 执行失败"
echo "尝试不带 --project-name 参数重新执行..."
flutter create --platforms=linux . || {
echo "错误: 无法创建 linux 平台,请检查错误信息"
echo "可以尝试手动执行: flutter create --platforms=linux . --project-name cloud_client_gui"
exit 1
}
fi
else
echo "已检测到 linux 目录,跳过 flutter create。"
fi
echo
#############################################
# 6. 获取依赖
#############################################
echo "==== 6. 获取 Dart / Flutter 依赖 ===="
flutter pub get
echo
#############################################
# 7. 构建 Linux Release 版本
#############################################
echo "==== 7. 构建 Linux Release 版本 ===="
flutter build linux --release
echo
echo "==== 构建完成 ===="
echo "可执行文件及运行目录位于:"
echo " $PROJECT_DIR/build/linux/x64/release/bundle/"
echo
echo "请确保将项目中的 bin/ 目录复制到该 bundle 目录同级,例如:"
echo " build/linux/x64/release/bundle/"
echo " ├─ cloud_client_gui"
echo " ├─ bin/"
echo " │ ├─ version"
echo " │ ├─ auth"
echo " │ ├─ mining.linux.conf"
echo " │ ├─ logs/"
echo " │ └─ mining_task.db"
echo
echo "然后在该目录下运行:"
echo " ./cloud_client_gui"
echo
echo "==== 运行时依赖说明 ===="
echo "如果运行时提示找不到 libsqlite3.so请确保已安装"
echo " sudo apt install -y libsqlite3-dev"
echo "或者确保系统已安装 libsqlite3.so通常在 /usr/lib/x86_64-linux-gnu/"
echo
echo "全部步骤完成。"

View File

@@ -1,74 +1,55 @@
import 'dart:async';
// import 'dart:io';
import 'dart:convert';
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'mining_task_info.dart';
import '../utils/path_utils.dart';
/// 数据库管理服务
/// 挖矿任务持久化服务(基于 .log 文件,而非 SQLite
///
/// 设计约定:
/// - 使用 `bin/mining_tasks.log` 记录当前(或最近)挖矿任务,一行一条 JSON 记录。
/// - 每次收到新的挖矿任务时,追加一条记录。
/// - 挖矿任务完成后,从 .log 中删除对应记录。
/// - 客户端启动时读取 .log
/// - 如果存在任务且 `endTimestamp` 尚未过期,则恢复该任务;
/// - 如果已过期,则删除该记录,并不恢复。
class DatabaseService {
static final DatabaseService _instance = DatabaseService._internal();
factory DatabaseService() => _instance;
DatabaseService._internal();
final Logger _logger = Logger('DatabaseService');
Database? _database;
/// 初始化数据库
/// 日志文件路径bin/mining_tasks.log
File get _logFile => File(PathUtils.binFile('mining_tasks.log'));
/// 初始化(确保 bin 目录存在)
Future<void> initialize() async {
try {
// Windows/Desktop: 使用 sqflite_common_ffi
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
// 确保 bin 目录存在
final binDir = Directory(PathUtils.binDir);
if (!await binDir.exists()) {
await binDir.create(recursive: true);
}
// 与 Go 版一致,尽量落到 ./bin/mining_task.db
final dbPath = PathUtils.binFile('mining_task.db');
_database = await databaseFactory.openDatabase(
dbPath,
options: OpenDatabaseOptions(
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE mining_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
coin TEXT NOT NULL,
algo TEXT NOT NULL,
pool TEXT NOT NULL,
pool_url TEXT NOT NULL,
wallet_address TEXT NOT NULL,
worker_id TEXT NOT NULL,
pool_user TEXT,
wallet_mining INTEGER NOT NULL,
end_timestamp INTEGER NOT NULL,
miner TEXT NOT NULL,
status TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
)
''');
},
),
);
_logger.info('数据库初始化成功');
// 日志文件可懒创建,不强制在这里创建
_logger.info('任务日志初始化完成(使用文件存储,不再使用 SQLite');
} catch (e) {
_logger.severe('数据库初始化失败: $e');
_logger.severe('任务日志初始化失败: $e');
rethrow;
}
}
/// 插入挖矿任务
/// 插入挖矿任务(在 .log 文件中追加一条记录)
Future<int> insertMiningTask(MiningTaskInfo task) async {
if (_database == null) {
await initialize();
}
try {
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
return await _database!.insert(
'mining_tasks',
{
final record = <String, dynamic>{
'coin': task.coin,
'algo': task.algo,
'pool': task.pool,
@@ -76,110 +57,184 @@ class DatabaseService {
'wallet_address': task.walletAddress,
'worker_id': task.workerId,
'pool_user': task.poolUser,
'wallet_mining': task.walletMining ? 1 : 0,
'wallet_mining': task.walletMining,
'end_timestamp': task.endTimestamp,
'miner': task.miner,
'status': 'running',
'created_at': now,
'updated_at': now,
},
);
};
final jsonLine = jsonEncode(record);
await _logFile.writeAsString('$jsonLine\n', mode: FileMode.append, flush: true);
return 1; // 返回值目前未被使用,保持兼容即可
} catch (e) {
_logger.severe('插入挖矿任务失败: $e');
rethrow;
}
}
/// 完成挖矿任务
Future<void> finishMiningTask(int taskId) async {
if (_database == null) {
/// 挖矿任务完成后,从 .log 文件中删除该任务
Future<void> finishMiningTask(MiningTaskInfo task) async {
await initialize();
}
try {
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
await _database!.update(
'mining_tasks',
{
'status': 'finished',
'updated_at': now,
},
where: 'id = ?',
whereArgs: [taskId],
);
if (!await _logFile.exists()) {
return;
}
final lines = await _logFile.readAsLines();
if (lines.isEmpty) return;
final List<String> keptLines = [];
for (final line in lines) {
if (line.trim().isEmpty) continue;
try {
final Map<String, dynamic> data = jsonDecode(line) as Map<String, dynamic>;
final existing = _taskFromJson(data);
// 如果与当前任务匹配,则跳过(即删除)
if (_isSameTask(existing, task)) {
continue;
}
keptLines.add(line);
} catch (_) {
// 解析失败的行保留,避免误删
keptLines.add(line);
}
}
await _logFile.writeAsString(keptLines.join('\n') + (keptLines.isEmpty ? '' : '\n'));
} catch (e) {
_logger.severe('完成挖矿任务失败: $e');
_logger.severe('完成挖矿任务(从日志中删除)失败: $e');
}
}
/// 加载未完成的挖矿任务
///
/// - 读取 .log 中所有任务;
/// - 删除已过期endTimestamp <= now的任务
/// - 返回最新的、尚未过期的任务(如果有)。
Future<MiningTaskInfo?> loadUnfinishedTask() async {
if (_database == null) {
await initialize();
}
try {
final results = await _database!.query(
'mining_tasks',
where: 'status = ?',
whereArgs: ['running'],
orderBy: 'created_at DESC',
limit: 1,
);
if (results.isEmpty) {
if (!await _logFile.exists()) {
return null;
}
final row = results.first;
final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final endTimestamp = row['end_timestamp'] as int;
if (currentTime >= endTimestamp) {
// 任务已过期,标记为完成
await finishMiningTask(row['id'] as int);
final lines = await _logFile.readAsLines();
if (lines.isEmpty) {
return null;
}
return MiningTaskInfo(
coin: row['coin'] as String,
algo: row['algo'] as String,
pool: row['pool'] as String,
poolUrl: row['pool_url'] as String,
walletAddress: row['wallet_address'] as String,
workerId: row['worker_id'] as String,
poolUser: row['pool_user'] as String?,
walletMining: (row['wallet_mining'] as int) == 1,
endTimestamp: endTimestamp,
miner: row['miner'] as String,
);
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final List<_StoredTask> validTasks = [];
for (final line in lines) {
if (line.trim().isEmpty) continue;
try {
final Map<String, dynamic> data = jsonDecode(line) as Map<String, dynamic>;
final task = _taskFromJson(data);
final createdAt = (data['created_at'] as int?) ?? task.endTimestamp;
// 过滤掉已过期任务
if (now >= task.endTimestamp) {
continue;
}
validTasks.add(_StoredTask(task: task, createdAt: createdAt));
} catch (e) {
_logger.warning('解析任务日志行失败,已跳过: $e, 原始行: $line');
}
}
// 重新写回仅包含未过期的任务
if (validTasks.isEmpty) {
await _logFile.writeAsString('');
return null;
} else {
// 按创建时间排序,取最新一条作为恢复任务
validTasks.sort((a, b) => b.createdAt.compareTo(a.createdAt));
final toKeep = validTasks;
final buffer = StringBuffer();
for (final t in toKeep) {
final record = _taskToJson(t.task, createdAt: t.createdAt);
buffer.writeln(jsonEncode(record));
}
await _logFile.writeAsString(buffer.toString());
return validTasks.first.task;
}
} catch (e) {
_logger.severe('加载挖矿任务失败: $e');
return null;
}
}
/// 获取任务历史
/// 获取任务历史(目前基于 .log 仅保存“当前/最近”任务,这里返回空列表以保持接口兼容)
Future<List<Map<String, dynamic>>> getTaskHistory({int limit = 100}) async {
if (_database == null) {
await initialize();
}
try {
return await _database!.query(
'mining_tasks',
orderBy: 'created_at DESC',
limit: limit,
);
} catch (e) {
_logger.severe('获取任务历史失败: $e');
// 如有需要,可以在未来扩展为持久化历史记录
return [];
}
/// 关闭(对文件存储无实际操作,保留接口以兼容旧代码)
Future<void> close() async {
// no-op
}
/// 关闭数据库
Future<void> close() async {
await _database?.close();
_database = null;
// === 内部工具方法 ===
MiningTaskInfo _taskFromJson(Map<String, dynamic> data) {
return MiningTaskInfo(
coin: data['coin'] as String,
algo: data['algo'] as String,
pool: (data['pool'] as String?) ?? '',
poolUrl: data['pool_url'] as String,
walletAddress: data['wallet_address'] as String,
workerId: data['worker_id'] as String,
poolUser: data['pool_user'] as String?,
walletMining: (data['wallet_mining'] as bool?) ??
((data['wallet_mining'] is int) ? (data['wallet_mining'] as int) == 1 : false),
endTimestamp: data['end_timestamp'] as int,
miner: data['miner'] as String,
);
}
Map<String, dynamic> _taskToJson(MiningTaskInfo task, {required int createdAt}) {
return <String, dynamic>{
'coin': task.coin,
'algo': task.algo,
'pool': task.pool,
'pool_url': task.poolUrl,
'wallet_address': task.walletAddress,
'worker_id': task.workerId,
'pool_user': task.poolUser,
'wallet_mining': task.walletMining,
'end_timestamp': task.endTimestamp,
'miner': task.miner,
'created_at': createdAt,
};
}
bool _isSameTask(MiningTaskInfo a, MiningTaskInfo b) {
return a.coin == b.coin &&
a.algo == b.algo &&
a.pool == b.pool &&
a.poolUrl == b.poolUrl &&
a.walletAddress == b.walletAddress &&
a.workerId == b.workerId &&
(a.poolUser ?? '') == (b.poolUser ?? '') &&
a.walletMining == b.walletMining &&
a.endTimestamp == b.endTimestamp &&
a.miner == b.miner;
}
}
class _StoredTask {
final MiningTaskInfo task;
final int createdAt;
_StoredTask({required this.task, required this.createdAt});
}

View File

@@ -231,6 +231,7 @@ class ClientProvider with ChangeNotifier {
/// 挖矿任务变化回调
void _onMiningTaskChanged(MiningTaskInfo? task) async {
final previousTask = _currentMiningTask;
_currentMiningTask = task;
if (task != null) {
@@ -246,6 +247,10 @@ class ClientProvider with ChangeNotifier {
} else {
// 停止挖矿
await _miningManager.stopMining();
// 挖矿任务完成后,从日志中删除该任务
if (previousTask != null) {
await _database.finishMiningTask(previousTask);
}
// 恢复持续挖矿
await _sustainMiner.resume();

View File

@@ -49,14 +49,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
code_assets:
dependency: transitive
description:
name: code_assets
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
collection:
dependency: transitive
description:
@@ -105,14 +97,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.5"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
@@ -144,22 +128,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.3"
hooks:
dependency: transitive
description:
name: hooks
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
http:
dependency: "direct main"
description:
@@ -256,14 +224,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.17.0"
native_toolchain_c:
dependency: transitive
description:
name: native_toolchain_c
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.17.4"
nested:
dependency: transitive
description:
@@ -357,30 +317,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.6"
sqflite_common_ffi:
dependency: "direct main"
description:
name: sqflite_common_ffi
sha256: c59fcdc143839a77581f7a7c4de018e53682408903a0a0800b95ef2dc4033eff
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.0+2"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: "00e5e65f8e9b556ed3d999ad310881c956ffb656ed96bea487a4c50ffdff6d14"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.3"
stack_trace:
dependency: transitive
description:
@@ -494,5 +430,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.10.0-0 <4.0.0"
dart: ">=3.8.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@@ -19,12 +19,8 @@ dependencies:
# 时间格式化
intl: ^0.18.1
# 数据库
# Windows 桌面端请使用 sqflite_common_ffi
sqflite_common_ffi: ^2.3.3
# 路径工具database.dart 使用 join
path: ^1.9.0
path: ^1.8.3
# 进程管理
process_run: ^0.12.5+2