RdbUtils.ets 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import relationalStore from '@ohos.data.relationalStore';
  2. import { LogUtil, StrUtil } from '@pura/harmony-utils';
  3. import Logger from './Logger';
  4. import NetAxiosUtil from './NetAxiosUtil';
  5. //
  6. interface GeneratedObjectLiteralInterface_1 {
  7. tableName: string;
  8. sqlCreate: string;
  9. columns: string[];
  10. }
  11. export default class RdbUtils {
  12. private rdbStore: relationalStore.RdbStore | null = null;
  13. private tableName: string;
  14. private sqlCreateTable: string;
  15. private columns: Array<string>;
  16. static readonly RDB_TAG: string = 'heanup onecold RdbUtils';
  17. /**
  18. * Rdb数据库配置。
  19. * name:数据库名称
  20. * securityLevel:数据库安全级别
  21. */
  22. static readonly STORE_CONFIG: relationalStore.StoreConfig = {
  23. name: 'mediaDB.db',
  24. securityLevel: relationalStore.SecurityLevel.S1
  25. };
  26. /**
  27. * 表配置信息
  28. * tableName:表名称
  29. * sqlCreate:创建表的语句
  30. * columns:表的列项目
  31. */
  32. static readonly MEDIA_TABLE: GeneratedObjectLiteralInterface_1 = {
  33. tableName: 'mediaTable',
  34. sqlCreate: ' CREATE TABLE IF NOT EXISTS mediaTable (\n' +
  35. ' id TEXT NOT NULL,\n' +
  36. ' name TEXT NOT NULL,\n' +
  37. ' filePath TEXT NOT NULL UNIQUE,\n' +
  38. ' mtype INTEGER NOT NULL,\n' +
  39. ' videoSize INTEGER,\n' +
  40. ' cTime TEXT NOT NULL,\n' +
  41. ' size TEXT,\n' +
  42. ' artist TEXT,\n' +
  43. ' album TEXT,\n' +
  44. ' fileName TEXT ,\n' +
  45. ' parentPath TEXT,\n' +
  46. ' isFav INTEGER,\n' +
  47. ' pixelMapPath TEXT,\n' +
  48. ' duration TEXT,\n' +
  49. ' sampleRate TEXT,\n' +
  50. ' playCount INTEGER DEFAULT 0,\n' +
  51. ' lastPlayedStr TEXT,\n' +
  52. ' trackCount TEXT,\n' +
  53. ' lyricContent TEXT,\n' +
  54. ' md5Str TEXT,\n' +
  55. ' extra_json TEXT,\n' +
  56. ' pyStr TEXT,\n' +
  57. ' bit_rate TEXT,\n' +
  58. ' probe_score INTEGER DEFAULT 0,\n' +
  59. ' year TEXT,\n' +
  60. ' nb_streams INTEGER DEFAULT 0,\n' +
  61. ' nb_programs INTEGER DEFAULT 0,\n' +
  62. ' genre TEXT,\n' +
  63. ' track TEXT,\n' +
  64. ' bits_per_raw_sample TEXT,\n' +
  65. ' channels TEXT,\n' +
  66. ' channel_layout TEXT,\n' +
  67. ' start_time TEXT,\n' +
  68. ' ALBUMARTIST TEXT,\n' +
  69. ' COMPOSER TEXT,\n' +
  70. ' LYRICIST TEXT,\n' +
  71. ' COMMENT TEXT,\n' +
  72. ' disc TEXT,\n' +
  73. ' webdav_account_id TEXT,\n' +
  74. ' remote_rel_path TEXT,\n' +
  75. ' mimeType TEXT' +
  76. ')',
  77. columns: ['id', 'name', 'filePath','mtype', 'videoSize', 'cTime','size', 'artist', 'album',
  78. 'fileName','parentPath','isFav','pixelMapPath',
  79. 'duration', 'sampleRate','playCount', 'lastPlayedStr', 'trackCount',
  80. 'lyricContent','md5Str','extra_json','pyStr','bit_rate','probe_score','year','nb_streams','nb_programs',
  81. 'genre','track', 'bits_per_raw_sample','channels', 'channel_layout','start_time',
  82. 'ALBUMARTIST', 'COMPOSER','LYRICIST','COMMENT','COMMENT','disc','webdav_account_id',
  83. 'remote_rel_path',
  84. 'mimeType']
  85. };
  86. /**
  87. * 歌单表配置
  88. */
  89. static readonly PLAYLIST_TABLE: GeneratedObjectLiteralInterface_1 = {
  90. tableName: 'playlistTable',
  91. sqlCreate: 'CREATE TABLE IF NOT EXISTS playlistTable (\n' +
  92. 'id TEXT PRIMARY KEY NOT NULL,\n' +
  93. 'name TEXT NOT NULL,\n' +
  94. 'coverPath TEXT,\n' +
  95. 'description TEXT,\n' +
  96. 'createTime TEXT NOT NULL,\n' +
  97. 'updateTime TEXT NOT NULL,\n' +
  98. 'songCount INTEGER DEFAULT 0,\n' +
  99. 'sortOrder INTEGER DEFAULT 0\n' +
  100. ')',
  101. columns: ['id', 'name', 'coverPath', 'description', 'createTime', 'updateTime', 'songCount', 'sortOrder']
  102. };
  103. /**
  104. * 歌单歌曲关联表配置
  105. */
  106. static readonly PLAYLIST_SONG_TABLE: GeneratedObjectLiteralInterface_1 = {
  107. tableName: 'playlistSongTable',
  108. sqlCreate: 'CREATE TABLE IF NOT EXISTS playlistSongTable (\n' +
  109. 'id TEXT PRIMARY KEY NOT NULL,\n' +
  110. 'playlistId TEXT NOT NULL,\n' +
  111. 'songFilePath TEXT NOT NULL,\n' +
  112. 'addTime TEXT NOT NULL,\n' +
  113. 'sortOrder INTEGER DEFAULT 0,\n' +
  114. 'UNIQUE(playlistId, songFilePath)\n' +
  115. ')',
  116. columns: ['id', 'playlistId', 'songFilePath', 'addTime', 'sortOrder']
  117. };
  118. constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
  119. this.tableName = tableName;
  120. this.sqlCreateTable = sqlCreateTable;
  121. this.columns = columns;
  122. }
  123. getRdbStore(context:Context,callback: Function = () => {
  124. }) {
  125. if (!callback || typeof callback === 'undefined' || callback === undefined) {
  126. Logger.info(RdbUtils.RDB_TAG, 'getRdbStore() has no callback!');
  127. return;
  128. }
  129. if (this.rdbStore !== null) {
  130. Logger.info(RdbUtils.RDB_TAG, 'The rdbStore exists.');
  131. callback();
  132. return
  133. }
  134. relationalStore.getRdbStore(context, RdbUtils.STORE_CONFIG, (err, rdb) => {
  135. if (err) {
  136. Logger.error(RdbUtils.RDB_TAG, `gerRdbStore() failed, err: ${err}`);
  137. return;
  138. }
  139. this.rdbStore = rdb;
  140. // 先创建表(如果不存在)
  141. this.rdbStore.executeSql(this.sqlCreateTable);
  142. // 检查数据库版本并更新列
  143. try {
  144. // 检查表结构
  145. this.checkAndUpdateTableColumns();
  146. // Logger.info(RdbUtils.RDB_TAG, `数据库表结构检查完成`);
  147. } catch (e) {
  148. Logger.error(RdbUtils.RDB_TAG, `检查表结构时出错: ${e.message}`);
  149. }
  150. callback();
  151. });
  152. }
  153. /**
  154. * 检查并更新表列结构
  155. * 使用表信息查询和ALTER TABLE添加缺失的列
  156. */
  157. private checkAndUpdateTableColumns() {
  158. if (!this.rdbStore) {
  159. Logger.error(RdbUtils.RDB_TAG, `数据库连接未初始化`);
  160. return;
  161. }
  162. try {
  163. // 查询表信息获取现有列
  164. const tableInfoQuery = `PRAGMA table_info(${this.tableName})`;
  165. // 使用Promise模式代替回调
  166. this.rdbStore.executeSql(tableInfoQuery)
  167. .then(() => {
  168. // 定义应该存在的列及其类型
  169. const requiredColumns: Record<string, string> = {
  170. 'duration': 'TEXT',
  171. 'sampleRate': 'TEXT',
  172. 'playCount': 'INTEGER DEFAULT 0',
  173. 'lastPlayedStr': 'TEXT',
  174. 'trackCount': 'TEXT',
  175. 'lyricContent': 'TEXT',
  176. 'md5Str': 'TEXT',
  177. 'extra_json': 'TEXT',
  178. 'mimeType': 'TEXT',
  179. 'pyStr': 'TEXT',
  180. 'bit_rate': 'TEXT',
  181. 'probe_score': 'INTEGER DEFAULT 0',
  182. 'year': 'TEXT',
  183. 'nb_streams': 'INTEGER DEFAULT 0',
  184. 'nb_programs': 'INTEGER DEFAULT 0',
  185. 'genre': 'TEXT',
  186. 'track': 'TEXT',
  187. 'bits_per_raw_sample': 'TEXT',
  188. 'channels': 'TEXT',
  189. 'channel_layout': 'TEXT',
  190. 'start_time': 'TEXT',
  191. 'ALBUMARTIST': 'TEXT',
  192. 'COMPOSER': 'TEXT',
  193. 'LYRICIST': 'TEXT',
  194. 'COMMENT': 'TEXT',
  195. 'disc': 'TEXT',
  196. 'webdav_account_id': 'TEXT',
  197. 'remote_rel_path': 'TEXT',
  198. };
  199. // 逐个添加列,不依赖于检查结果
  200. if (this.rdbStore) {
  201. // 遍历映射
  202. const columnEntries = Object.keys(requiredColumns);
  203. for (let i = 0; i < columnEntries.length; i++) {
  204. const column = columnEntries[i];
  205. const type = requiredColumns[column];
  206. const alterSql = `ALTER TABLE ${this.tableName} ADD COLUMN ${column} ${type}`;
  207. try {
  208. // 对每个列使用Promise模式
  209. this.rdbStore.executeSql(alterSql)
  210. .then(() => {
  211. Logger.info(RdbUtils.RDB_TAG, `成功添加列: ${column}`);
  212. })
  213. .catch((alterErr: Error) => {
  214. // 列可能已经存在,这是预期的错误
  215. // Logger.info(RdbUtils.RDB_TAG, `列 ${column} 可能已存在: ${alterErr.message}`);
  216. });
  217. } catch (e) {
  218. Logger.error(RdbUtils.RDB_TAG, `添加列 ${column} 出错: ${e.message}`);
  219. }
  220. }
  221. // 移除废弃的 pixelMapToString 列
  222. const dropPixelMapSql = `ALTER TABLE ${this.tableName} DROP COLUMN pixelMapToString`;
  223. try {
  224. this.rdbStore.executeSql(dropPixelMapSql)
  225. .then(() => {
  226. Logger.info(RdbUtils.RDB_TAG, '已删除废弃列 pixelMapToString');
  227. })
  228. .catch(() => {
  229. // 列不存在或数据库版本不支持 DROP COLUMN,忽略
  230. });
  231. } catch (dropErr) {
  232. Logger.warn(RdbUtils.RDB_TAG, `移除 pixelMapToString 列失败: ${(dropErr as Error).message}`);
  233. }
  234. // 设置数据库版本
  235. if (this.rdbStore) {
  236. this.rdbStore.version = 1;
  237. // Logger.info(RdbUtils.RDB_TAG, `数据库升级完成,版本设置为 1`);
  238. }
  239. }
  240. })
  241. .catch((err: Error) => {
  242. Logger.error(RdbUtils.RDB_TAG, `获取表信息失败: ${err.message}`);
  243. });
  244. } catch (e) {
  245. Logger.error(RdbUtils.RDB_TAG, `检查表结构时出错: ${e.message}`);
  246. }
  247. }
  248. //检查是否存在相同id的记录,存在则不插入,进一步判断pixelMapPath字段,如果旧值为空而新值不为空,则更新该字段。
  249. async insertData(data: relationalStore.ValuesBucket, callback: Function = () => {},cover_api?:string) {
  250. if (!callback || typeof callback !== 'function') {
  251. Logger.info(RdbUtils.RDB_TAG, 'insertData() has no valid callback!');
  252. return;
  253. }
  254. const valueBucket: relationalStore.ValuesBucket = data;
  255. const id = valueBucket.id;
  256. let titleNameV = valueBucket.name;
  257. let artistV = valueBucket.artist ;
  258. let pixelMapPathV = valueBucket.pixelMapPath
  259. if (this.rdbStore) {
  260. // Create a predicate to check if the record exists
  261. const predicates = new relationalStore.RdbPredicates(this.tableName);
  262. predicates.equalTo('id', id);
  263. // Query the database
  264. this.rdbStore.query(predicates, async (err, resultSet) => {
  265. if (err) {
  266. Logger.error(RdbUtils.RDB_TAG, `Query failed, err: ${err}`);
  267. callback(false);
  268. return;
  269. }
  270. if (resultSet.rowCount > 0) {
  271. Logger.info(RdbUtils.RDB_TAG, `Record with id ${id} already exists. Skipping insert.`);
  272. //检查是否存在相同id的记录,存在则不插入,进一步判断pixelMapPath字段,如果旧值为空而新值不为空,则更新该字段。
  273. //20250924不判断pixelMapPath字段了,如果存在不插入,则返回
  274. if (resultSet.goToFirstRow()) {
  275. callback(resultSet);
  276. resultSet.close();
  277. return;
  278. // const existingPixelMapPath = resultSet.getString(resultSet.getColumnIndex('pixelMapPath'));
  279. // const name = resultSet.getString(resultSet.getColumnIndex('name'));
  280. // const artist = resultSet.getString(resultSet.getColumnIndex('artist'));
  281. // if ((!existingPixelMapPath || existingPixelMapPath.trim() === '')&& cover_api!==undefined) {
  282. // let newPixelMapPath = await NetAxiosUtil.getLyricCover(name,artist,cover_api)
  283. // console.info('onecold getLyricCover success. name= '+name);
  284. // console.info('onecold getLyricCover success. newPixelMapPath= '+newPixelMapPath);
  285. // if(StrUtil.isNotEmpty(newPixelMapPath)){
  286. // // Update pixelMapPath
  287. // const updateValues: relationalStore.ValuesBucket = { pixelMapPath: newPixelMapPath };
  288. // this.updateData(predicates, updateValues, (updateSuccess: boolean) => {
  289. // if (updateSuccess) {
  290. // Logger.info(RdbUtils.RDB_TAG, `onecold Updated pixelMapPath for id ${id}.`);
  291. // } else {
  292. // Logger.error(RdbUtils.RDB_TAG, `onecold Failed to update pixelMapPath for id ${id}.`);
  293. // }
  294. // callback(updateSuccess);
  295. // });
  296. // }
  297. //
  298. // resultSet.close();
  299. // return;
  300. // }
  301. }
  302. Logger.info(RdbUtils.RDB_TAG, `No update needed for id ${id}.`);
  303. callback(false);
  304. return;
  305. }
  306. if (!this.rdbStore) {
  307. Logger.error(RdbUtils.RDB_TAG, 'RdbStore is not initialized.');
  308. callback(false);
  309. return;
  310. }
  311. //不存在相同记录 ,判断要不要去请求getLyricCover
  312. if(pixelMapPathV==undefined||pixelMapPathV.toString()==='' ){
  313. valueBucket.pixelMapPath = await NetAxiosUtil.getLyricCover(titleNameV+'',artistV+'',cover_api)
  314. }
  315. console.info('onecold getLyricCover pixelMapPathV1= '+pixelMapPathV);
  316. // Proceed with insert if record does not exist
  317. this.rdbStore.insert(this.tableName, valueBucket, (err, ret) => {
  318. if (err) {
  319. Logger.error(RdbUtils.RDB_TAG, `insertData() failed, err: ${err}`);
  320. callback(false);
  321. return;
  322. }
  323. Logger.info(RdbUtils.RDB_TAG, `insertData() finished: ${ret}`);
  324. callback(ret);
  325. });
  326. });
  327. }else{
  328. Logger.error(RdbUtils.RDB_TAG, 'RdbStore2 is not initialized.');
  329. }
  330. }
  331. deleteData(predicates: relationalStore.RdbPredicates, callback: Function = () => {
  332. }) {
  333. if (!callback || typeof callback === 'undefined' || callback === undefined) {
  334. Logger.info(RdbUtils.RDB_TAG, 'deleteData() has no callback!');
  335. return;
  336. }
  337. let resFlag: boolean = false;
  338. if (this.rdbStore) {
  339. this.rdbStore.delete(predicates, (err, ret) => {
  340. if (err) {
  341. Logger.info('xiaozheng 校正数据开始 deleteData5 = ' )
  342. Logger.error(RdbUtils.RDB_TAG, `deleteData() failed, err: ${err}`);
  343. callback(resFlag);
  344. return;
  345. }
  346. Logger.info('xiaozheng 校正数据开始 deleteData6 = ' )
  347. Logger.info(RdbUtils.RDB_TAG, `deleteData() finished: ${ret}`);
  348. callback(!resFlag);
  349. });
  350. }else{
  351. Logger.info(RdbUtils.RDB_TAG, 'deleteData rdbStore() 未初始化!');
  352. }
  353. }
  354. updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket, callback: Function = () => {
  355. }) {
  356. if (!callback || typeof callback === 'undefined' || callback === undefined) {
  357. Logger.info(RdbUtils.RDB_TAG, 'updateDate() has no callback!');
  358. return;
  359. }
  360. let resFlag: boolean = false;
  361. const valueBucket: relationalStore.ValuesBucket = data;
  362. if (this.rdbStore) {
  363. this.rdbStore.update(valueBucket, predicates, (err, ret) => {
  364. if (err) {
  365. Logger.error(RdbUtils.RDB_TAG, `updateData() failed, err: ${err}`);
  366. callback(resFlag);
  367. return;
  368. }
  369. console.info( `onecold updateData() finished: ${ret}`);
  370. callback(!resFlag);
  371. });
  372. }
  373. }
  374. query(predicates: relationalStore.RdbPredicates, callback: Function = () => {
  375. }) {
  376. if (!callback || typeof callback === 'undefined' || callback === undefined) {
  377. Logger.info(RdbUtils.RDB_TAG, 'query() has no callback!');
  378. return;
  379. }
  380. if (this.rdbStore) {
  381. this.rdbStore.query(predicates, this.columns, (err, resultSet) => {
  382. if (err) {
  383. Logger.error(RdbUtils.RDB_TAG, `query() failed, err: ${err}`);
  384. return;
  385. }
  386. // Logger.info(RdbUtils.RDB_TAG, 'query() finished.');
  387. callback(resultSet);
  388. resultSet.close();
  389. });
  390. }
  391. }
  392. }