Просмотр исходного кода

Merge branch 'master' into feature/cdb/微信登录+用户中心

chendeben 1 год назад
Родитель
Сommit
3a3d7e2ae7

+ 158 - 0
.cursor/rules/cursorrules.mdc

@@ -0,0 +1,158 @@
+---
+description: 
+globs: 
+alwaysApply: true
+---
+# 鸿蒙ArkTS编程规则与最佳实践
+
+## 语法差异规则:ArkTS vs TypeScript
+
+### 1. 空值检查规则
+```typescript
+// ❌ 错误: 未对可能为null的对象执行检查
+this.dbObject.executeSql(sql);
+
+// ✅ 正确: 执行null检查
+if (this.dbObject) {
+  this.dbObject.executeSql(sql);
+}
+```
+
+### 2. 解构赋值规则
+```typescript
+// ❌ 错误: ArkTS不支持解构赋值语法
+for (const [key, value] of Object.entries(obj)) {
+  // 处理逻辑
+}
+
+// ✅ 正确: 使用传统循环方式
+const keys = Object.keys(obj);
+for (let i = 0; i < keys.length; i++) {
+  const key = keys[i];
+  const value = obj[key];
+  // 处理逻辑
+}
+```
+
+### 3. 异步API调用规则
+```typescript
+// ❌ 错误: 回调参数类型不匹配
+dbStore.executeSql(sql, (err, result: ResultSet) => {
+  // 处理逻辑
+});
+
+// ✅ 正确: 使用Promise模式
+dbStore.executeSql(sql)
+  .then(() => {
+    // 成功处理
+  })
+  .catch((err: Error) => {
+    // 错误处理
+    console.log(err.message);
+  });
+```
+
+### 4. 计算属性名规则
+```typescript
+// ❌ 错误: 不支持计算属性名语法
+const obj = { [CONSTANT.KEY]: value };
+
+// ✅ 正确: 使用对象属性赋值语法
+const obj = {};
+obj[CONSTANT.KEY] = value;
+```
+
+### 5. 数据类型规则
+```typescript
+// ❌ 错误: 使用any或未指定泛型类型
+const items = new Set();
+const map = new Map();
+
+// ✅ 正确: 明确指定泛型类型
+const items = new Set<string>();
+const map = new Map<string, number>();
+```
+
+### 6. 错误对象类型规则
+```typescript
+// ❌ 错误: 使用隐式any类型的错误对象
+try {
+  // 代码
+} catch (e) {
+  console.log(`错误: ${e}`);
+}
+
+// ✅ 正确: 明确指定错误对象的类型
+try {
+  // 代码
+} catch (e: Error) {
+  console.log(`错误: ${e.message}`);
+}
+```
+
+## 数据库操作最佳实践
+
+### 1. 表结构升级
+```typescript
+// ❌ 错误: 依赖回调处理的表结构升级
+this.rdbStore.executeSql(tableInfoQuery, (err, result) => {
+  // 处理逻辑
+});
+
+// ✅ 正确: 使用Promise模式并独立处理每个列的添加操作
+this.rdbStore.executeSql(tableInfoQuery)
+  .then(() => {
+    // 为每个需要添加的列单独执行ALTER TABLE
+    Object.keys(columnsToAdd).forEach(column => {
+      const type = columnsToAdd[column];
+      this.rdbStore.executeSql(`ALTER TABLE ${tableName} ADD COLUMN ${column} ${type}`)
+        .then(() => { /* 成功处理 */ })
+        .catch((err: Error) => { /* 错误处理 */ });
+    });
+  });
+```
+
+### 2. 资源释放
+```typescript
+// ❌ 错误: 未关闭ResultSet
+this.rdbStore.query(predicates, (resultSet) => {
+  // 处理逻辑
+});
+
+// ✅ 正确: 确保关闭ResultSet
+this.rdbStore.query(predicates, (resultSet) => {
+  try {
+    // 处理逻辑
+  } finally {
+    resultSet.close();
+  }
+});
+```
+
+## 对象字面量规则
+
+### 1. 复杂对象初始化
+```typescript
+// ❌ 错误: 不支持复杂对象字面量初始化
+const config = {
+  complex: {
+    nested: {
+      value: someValue
+    }
+  }
+};
+
+// ✅ 正确: 分步创建复杂对象
+const config = {};
+config.complex = {};
+config.complex.nested = {};
+config.complex.nested.value = someValue;
+```
+
+## 命名规范
+
+- 类名: PascalCase (例如 MediaTable)
+- 方法名: camelCase (例如 queryByParentPath)
+- 常量: UPPER_SNAKE_CASE (例如 DB_COLUMNS.FILE_PATH)
+- 私有属性: _camelCase (例如 _dbStore)
+

+ 163 - 63
entry/src/main/ets/common/util/MediaTable.ets

@@ -5,6 +5,67 @@ import Logger from './Logger';
 import RdbUtils from './RdbUtils';
 import RdbUtils from './RdbUtils';
 import { Utility } from './Utility';
 import { Utility } from './Utility';
 
 
