|
|
@@ -0,0 +1,412 @@
|
|
|
+import { relationalStore } from '@kit.ArkData';
|
|
|
+import { Context } from '@kit.AbilityKit';
|
|
|
+import Logger from './Logger';
|
|
|
+import RdbUtils from './RdbUtils';
|
|
|
+import { Playlist, PlaylistSong } from '../../viewmodel/Playlist';
|
|
|
+
|
|
|
+/**
|
|
|
+ * 歌单数据库操作类
|
|
|
+ */
|
|
|
+export default class PlaylistTable {
|
|
|
+ private context: Context;
|
|
|
+ private rdbStore: relationalStore.RdbStore | null = null;
|
|
|
+
|
|
|
+ constructor(context: Context) {
|
|
|
+ this.context = context;
|
|
|
+ this.initRdbStore();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化数据库
|
|
|
+ */
|
|
|
+ private async initRdbStore(): Promise<void> {
|
|
|
+ try {
|
|
|
+ const config: relationalStore.StoreConfig = {
|
|
|
+ name: 'PlaylistStore.db',
|
|
|
+ securityLevel: relationalStore.SecurityLevel.S1 // 使用标准安全级别
|
|
|
+ };
|
|
|
+
|
|
|
+ this.rdbStore = await relationalStore.getRdbStore(this.context, config);
|
|
|
+
|
|
|
+ // 创建表
|
|
|
+ await this.createTables();
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', '数据库初始化成功');
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `初始化数据库失败: ${error.message}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建表
|
|
|
+ */
|
|
|
+ private async createTables(): Promise<void> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 创建歌单表
|
|
|
+ await this.rdbStore.executeSql(RdbUtils.PLAYLIST_TABLE.sqlCreate);
|
|
|
+
|
|
|
+ // 创建歌单歌曲关联表
|
|
|
+ await this.rdbStore.executeSql(RdbUtils.PLAYLIST_SONG_TABLE.sqlCreate);
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', '数据库表创建成功');
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `创建表失败: ${error.message}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成唯一ID
|
|
|
+ */
|
|
|
+ private generateId(): string {
|
|
|
+ return Date.now().toString() + Math.random().toString(36).substr(2, 9);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建歌单
|
|
|
+ */
|
|
|
+ async createPlaylist(name: string, description?: string, coverPath?: string): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const id = this.generateId();
|
|
|
+ const now = new Date().toISOString();
|
|
|
+ const sql = 'INSERT INTO playlistTable (id, name, coverPath, description, createTime, updateTime, songCount, sortOrder) VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
|
|
|
+ const params = [id, name, coverPath || null, description || null, now, now, 0, 0];
|
|
|
+
|
|
|
+ await this.rdbStore.executeSql(sql, params);
|
|
|
+ Logger.info('PlaylistTable', `歌单创建成功: ${name}`);
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `创建歌单失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除歌单及其所有歌曲
|
|
|
+ */
|
|
|
+ async deletePlaylist(playlistId: string): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 先删除歌单歌曲关联
|
|
|
+ const deleteSongsSql = 'DELETE FROM playlistSongTable WHERE playlistId = ?';
|
|
|
+ await this.rdbStore.executeSql(deleteSongsSql, [playlistId]);
|
|
|
+
|
|
|
+ // 再删除歌单
|
|
|
+ const deletePlaylistSql = 'DELETE FROM playlistTable WHERE id = ?';
|
|
|
+ await this.rdbStore.executeSql(deletePlaylistSql, [playlistId]);
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', `歌单删除成功: ${playlistId}`);
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `删除歌单失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新歌单信息
|
|
|
+ */
|
|
|
+ async updatePlaylist(playlistId: string, name?: string, description?: string, coverPath?: string): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const updateTime = new Date().toISOString();
|
|
|
+ const updates: string[] = [];
|
|
|
+ const params: (string | number | null)[] = [];
|
|
|
+
|
|
|
+ if (name !== undefined) {
|
|
|
+ updates.push('name = ?');
|
|
|
+ params.push(name);
|
|
|
+ }
|
|
|
+ if (description !== undefined) {
|
|
|
+ updates.push('description = ?');
|
|
|
+ params.push(description);
|
|
|
+ }
|
|
|
+ if (coverPath !== undefined) {
|
|
|
+ updates.push('coverPath = ?');
|
|
|
+ params.push(coverPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ updates.push('updateTime = ?');
|
|
|
+ params.push(updateTime);
|
|
|
+ params.push(playlistId);
|
|
|
+
|
|
|
+ const sql = `UPDATE playlistTable SET ${updates.join(', ')} WHERE id = ?`;
|
|
|
+ await this.rdbStore.executeSql(sql, params);
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', `歌单更新成功: ${playlistId}`);
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `更新歌单失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询所有歌单
|
|
|
+ */
|
|
|
+ async queryAllPlaylists(): Promise<Playlist[]> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sql = 'SELECT * FROM playlistTable ORDER BY sortOrder ASC, createTime DESC';
|
|
|
+ const resultSet = await this.rdbStore.querySql(sql);
|
|
|
+
|
|
|
+ const playlists: Playlist[] = [];
|
|
|
+ if (resultSet.goToFirstRow()) {
|
|
|
+ do {
|
|
|
+ const playlist = new Playlist(
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('id')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('name')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('createTime')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('updateTime')),
|
|
|
+ resultSet.getLong(resultSet.getColumnIndex('songCount')),
|
|
|
+ resultSet.getLong(resultSet.getColumnIndex('sortOrder')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('coverPath')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('description'))
|
|
|
+ );
|
|
|
+ playlists.push(playlist);
|
|
|
+ } while (resultSet.goToNextRow());
|
|
|
+ }
|
|
|
+
|
|
|
+ resultSet.close();
|
|
|
+ return playlists;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `查询歌单失败: ${error.message}`);
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据ID查询歌单
|
|
|
+ */
|
|
|
+ async queryPlaylistById(playlistId: string): Promise<Playlist | null> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sql = 'SELECT * FROM playlistTable WHERE id = ?';
|
|
|
+ const resultSet = await this.rdbStore.querySql(sql, [playlistId]);
|
|
|
+
|
|
|
+ if (resultSet.goToFirstRow()) {
|
|
|
+ const playlist = new Playlist(
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('id')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('name')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('createTime')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('updateTime')),
|
|
|
+ resultSet.getLong(resultSet.getColumnIndex('songCount')),
|
|
|
+ resultSet.getLong(resultSet.getColumnIndex('sortOrder')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('coverPath')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('description'))
|
|
|
+ );
|
|
|
+ resultSet.close();
|
|
|
+ return playlist;
|
|
|
+ }
|
|
|
+
|
|
|
+ resultSet.close();
|
|
|
+ return null;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `查询歌单失败: ${error.message}`);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加歌曲到歌单
|
|
|
+ */
|
|
|
+ async addSongToPlaylist(playlistId: string, songFilePath: string): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 检查歌曲是否已在歌单中
|
|
|
+ const isInPlaylist = await this.isSongInPlaylist(playlistId, songFilePath);
|
|
|
+ if (isInPlaylist) {
|
|
|
+ Logger.info('PlaylistTable', '歌曲已在歌单中');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const id = this.generateId();
|
|
|
+ const addTime = new Date().toISOString();
|
|
|
+ const sql = 'INSERT INTO playlistSongTable (id, playlistId, songFilePath, addTime, sortOrder) VALUES (?, ?, ?, ?, ?)';
|
|
|
+ const params = [id, playlistId, songFilePath, addTime, 0];
|
|
|
+
|
|
|
+ await this.rdbStore.executeSql(sql, params);
|
|
|
+
|
|
|
+ // 更新歌单歌曲数量
|
|
|
+ await this.updatePlaylistSongCount(playlistId);
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', `歌曲添加到歌单成功: ${songFilePath}`);
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `添加歌曲到歌单失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从歌单中移除歌曲
|
|
|
+ */
|
|
|
+ async removeSongFromPlaylist(playlistId: string, songFilePath: string): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sql = 'DELETE FROM playlistSongTable WHERE playlistId = ? AND songFilePath = ?';
|
|
|
+ await this.rdbStore.executeSql(sql, [playlistId, songFilePath]);
|
|
|
+
|
|
|
+ // 更新歌单歌曲数量
|
|
|
+ await this.updatePlaylistSongCount(playlistId);
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', `歌曲从歌单移除成功: ${songFilePath}`);
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `从歌单移除歌曲失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询歌单中的歌曲
|
|
|
+ */
|
|
|
+ async queryPlaylistSongs(playlistId: string): Promise<PlaylistSong[]> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sql = 'SELECT * FROM playlistSongTable WHERE playlistId = ? ORDER BY sortOrder ASC, addTime ASC';
|
|
|
+ const resultSet = await this.rdbStore.querySql(sql, [playlistId]);
|
|
|
+
|
|
|
+ const songs: PlaylistSong[] = [];
|
|
|
+ if (resultSet.goToFirstRow()) {
|
|
|
+ do {
|
|
|
+ const song = new PlaylistSong(
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('id')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('playlistId')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('songFilePath')),
|
|
|
+ resultSet.getString(resultSet.getColumnIndex('addTime')),
|
|
|
+ resultSet.getLong(resultSet.getColumnIndex('sortOrder'))
|
|
|
+ );
|
|
|
+ songs.push(song);
|
|
|
+ } while (resultSet.goToNextRow());
|
|
|
+ }
|
|
|
+
|
|
|
+ resultSet.close();
|
|
|
+ return songs;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `查询歌单歌曲失败: ${error.message}`);
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查歌曲是否在歌单中
|
|
|
+ */
|
|
|
+ async isSongInPlaylist(playlistId: string, songFilePath: string): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sql = 'SELECT COUNT(*) as count FROM playlistSongTable WHERE playlistId = ? AND songFilePath = ?';
|
|
|
+ const resultSet = await this.rdbStore.querySql(sql, [playlistId, songFilePath]);
|
|
|
+
|
|
|
+ let isInPlaylist = false;
|
|
|
+ if (resultSet.goToFirstRow()) {
|
|
|
+ const count = resultSet.getLong(resultSet.getColumnIndex('count'));
|
|
|
+ isInPlaylist = count > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ resultSet.close();
|
|
|
+ return isInPlaylist;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `检查歌曲是否在歌单中失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新歌单歌曲数量
|
|
|
+ */
|
|
|
+ private async updatePlaylistSongCount(playlistId: string): Promise<void> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const countSql = 'SELECT COUNT(*) as count FROM playlistSongTable WHERE playlistId = ?';
|
|
|
+ const resultSet = await this.rdbStore.querySql(countSql, [playlistId]);
|
|
|
+
|
|
|
+ let count = 0;
|
|
|
+ if (resultSet.goToFirstRow()) {
|
|
|
+ count = resultSet.getLong(resultSet.getColumnIndex('count'));
|
|
|
+ }
|
|
|
+ resultSet.close();
|
|
|
+
|
|
|
+ const updateSql = 'UPDATE playlistTable SET songCount = ?, updateTime = ? WHERE id = ?';
|
|
|
+ const updateTime = new Date().toISOString();
|
|
|
+ await this.rdbStore.executeSql(updateSql, [count, updateTime, playlistId]);
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `更新歌单歌曲数量失败: ${error.message}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新歌单排序
|
|
|
+ */
|
|
|
+ async updatePlaylistSortOrder(playlistId: string, sortOrder: number): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const updateTime = new Date().toISOString();
|
|
|
+ const sql = 'UPDATE playlistTable SET sortOrder = ?, updateTime = ? WHERE id = ?';
|
|
|
+ await this.rdbStore.executeSql(sql, [sortOrder, updateTime, playlistId]);
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', `歌单排序更新成功: ${playlistId}`);
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `更新歌单排序失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新歌单歌曲排序
|
|
|
+ */
|
|
|
+ async updatePlaylistSongSortOrder(playlistId: string, songFilePath: string, sortOrder: number): Promise<boolean> {
|
|
|
+ if (!this.rdbStore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sql = 'UPDATE playlistSongTable SET sortOrder = ? WHERE playlistId = ? AND songFilePath = ?';
|
|
|
+ await this.rdbStore.executeSql(sql, [sortOrder, playlistId, songFilePath]);
|
|
|
+
|
|
|
+ Logger.info('PlaylistTable', `歌单歌曲排序更新成功: ${songFilePath}`);
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('PlaylistTable', `更新歌单歌曲排序失败: ${error.message}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|