From 1e22f5b452e0f0f007b773437c28c8e6a20fbed4 Mon Sep 17 00:00:00 2001 From: leon <916117771@qq.com> Date: Thu, 9 Apr 2026 17:51:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(ota):=20=E6=96=B0=E5=A2=9EOTA=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E5=8D=87=E7=BA=A7=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建OTA版本管理表结构,支持版本名称、构建号、APK文件等信息存储 - 实现后台OTA版本管理界面,包含新增、编辑、删除和发布功能 - 开发API接口用于设备版本检查和更新包下载 - 实现版本发布逻辑,自动归档旧版本并计算APK文件哈希值 - 添加强制更新、目标设备白名单和最低版本限制功能 - 集成文件上传和选择组件,支持APK文件管理 - 实现版本状态管理(草稿、已发布、已归档)和权限控制 --- application/admin/controller/ota/Version.php | 78 ++++++++++ application/admin/lang/zh-cn/ota/version.php | 17 ++ application/admin/model/ota/Version.php | 66 ++++++++ application/admin/validate/ota/Version.php | 27 ++++ application/admin/view/ota/version/add.html | 89 +++++++++++ application/admin/view/ota/version/edit.html | 89 +++++++++++ application/admin/view/ota/version/index.html | 46 ++++++ application/api/controller/Ota.php | 147 ++++++++++++++++++ application/common/model/OtaVersion.php | 57 +++++++ public/assets/js/backend/ota/version.js | 93 +++++++++++ sql/ota_version.sql | 22 +++ 11 files changed, 731 insertions(+) create mode 100644 application/admin/controller/ota/Version.php create mode 100644 application/admin/lang/zh-cn/ota/version.php create mode 100644 application/admin/model/ota/Version.php create mode 100644 application/admin/validate/ota/Version.php create mode 100644 application/admin/view/ota/version/add.html create mode 100644 application/admin/view/ota/version/edit.html create mode 100644 application/admin/view/ota/version/index.html create mode 100644 application/api/controller/Ota.php create mode 100644 application/common/model/OtaVersion.php create mode 100644 public/assets/js/backend/ota/version.js create mode 100644 sql/ota_version.sql diff --git a/application/admin/controller/ota/Version.php b/application/admin/controller/ota/Version.php new file mode 100644 index 0000000..f3e9e60 --- /dev/null +++ b/application/admin/controller/ota/Version.php @@ -0,0 +1,78 @@ +model = new \app\admin\model\ota\Version; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + + /** + * 发布版本 + * + * 将指定版本设为 published 状态,同时自动归档其他已发布版本, + * 并计算 APK 文件大小和 SHA-256 哈希值。 + * + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function publish() + { + $id = $this->request->param('ids'); + if (!$id) { + $this->error('参数错误'); + } + + // 先归档其他已发布版本 + $this->model->where('status', 'published')->update(['status' => 'archived']); + + // 获取待发布版本 + $row = $this->model->get($id); + if (!$row) { + $this->error('记录不存在'); + } + + // 计算 APK 文件信息 + if ($row['apk_file']) { + $fullPath = root_path() . 'public' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . ltrim($row['apk_file'], '/'); + if (file_exists($fullPath)) { + $row->file_size = filesize($fullPath); + $row->sha256 = hash_file('sha256', $fullPath); + } + } + + $row->status = 'published'; + $row->save(); + + $this->success('发布成功'); + } + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/lang/zh-cn/ota/version.php b/application/admin/lang/zh-cn/ota/version.php new file mode 100644 index 0000000..6c8b505 --- /dev/null +++ b/application/admin/lang/zh-cn/ota/version.php @@ -0,0 +1,17 @@ + '版本名称,如 1.1.0', + 'Version_code' => '版本号(构建号),用于比较', + 'Apk_file' => 'APK 文件路径(相对于 upload 目录)', + 'File_size' => '文件大小(字节)', + 'Sha256' => 'APK 文件 SHA-256 哈希值', + 'Update_log' => '更新日志', + 'Is_force_update' => '是否强制更新:0=否,1=是', + 'Status' => '状态:草稿/已发布/已归档', + 'Target_devices' => '目标设备 IMEI,逗号分隔;NULL=所有设备', + 'Min_version' => '最低可升级版本,NULL=不限制', + 'Creator_id' => '创建人ID', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间' +]; diff --git a/application/admin/model/ota/Version.php b/application/admin/model/ota/Version.php new file mode 100644 index 0000000..f1c14d0 --- /dev/null +++ b/application/admin/model/ota/Version.php @@ -0,0 +1,66 @@ + __('Draft'), 'published' => __('Published'), 'archived' => __('Archived')]; + } + + /** + * 获取状态文本 + * + * @param string $value + * @param array $data + * @return string + */ + public function getStatusTextAttr($value, $data) + { + $value = $value ?: ($data['status'] ?? ''); + $list = $this->getStatusList(); + return $list[$value] ?? ''; + } + + /** + * 获取强制更新状态文本 + * + * @param string $value + * @param array $data + * @return string + */ + public function getIsForceUpdateTextAttr($value, $data) + { + return ($data['is_force_update'] ?? 0) == 1 ? '是' : '否'; + } +} diff --git a/application/admin/validate/ota/Version.php b/application/admin/validate/ota/Version.php new file mode 100644 index 0000000..75e8893 --- /dev/null +++ b/application/admin/validate/ota/Version.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/view/ota/version/add.html b/application/admin/view/ota/version/add.html new file mode 100644 index 0000000..d6de741 --- /dev/null +++ b/application/admin/view/ota/version/add.html @@ -0,0 +1,89 @@ +
diff --git a/application/admin/view/ota/version/edit.html b/application/admin/view/ota/version/edit.html new file mode 100644 index 0000000..16babf0 --- /dev/null +++ b/application/admin/view/ota/version/edit.html @@ -0,0 +1,89 @@ + diff --git a/application/admin/view/ota/version/index.html b/application/admin/view/ota/version/index.html new file mode 100644 index 0000000..a0fc51f --- /dev/null +++ b/application/admin/view/ota/version/index.html @@ -0,0 +1,46 @@ +' + value.substring(0, 16) + '...';
+ }},
+ {field: 'is_force_update', title: '强制更新', searchList: {"0":"否","1":"是"}, formatter: Table.api.formatter.toggle},
+ {field: 'status', title: '状态', searchList: {"draft": __('Draft'), "published": __('Published'), "archived": __('Archived')}, formatter: Table.api.formatter.status},
+ {field: 'target_devices', title: '目标设备', operate: false, visible: false},
+ {field: 'min_version', title: '最低版本', operate: 'LIKE', visible: false},
+ {field: 'creator_id', title: __('Creator_id'), visible: false},
+ {field: 'createtime', title: '创建时间', operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime},
+ {field: 'updatetime', title: '更新时间', operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime, visible: false},
+ {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: function (value, row, index) {
+ var buttons = [];
+
+ // 发布按钮:仅草稿和已归档版本可发布
+ if (row.status === 'draft' || row.status === 'archived') {
+ buttons.push({
+ name: 'publish',
+ text: '发布',
+ title: '发布此版本',
+ classname: 'btn btn-xs btn-success btn-ajax',
+ url: 'ota/version/publish?ids=' + row.id,
+ confirm: '确认发布此版本?发布后其他已发布版本将自动归档。',
+ success: function () {
+ table.bootstrapTable('refresh');
+ }
+ });
+ }
+
+ // 默认操作按钮
+ var defaultButtons = Table.api.formatter.operate(value, row, index);
+ return buttons.join(' ') + ' ' + defaultButtons;
+ }}
+ ]
+ ]
+ });
+
+ // 为表格绑定事件
+ Table.api.bindevent(table);
+ },
+ add: function () {
+ Controller.api.bindevent();
+ },
+ edit: function () {
+ Controller.api.bindevent();
+ },
+ api: {
+ bindevent: function () {
+ Form.api.bindevent($("form[role=form]"));
+ }
+ }
+ };
+ return Controller;
+});
diff --git a/sql/ota_version.sql b/sql/ota_version.sql
new file mode 100644
index 0000000..194b6be
--- /dev/null
+++ b/sql/ota_version.sql
@@ -0,0 +1,22 @@
+-- OTA 版本管理表
+-- 用于芬太尼智能药品管理柜的 OTA 远程升级功能
+
+CREATE TABLE IF NOT EXISTS `ota_version` (
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `version_name` VARCHAR(20) NOT NULL COMMENT '版本名称,如 1.1.0',
+ `version_code` INT(11) UNSIGNED NOT NULL COMMENT '版本号(构建号),用于比较',
+ `apk_file` VARCHAR(255) NOT NULL COMMENT 'APK 文件路径(相对于 upload 目录)',
+ `file_size` BIGINT(20) UNSIGNED DEFAULT NULL COMMENT '文件大小(字节)',
+ `sha256` CHAR(64) DEFAULT NULL COMMENT 'APK 文件 SHA-256 哈希值',
+ `update_log` TEXT NOT NULL COMMENT '更新日志',
+ `is_force_update` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否强制更新:0=否,1=是',
+ `status` ENUM('draft','published','archived') NOT NULL DEFAULT 'draft' COMMENT '状态:草稿/已发布/已归档',
+ `target_devices` TEXT DEFAULT NULL COMMENT '目标设备 IMEI,逗号分隔;NULL=所有设备',
+ `min_version` VARCHAR(20) DEFAULT NULL COMMENT '最低可升级版本,NULL=不限制',
+ `creator_id` INT(11) UNSIGNED DEFAULT NULL COMMENT '创建人ID',
+ `createtime` INT(10) UNSIGNED DEFAULT NULL COMMENT '创建时间',
+ `updatetime` INT(10) UNSIGNED DEFAULT NULL COMMENT '更新时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `idx_version_code` (`version_code`),
+ KEY `idx_status_version` (`status`, `version_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='OTA 版本管理表';