+/**
+ * 数据库字段常量接口定义
+ */
+interface DBColumnsInterface {
+  ID: string;
+  NAME: string;
+  FILE_PATH: string;
+  TYPE: string;
+  VIDEO_SIZE: string;
+  C_TIME: string;
+  PARENT_PATH: string;
+  IS_FAV: string;
+  PIXEL_MAP_PATH: string;
+  ARTIST: string;
+  ALBUM: string;
+  FILE_NAME: string;
+  SIZE: string;
+  DURATION: string;
+  MIME_TYPE: string;
+  TRACK_COUNT: string;
+  SAMPLE_RATE: string;
+  LAST_PLAYED_STR: string;
+  PLAY_COUNT: string;
+  LYRIC_CONTENT: string;
+}
+
+/**
+ * 媒体元数据接口定义
+ */
+export interface MediaMetadata {
+  duration?: string;
+  mimeType?: string;
+  sampleRate?: string;
+  trackCount?: string;
+}
+
+/**
+ * 数据库字段常量,避免硬编码
+ */
+const DB_COLUMNS: DBColumnsInterface = {
+  ID: 'id',
+  NAME: 'name', 
+  FILE_PATH: 'filePath',
+  TYPE: 'mtype',
+  VIDEO_SIZE: 'videoSize',
+  C_TIME: 'cTime',
+  PARENT_PATH: 'parentPath',
+  IS_FAV: 'isFav',
+  PIXEL_MAP_PATH: 'pixelMapPath',
+  ARTIST: 'artist',
+  ALBUM: 'album',
+  FILE_NAME: 'fileName',
+  SIZE: 'size',
+  DURATION: 'duration',
+  MIME_TYPE: 'mimeType',
+  TRACK_COUNT: 'trackCount',
+  SAMPLE_RATE: 'sampleRate',
+  LAST_PLAYED_STR: 'lastPlayedStr',
+  PLAY_COUNT: 'playCount',
+  LYRIC_CONTENT: 'lyricContent'
+};
 
 
 export default class MediaTable {
 export default class MediaTable {
   private accountTable = new RdbUtils(RdbUtils.MEDIA_TABLE.tableName, RdbUtils.MEDIA_TABLE.sqlCreate,
   private accountTable = new RdbUtils(RdbUtils.MEDIA_TABLE.tableName, RdbUtils.MEDIA_TABLE.sqlCreate,
@@ -228,15 +289,12 @@ export default class MediaTable {
         console.log(`${RdbUtils.RDB_TAG}` + 'Query no results!');
         console.log(`${RdbUtils.RDB_TAG}` + 'Query no results!');
         callback([]);
         callback([]);
       } else {
       } else {
-
-
         const result = this.parseResultSetToVideoItems(resultSet);
         const result = this.parseResultSetToVideoItems(resultSet);
         callback(result);
         callback(result);
       }
       }
     });
     });
   }
   }
 
 
-
   // 新增方法:根据parentPath查询数据
   // 新增方法:根据parentPath查询数据
   public queryByParentPath(path: string, callback: (result: VideoItem[]) => void) {
   public queryByParentPath(path: string, callback: (result: VideoItem[]) => void) {
     try {
     try {
@@ -249,14 +307,12 @@ export default class MediaTable {
         const result = this.parseResultSetToVideoItems(resultSet);
         const result = this.parseResultSetToVideoItems(resultSet);
         callback(result);
         callback(result);
       });
       });
-    }catch (err) {
-      Logger.error(` onecold testtag queryByParentPath: ${err.code}  - ${err.message}`);
-
+    } catch (err) {
+      Logger.error(`查询parentPath失败: ${err.message}`);
+      callback([]);
     }
     }
-    // 1. 构建查询条件
-
   }
   }
-
+  
   // 查询所有艺术家及其歌曲(返回Map结构:artist -> VideoItem[])
   // 查询所有艺术家及其歌曲(返回Map结构:artist -> VideoItem[])
   public queryArtistsWithSongs(callback: (result: Map<string, VideoItem[]>) => void) {
   public queryArtistsWithSongs(callback: (result: Map<string, VideoItem[]>) => void) {
     // 1. 查询去重的艺术家列表(非空)
     // 1. 查询去重的艺术家列表(非空)
@@ -290,9 +346,6 @@ export default class MediaTable {
     });
     });
   }
   }
 
 
-
-
-
   // 查询所有专辑及其歌曲(返回Map结构:album -> VideoItem[])
   // 查询所有专辑及其歌曲(返回Map结构:album -> VideoItem[])
   public queryAlbumsWithSongs(callback: (result: Map<string, VideoItem[]>) => void) {
   public queryAlbumsWithSongs(callback: (result: Map<string, VideoItem[]>) => void) {
     // 1. 查询去重的专辑列表(非空)
     // 1. 查询去重的专辑列表(非空)
@@ -326,8 +379,6 @@ export default class MediaTable {
     });
     });
   }
   }
 
 
-
-
   // 解析去重列数据(如artist/album)
   // 解析去重列数据(如artist/album)
   private parseDistinctColumn(resultSet: relationalStore.ResultSet, columnName: string): string[] {
   private parseDistinctColumn(resultSet: relationalStore.ResultSet, columnName: string): string[] {
     const uniqueValues = new Set<string>();  // 使用Set特性自动去重
     const uniqueValues = new Set<string>();  // 使用Set特性自动去重
@@ -347,69 +398,122 @@ export default class MediaTable {
     return Array.from(uniqueValues);   // Set转数组
     return Array.from(uniqueValues);   // Set转数组
   }
   }
 
 
-  // 将ResultSet解析为VideoItem数组(复用原有逻辑)
+  // 将ResultSet解析为VideoItem数组
   private parseResultSetToVideoItems(resultSet: relationalStore.ResultSet): VideoItem[] {
   private parseResultSetToVideoItems(resultSet: relationalStore.ResultSet): VideoItem[] {
     const items: VideoItem[] = [];
     const items: VideoItem[] = [];
 
 
     try {
     try {
-      // 逆向遍历方案(规避鸿蒙API特性)
-      while (resultSet.goToNextRow())  {  // 自动边界检查
-        const item = this.buildVideoItem(resultSet);
-        // let id = resultSet.getString(resultSet.getColumnIndex('id'));
-        // let name = resultSet.getString(resultSet.getColumnIndex('name'));
-        // let filePath  = resultSet.getString(resultSet.getColumnIndex('filePath'));
-        // let type  = resultSet.getDouble(resultSet.getColumnIndex('mtype'));
-        // let videoSize  = resultSet.getDouble(resultSet.getColumnIndex('videoSize'));
-        // let cTime = resultSet.getString(resultSet.getColumnIndex('cTime'));
-        // let size = resultSet.getString(resultSet.getColumnIndex('size'));
-        // let parentPath = resultSet.getString(resultSet.getColumnIndex('parentPath'));
-        //
-        // let pixelMapToString = resultSet.getString(resultSet.getColumnIndex('pixelMapToString'));
-        // let artist  = resultSet.getString(resultSet.getColumnIndex('artist'));
-        // let album  = resultSet.getString(resultSet.getColumnIndex('album'));
-        // let fileName  = resultSet.getString(resultSet.getColumnIndex('fileName'));
-        // let item = new VideoItem(name, id, filePath, type, videoSize, cTime,
-        //   undefined,size,pixelMapToString,artist,album,fileName);
-        items.push(item);
+      // 检查结果集是否有效
+      if (resultSet && resultSet.rowCount > 0) {
+        while (resultSet.goToNextRow()) {
+          const item = this.buildVideoItem(resultSet);
+          items.push(item);
+        }
       }
       }
-      // 释放数据集的内存
-      resultSet.close();
     } catch (err) {
     } catch (err) {
-      Logger.error(` onecold testtag parseResultSetToVideoItems: ${err.code} - ${err.message}`);
+      Logger.error(`解析结果集出错: ${err.message}`);
+    } finally {
+      // 确保结果集被关闭
+      if (resultSet) {
+        resultSet.close();
+      }
     }
     }
 
 
-
     return items;
     return items;
   }
   }
 
 
   private buildVideoItem(rs: relationalStore.ResultSet): VideoItem {
   private buildVideoItem(rs: relationalStore.ResultSet): VideoItem {
     // 添加空值保护
     // 添加空值保护
-    const safeGet = (col: string) => rs.getColumnIndex(col)  >= 0 ? rs.getString(rs.getColumnIndex(col))  : '';
-
-    let item =  new VideoItem(
-      safeGet('name'),
-      safeGet('id'),
-      safeGet('filePath'),
-      rs.getDouble(rs.getColumnIndex('mtype'))  || 0,
-      rs.getDouble(rs.getColumnIndex('videoSize'))  || 0,safeGet('cTime'),undefined,safeGet('size'),
-      safeGet('pixelMapPath'),safeGet('artist'),
-      safeGet('album'),safeGet('fileName')
+    const safeGet = (col: string) => {
+      const index = rs.getColumnIndex(col);
+      return index >= 0 ? rs.getString(index) || '' : '';
+    };
+    
+    const safeGetNumber = (col: string) => {
+      const index = rs.getColumnIndex(col);
+      return index >= 0 ? rs.getDouble(index) || 0 : 0;
+    };
+
+    let item = new VideoItem(
+      safeGet(DB_COLUMNS.NAME),
+      safeGet(DB_COLUMNS.ID),
+      safeGet(DB_COLUMNS.FILE_PATH),
+      safeGetNumber(DB_COLUMNS.TYPE),
+      safeGetNumber(DB_COLUMNS.VIDEO_SIZE),
+      safeGet(DB_COLUMNS.C_TIME),
+      undefined,
+      safeGet(DB_COLUMNS.SIZE),
+      safeGet(DB_COLUMNS.PIXEL_MAP_PATH),
+      safeGet(DB_COLUMNS.ARTIST),
+      safeGet(DB_COLUMNS.ALBUM),
+      safeGet(DB_COLUMNS.FILE_NAME)
     );
     );
-    item.isFav = rs.getDouble(rs.getColumnIndex('isFav'));
-
-    item.duration = safeGet('duration');
-    item.mimeType = safeGet('mimeType');
-    item.trackCount =  safeGet('trackCount');
-    item.sampleRate = safeGet('sampleRate');
-    item.lastPlayedStr =safeGet('lastPlayedStr');
-    // item.playCount = rs.getDouble(rs.getColumnIndex('playCount'));
-    item.lyricContent =  safeGet('lyricContent')
-
-    return item
+    
+    // 设置额外属性,添加安全检查
+    item.isFav = safeGetNumber(DB_COLUMNS.IS_FAV);
+    item.duration = safeGet(DB_COLUMNS.DURATION);
+    item.mimeType = safeGet(DB_COLUMNS.MIME_TYPE);
+    item.trackCount = safeGet(DB_COLUMNS.TRACK_COUNT);
+    item.sampleRate = safeGet(DB_COLUMNS.SAMPLE_RATE);
+    item.lastPlayedStr = safeGet(DB_COLUMNS.LAST_PLAYED_STR);
+    item.playCount = safeGetNumber(DB_COLUMNS.PLAY_COUNT);
+    item.lyricContent = safeGet(DB_COLUMNS.LYRIC_CONTENT);
+
+    return item;
   }
   }
 
 
+  // 根据文件路径查询数据
+  public queryByFilePath(filePath: string, callback: (result: VideoItem[]) => void) {
+    try {
+      const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      predicates.equalTo('filePath', filePath);
 
 
+      // 执行查询并处理结果
+      this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
+        // 复用已有的解析逻辑
+        const result = this.parseResultSetToVideoItems(resultSet);
+        callback(result);
+      });
+    } catch (err) {
+      Logger.error(`查询文件路径失败 - filePath: ${filePath}, error: ${err.code} - ${err.message}`);
+      callback([]);
+    }
+  }
 
 
+  // 更新媒体文件的元数据信息(采样率、MIME类型等)
+  public updateMediaMetadata(filePath: string, metadata: MediaMetadata, callback: (success: boolean) => void) {
+    try {
+      // 构建查询条件
+      const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      predicates.equalTo('filePath', filePath);
+      
+      // 构建更新值
+      const valuesToUpdate: relationalStore.ValuesBucket = {};
+      if (metadata.duration) {
+        valuesToUpdate.duration = metadata.duration;
+      }
+      if (metadata.mimeType) {
+        valuesToUpdate.mimeType = metadata.mimeType;
+      }
+      if (metadata.sampleRate) {
+        valuesToUpdate.sampleRate = metadata.sampleRate;
+      }
+      if (metadata.trackCount) {
+        valuesToUpdate.trackCount = metadata.trackCount;
+      }
+      
+      // 执行更新
+      if (Object.keys(valuesToUpdate).length > 0) {
+        this.accountTable.updateData(predicates, valuesToUpdate, callback);
+      } else {
+        Logger.info('No metadata to update');
+        callback(false);
+      }
+    } catch (err) {
+      Logger.error(`更新媒体元数据失败: ${err.message}`);
+      callback(false);
+    }
+  }
 }
 }
 
 
 function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
 function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
@@ -423,9 +527,6 @@ function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
   obj.parentPath = item.parentPath;
   obj.parentPath = item.parentPath;
   obj.isFav = item.isFav;
   obj.isFav = item.isFav;
 
 
-  // if(item.pixelMapToString){
-  //   obj.pixelMapToString = item.pixelMapToString;
-  // }
   if(item.artist){
   if(item.artist){
     obj.artist = item.artist;
     obj.artist = item.artist;
   }
   }
@@ -442,7 +543,6 @@ function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
     obj.pixelMapPath = item.pixelMapPath;
     obj.pixelMapPath = item.pixelMapPath;
   }
   }
 
 
-
   if(item.duration){
   if(item.duration){
     obj.duration = item.duration;
     obj.duration = item.duration;
   }
   }

+ 97 - 36
entry/src/main/ets/common/util/RdbUtils.ets

@@ -1,4 +1,3 @@
-
 import relationalStore from '@ohos.data.relationalStore';
 import relationalStore from '@ohos.data.relationalStore';
 import { LogUtil, StrUtil } from '@pura/harmony-utils';
 import { LogUtil, StrUtil } from '@pura/harmony-utils';
 import Logger from './Logger';
 import Logger from './Logger';
@@ -52,10 +51,16 @@ export default class RdbUtils {
       '        parentPath TEXT,\n' +
       '        parentPath TEXT,\n' +
       '        isFav INTEGER,\n' +
       '        isFav INTEGER,\n' +
       '        pixelMapPath TEXT,\n' +
       '        pixelMapPath TEXT,\n' +
-      '        pixelMapToString TEXT' +
-
+      '        pixelMapToString TEXT,\n' +
+      '        duration TEXT,\n' +
+      '        sampleRate TEXT,\n' +
+      '        playCount INTEGER DEFAULT 0,\n' +
+      '        lastPlayedStr TEXT,\n' +
+      '        trackCount TEXT,\n' +
+      '        lyricContent TEXT,\n' +
+      '        mimeType TEXT' +
       ')',
       ')',
-    columns: ['id', 'name', 'filePath','mtype', 'videoSize', 'cTime','size', 'artist', 'album','fileName','parentPath','isFav','pixelMapPath','pixelMapToString']
+    columns: ['id', 'name', 'filePath','mtype', 'videoSize', 'cTime','size', 'artist', 'album','fileName','parentPath','isFav','pixelMapPath','pixelMapToString', 'duration', 'mimeType', 'trackCount', 'sampleRate', 'lastPlayedStr', 'playCount', 'lyricContent']
   };
   };
 
 
   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
@@ -77,46 +82,95 @@ export default class RdbUtils {
       callback();
       callback();
       return
       return
     }
     }
-    // let context: Context = getContext(this) as Context;
+    
     relationalStore.getRdbStore(context, RdbUtils.STORE_CONFIG, (err, rdb) => {
     relationalStore.getRdbStore(context, RdbUtils.STORE_CONFIG, (err, rdb) => {
       if (err) {
       if (err) {
         Logger.error(RdbUtils.RDB_TAG, `gerRdbStore() failed, err: ${err}`);
         Logger.error(RdbUtils.RDB_TAG, `gerRdbStore() failed, err: ${err}`);
         return;
         return;
       }
       }
       this.rdbStore = rdb;
       this.rdbStore = rdb;
+      
+      // 先创建表(如果不存在)
       this.rdbStore.executeSql(this.sqlCreateTable);
       this.rdbStore.executeSql(this.sqlCreateTable);
-      if (this.rdbStore.version  == 0) {
-          // 升级到版本1,添加列
-          // ✅ SQLite要求每列单独执行ALTER TABLE
-          const alterColumns = [
-            "ALTER TABLE mediaTable ADD COLUMN duration TEXT",
-            "ALTER TABLE mediaTable ADD COLUMN sampleRate TEXT",
-            "ALTER TABLE mediaTable ADD COLUMN playCount INTEGER DEFAULT 0",
-            "ALTER TABLE mediaTable ADD COLUMN lastPlayedStr TEXT",
-            "ALTER TABLE mediaTable ADD COLUMN trackCount TEXT",
-            "ALTER TABLE mediaTable ADD COLUMN lyricContent TEXT",
-            "ALTER TABLE mediaTable ADD COLUMN mimeType TEXT"
-          ];
-        try {
-          // 逐列执行添加
-          alterColumns.forEach(sql  => {
-            if(this.rdbStore)
-              this.rdbStore.executeSql(sql);
-          });
-          this.rdbStore.version = 1
-          LogUtil.info('onecold Upgrade  database from version 0 to 1 success.');
-        } catch (e) {
-          Logger.error(RdbUtils.RDB_TAG,  `Upgrade database failed: ${e}`);
-          // 注意:升级失败,可能需要处理,这里我们记录错误,但继续执行回调
-        }
+      
+      // 检查数据库版本并更新列
+      try {
+        // 检查表结构
+        this.checkAndUpdateTableColumns();
+        Logger.info(RdbUtils.RDB_TAG, `数据库表结构检查完成`);
+      } catch (e) {
+        Logger.error(RdbUtils.RDB_TAG, `检查表结构时出错: ${e.message}`);
       }
       }
 
 
-
-      // Logger.info(RdbUtils.RDB_TAG, 'getRdbStore() finished.');
       callback();
       callback();
     });
     });
   }
   }
-
+  
+  /**
+   * 检查并更新表列结构
+   * 使用表信息查询和ALTER TABLE添加缺失的列
+   */
+  private checkAndUpdateTableColumns() {
+    if (!this.rdbStore) {
+      Logger.error(RdbUtils.RDB_TAG, `数据库连接未初始化`);
+      return;
+    }
+    
+    try {
+      // 查询表信息获取现有列
+      const tableInfoQuery = `PRAGMA table_info(${this.tableName})`;
+      // 使用Promise模式代替回调
+      this.rdbStore.executeSql(tableInfoQuery)
+        .then(() => {
+          // 定义应该存在的列及其类型
+          const requiredColumns: Record<string, string> = {
+            'duration': 'TEXT',
+            'sampleRate': 'TEXT',
+            'playCount': 'INTEGER DEFAULT 0',
+            'lastPlayedStr': 'TEXT',
+            'trackCount': 'TEXT',
+            'lyricContent': 'TEXT',
+            'mimeType': 'TEXT'
+          };
+          
+          // 逐个添加列,不依赖于检查结果
+          if (this.rdbStore) {
+            // 遍历映射
+            const columnEntries = Object.keys(requiredColumns);
+            for (let i = 0; i < columnEntries.length; i++) {
+              const column = columnEntries[i];
+              const type = requiredColumns[column];
+              
+              const alterSql = `ALTER TABLE ${this.tableName} ADD COLUMN ${column} ${type}`;
+              try {
+                // 对每个列使用Promise模式
+                this.rdbStore.executeSql(alterSql)
+                  .then(() => {
+                    Logger.info(RdbUtils.RDB_TAG, `成功添加列: ${column}`);
+                  })
+                  .catch((alterErr: Error) => {
+                    // 列可能已经存在,这是预期的错误
+                    Logger.info(RdbUtils.RDB_TAG, `列 ${column} 可能已存在: ${alterErr.message}`);
+                  });
+              } catch (e) {
+                Logger.error(RdbUtils.RDB_TAG, `添加列 ${column} 出错: ${e.message}`);
+              }
+            }
+            
+            // 设置数据库版本
+            if (this.rdbStore) {
+              this.rdbStore.version = 1;
+              Logger.info(RdbUtils.RDB_TAG, `数据库升级完成,版本设置为 1`);
+            }
+          }
+        })
+        .catch((err: Error) => {
+          Logger.error(RdbUtils.RDB_TAG, `获取表信息失败: ${err.message}`);
+        });
+    } catch (e) {
+      Logger.error(RdbUtils.RDB_TAG, `检查表结构时出错: ${e.message}`);
+    }
+  }
 
 
   //检查是否存在相同id的记录,存在则不插入,进一步判断pixelMapPath字段,如果旧值为空而新值不为空,则更新该字段。
   //检查是否存在相同id的记录,存在则不插入,进一步判断pixelMapPath字段,如果旧值为空而新值不为空,则更新该字段。
   async insertData(data: relationalStore.ValuesBucket, callback: Function = () => {},cover_api?:string) {
   async insertData(data: relationalStore.ValuesBucket, callback: Function = () => {},cover_api?:string) {
@@ -253,15 +307,22 @@ export default class RdbUtils {
       Logger.info(RdbUtils.RDB_TAG, 'query() has no callback!');
       Logger.info(RdbUtils.RDB_TAG, 'query() has no callback!');
       return;
       return;
     }
     }
+    
     if (this.rdbStore) {
     if (this.rdbStore) {
-      this.rdbStore.query(predicates, this.columns, (err, resultSet) => {
+      // 使用安全的列集合查询
+      // 首先仅查询基本列(确保100%存在)
+      const safeColumns = ['id', 'name', 'filePath', 'mtype', 'videoSize', 'cTime', 
+                           'size', 'artist', 'album', 'fileName', 'parentPath', 
+                           'isFav', 'pixelMapPath', 'pixelMapToString'];
+      
+      this.rdbStore.query(predicates, safeColumns, (err, resultSet) => {
         if (err) {
         if (err) {
-          Logger.error(RdbUtils.RDB_TAG, `query() failed, err:  ${err}`);
+          Logger.error(RdbUtils.RDB_TAG, `query() failed, err: ${err}`);
+          callback(null);
           return;
           return;
         }
         }
-        // Logger.info(RdbUtils.RDB_TAG, 'query() finished.');
+        
         callback(resultSet);
         callback(resultSet);
-        resultSet.close();
       });
       });
     }
     }
   }
   }

+ 38 - 4
entry/src/main/ets/common/util/Utility.ets

@@ -125,11 +125,45 @@ export class Utility {
   }
   }
 
 
   static convertToKHz(sampleRateHz: string|undefined): string {
   static convertToKHz(sampleRateHz: string|undefined): string {
-    if(StrUtil.isEmpty(sampleRateHz)||sampleRateHz ===undefined)
-      return '0KHz'
+    if(StrUtil.isEmpty(sampleRateHz) || sampleRateHz === undefined || sampleRateHz === "0") {
+      return '未知';
+    }
+
+    try {
+      const sampleRateNum = Number(sampleRateHz);
+      if (isNaN(sampleRateNum) || sampleRateNum <= 0) {
+        return '未知';
+      }
+      const sampleRateKHz = sampleRateNum / 1000;
+      return `${sampleRateKHz.toFixed(1)} KHz`;
+    } catch (err) {
+      console.error(`转换采样率出错: ${err}`);
+      return '未知';
+    }
+  }
 
 
-    const sampleRateKHz = Number(sampleRateHz) / 1000;
-    return `${sampleRateKHz} KHz`;
+  /**
+   * 格式化媒体格式类型显示
+   * @param mimeType 媒体格式类型字符串
+   * @return 格式化后的显示字符串
+   */
+  static formatMimeType(mimeType: string|undefined): string {
+    if(StrUtil.isEmpty(mimeType) || mimeType === undefined) {
+      return '未知';
+    }
+    
+    // 从MIME类型中提取格式部分,例如 "audio/mp3" -> "MP3"
+    try {
+      const parts = mimeType.split('/');
+      if (parts.length > 1) {
+        return parts[1].toUpperCase();
+      } else {
+        return mimeType.toUpperCase();
+      }
+    } catch (err) {
+      console.error(`格式化媒体类型出错: ${err}`);
+      return '未知';
+    }
   }
   }
 
 
   //根据字节获取大小
   //根据字节获取大小

+ 95 - 8
entry/src/main/ets/view/LocalMusic.ets

@@ -73,7 +73,7 @@ import { secondToTime } from '../common/util/CommUtils';
 import { CommonConstants2 } from '../common/util/CommonConstants2';
 import { CommonConstants2 } from '../common/util/CommonConstants2';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { LazyDataSource } from '../common/util/LazyDataSource';
 import { LazyDataSource } from '../common/util/LazyDataSource';
-import MediaTable from '../common/util/MediaTable';
+import MediaTable, { MediaMetadata } from '../common/util/MediaTable';
 import NetAxiosUtil from '../common/util/NetAxiosUtil';
 import NetAxiosUtil from '../common/util/NetAxiosUtil';
 import ImageUtils from '../common/util/ImageUtils';
 import ImageUtils from '../common/util/ImageUtils';
 import { ringtone } from '@kit.RingtoneKit';
 import { ringtone } from '@kit.RingtoneKit';
@@ -631,17 +631,40 @@ export struct LocalMusic {
 
 
       this.songList = PreferencesUtil.getSync('LastMusicList', []) as Array<VideoItem>
       this.songList = PreferencesUtil.getSync('LastMusicList', []) as Array<VideoItem>
       this.currentSong = PreferencesUtil.getSync('LastMusicInfo', undefined) as VideoItem
       this.currentSong = PreferencesUtil.getSync('LastMusicInfo', undefined) as VideoItem
+      
+      // 打印加载的当前歌曲信息,用于调试
+      if (this.currentSong) {
+        console.info(`加载歌曲信息 - 采样率: ${this.currentSong.sampleRate}, MIME类型: ${this.currentSong.mimeType}`);
+      }
+      
       if (ArrayUtil.isEmpty(this.songList)) {
       if (ArrayUtil.isEmpty(this.songList)) {
         this.songList = this.getCurFileList()
         this.songList = this.getCurFileList()
       } else {
       } else {
-        this.curIndex = Utility.getIndexFromList(this.songList, this.currentSong.filePath)
+        this.curIndex = Utility.getIndexFromList(this.songList, this.currentSong?.filePath || '')
       }
       }
       if (ArrayUtil.isNotEmpty(this.songList)) {
       if (ArrayUtil.isNotEmpty(this.songList)) {
         this.sonDataSource.pushArrayData(this.songList)
         this.sonDataSource.pushArrayData(this.songList)
         if (this.currentSong === undefined) {
         if (this.currentSong === undefined) {
-
           this.currentSong = this.songList[0]
           this.currentSong = this.songList[0]
         }
         }
+        
+        // 如果当前歌曲缺少必要信息,尝试从数据库中重新加载
+        if (this.currentSong && (!this.currentSong.sampleRate || !this.currentSong.mimeType)) {
+          this.table.queryByFilePath(this.currentSong.filePath, (result: VideoItem[]) => {
+            if (result && result.length > 0) {
+              // 更新当前歌曲对象,手动复制属性
+              if (this.currentSong) {
+                const dbItem = result[0];
+                this.currentSong.sampleRate = dbItem.sampleRate;
+                this.currentSong.mimeType = dbItem.mimeType;
+                this.currentSong.duration = dbItem.duration;
+                this.currentSong.trackCount = dbItem.trackCount;
+                console.info(`从数据库更新歌曲属性 - 采样率: ${dbItem.sampleRate}, MIME类型: ${dbItem.mimeType}`);
+              }
+            }
+          });
+        }
+        
         this.videoUrl = this.currentSong.filePath
         this.videoUrl = this.currentSong.filePath
         this.name = this.currentSong.name
         this.name = this.currentSong.name
         this.cover = this.currentSong.pixelMapPath
         this.cover = this.currentSong.pixelMapPath
@@ -3596,6 +3619,9 @@ export struct LocalMusic {
 
 
           if (ArrayUtil.isNotEmpty(this.songList)) {
           if (ArrayUtil.isNotEmpty(this.songList)) {
             this.isShowPlay = true;
             this.isShowPlay = true;
+            // 显示播放界面时重新提取音频元数据
+            this.refreshCurrentSongMetadata();
+            
             let lyricPath = this.videoUrl.substring(0, this.videoUrl.lastIndexOf('.')) + '.lrc'
             let lyricPath = this.videoUrl.substring(0, this.videoUrl.lastIndexOf('.')) + '.lrc'
             this.initLyric(lyricPath);
             this.initLyric(lyricPath);
             if (FileUtil.accessSync(this.favPath)) {
             if (FileUtil.accessSync(this.favPath)) {
@@ -4156,11 +4182,15 @@ export struct LocalMusic {
             .fontSize(14)
             .fontSize(14)
             .fontColor(Color.White)
             .fontColor(Color.White)
             .margin({ left: 22 })
             .margin({ left: 22 })
-          Text(Utility.convertToKHz(this.currentSong.sampleRate))
+          Text(Utility.convertToKHz(this.currentSong?.sampleRate || ''))
             .fontSize(14)
             .fontSize(14)
             .margin({ left: 10 })
             .margin({ left: 10 })
             .fontColor(Color.White)
             .fontColor(Color.White)
             .layoutWeight(1)
             .layoutWeight(1)
+            .onAppear(() => {
+              console.info('音频信息:'+JSON.stringify(this.currentSong))
+              console.info(`音频采样率原始值: ${this.currentSong?.sampleRate}, 类型: ${typeof this.currentSong?.sampleRate}`);
+            })
         }
         }
         .width('100%')
         .width('100%')
         .margin({ top: 10, bottom: 10 })
         .margin({ top: 10, bottom: 10 })
@@ -4171,11 +4201,14 @@ export struct LocalMusic {
             .fontSize(14)
             .fontSize(14)
             .fontColor(Color.White)
             .fontColor(Color.White)
             .margin({ left: 22 })
             .margin({ left: 22 })
-          Text(this.currentSong.mimeType)
+          Text(Utility.formatMimeType(this.currentSong?.mimeType || ''))
             .fontSize(14)
             .fontSize(14)
             .margin({ left: 10 })
             .margin({ left: 10 })
             .fontColor(Color.White)
             .fontColor(Color.White)
             .layoutWeight(1)
             .layoutWeight(1)
+            .onAppear(() => {
+              console.info(`音频MIME类型原始值: ${this.currentSong?.mimeType}, 类型: ${typeof this.currentSong?.mimeType}`);
+            })
         }
         }
         .width('100%')
         .width('100%')
         .margin({ top: 10, bottom: 10 })
         .margin({ top: 10, bottom: 10 })
@@ -7417,6 +7450,53 @@ export struct LocalMusic {
     this.replayVisible = Visibility.Visible;
     this.replayVisible = Visibility.Visible;
   }
   }
 
 
+  // 从文件重新提取音频元数据
+  private async refreshCurrentSongMetadata() {
+    if (!this.currentSong || StrUtil.isEmpty(this.currentSong.filePath)) {
+      console.error('无法刷新元数据,当前歌曲或文件路径为空');
+      return;
+    }
+    
+    try {
+      // 直接从音频文件重新加载完整的元数据
+      console.info(`开始重新提取音频元数据: ${this.currentSong.filePath}`);
+      const refreshedItem = await Utility.uriGetMusicAssetsFromFile(
+        this.context, 
+        this.currentSong.filePath, 
+        CommonConstants.TYPE_LOCAL, 
+        false
+      );
+      
+      // 只更新元数据相关字段,保留其他字段
+      if (this.currentSong) {
+        this.currentSong.duration = refreshedItem.duration;
+        this.currentSong.mimeType = refreshedItem.mimeType;
+        this.currentSong.sampleRate = refreshedItem.sampleRate;
+        this.currentSong.trackCount = refreshedItem.trackCount;
+        
+        // 更新数据库中的记录以确保下次不需要重新提取
+        // 创建符合MediaMetadata接口的对象
+        const metadataToUpdate: MediaMetadata = {
+          duration: refreshedItem.duration,
+          mimeType: refreshedItem.mimeType,
+          sampleRate: refreshedItem.sampleRate,
+          trackCount: refreshedItem.trackCount
+        };
+        this.table.updateMediaMetadata(this.currentSong.filePath, metadataToUpdate, (success: boolean) => {
+          if (success) {
+            console.info('成功更新音频元数据到数据库');
+          } else {
+            console.error('更新音频元数据到数据库失败');
+          }
+        });
+        
+        console.info(`刷新后的采样率: ${this.currentSong.sampleRate}, MIME类型: ${this.currentSong.mimeType}`);
+      }
+    } catch (err) {
+      console.error(`刷新音频元数据失败: ${err}`);
+    }
+  }
+  
   private async play(url: string) {
   private async play(url: string) {
     let that = this;
     let that = this;
     that.showLoadIng();
     that.showLoadIng();
@@ -7736,10 +7816,16 @@ export struct LocalMusic {
 
 
   //保存最后播放的那首歌和已经对应的播放列表
   //保存最后播放的那首歌和已经对应的播放列表
   saveLastPlayList() {
   saveLastPlayList() {
-
-    PreferencesUtil.putSync('LastMusicInfo', this.currentSong)
+    // 确保所有字段都被保存,特别是sampleRate和mimeType
+    if (this.currentSong) {
+      // 打印当前歌曲的采样率和MIME类型值,用于调试
+      console.info(`保存歌曲信息 - 采样率: ${this.currentSong.sampleRate}, MIME类型: ${this.currentSong.mimeType}`);
+      
+      // 将完整对象保存到LastMusicInfo
+      PreferencesUtil.putSync('LastMusicInfo', this.currentSong)
+    }
+    
     PreferencesUtil.putSync('LastMusicList', this.songList)
     PreferencesUtil.putSync('LastMusicList', this.songList)
-
   }
   }
 
 
   //添加播放历史记录
   //添加播放历史记录
@@ -8976,3 +9062,4 @@ function cutPopupBuilder(dataBu: BubbleBean) {
 
 
 }
 }
 
 
+