1.2.0部分代码

This commit is contained in:
xiaoxiao
2025-09-09 13:27:34 +08:00
parent e89a5f15ce
commit 3013b441a6
80 changed files with 4824 additions and 105 deletions
+13 -1
View File
@@ -113,4 +113,16 @@ export { compressedImage,saveImageToGallery,CompressedImageInfo } from './src/ma
export { ScreeningView } from './src/main/ets/Views/ScreeningView'
export { TagListModel, TagList} from './src/main/ets/models/TagListModel'
export { TagListModel, TagList} from './src/main/ets/models/TagListModel'
export { AppUtil } from './src/main/ets/utils/AppUtil'
export { OnWXResp,WXApi,WXEventHandler } from './src/main/ets/models/WXApiWrap'
// export { DBManager,FileDownloadManager } from './src/main/ets/utils/FileManager'
export { FileManager } from './src/main/ets/utils/FileManager'
export { FileInfo,DownloadCallback,PreviewConfig } from './src/main/ets/models/FileModel'
export { FileDownloadManager } from './src/main/ets/utils/FileDownloadManager'
@@ -1,3 +1,5 @@
import { ChangeUtil } from "../../../../Index";
import { promptAction } from "@kit.ArkUI";
@CustomDialog
export struct DefaultHintProWindows {
@@ -12,16 +14,23 @@ export struct DefaultHintProWindows {
@Prop cancleColor:ResourceStr = '#FFFFFF';
@Prop confirmColor:ResourceStr = '#FFFFFF';
controller: CustomDialogController;
@State inputContent:string = ''
// 添加回调函数属性
private selectedButton: (index: number) => void = () => {};
private selectedButtonAndContent: (index: number,content:string) => void = () => {};
// 修改构造函数
constructor(controller: CustomDialogController, selectedButton: (index:number) => void) {
super();
this.controller = controller;
this.selectedButton = selectedButton;
}
constructor(controller:CustomDialogController,selectedButtonAndContent: (index: number,content:string) => void) {
super();
this.controller = controller
this.selectedButtonAndContent = selectedButtonAndContent
}
build() {
Row(){
@@ -32,10 +41,21 @@ export struct DefaultHintProWindows {
.textAlign(TextAlign.Center)
.margin({ top: 20 })
Text(this.message)
.fontSize(this.messageFont)
.textAlign(TextAlign.Center)
.margin({ top: 10 })
if (!ChangeUtil.stringIsUndefinedAndNull(this.message)) {
Text(this.message)
.fontSize(this.messageFont)
.textAlign(TextAlign.Center)
.margin({ top: 10 })
.padding({left:10,right:10})
} else {
TextInput({placeholder:'请输入您的登录密码'})
.margin(10)
.backgroundColor('#f4f4f4')
.borderRadius(5)
.onChange((value:string)=>{
this.inputContent = value
})
}
Row({ space: 20 }) {
Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) {
@@ -45,7 +65,11 @@ export struct DefaultHintProWindows {
}
.width('45%').height(30).backgroundColor(this.cancleColor)
.onClick(() => {
this.selectedButton(0)
if (ChangeUtil.stringIsUndefinedAndNull(this.message)) {
this.selectedButtonAndContent(0,'')
} else {
this.selectedButton(0)
}
})
.visibility(this.cancleTitle?Visibility.Visible:Visibility.None)
@@ -55,7 +79,15 @@ export struct DefaultHintProWindows {
.fontColor(this.confirmTitleColor)
}.width('45%').height(30).backgroundColor(this.confirmColor)
.onClick(() => {
this.selectedButton(1)
if (ChangeUtil.stringIsUndefinedAndNull(this.message)) {
if (ChangeUtil.stringIsUndefinedAndNull(this.inputContent)) {
promptAction.showToast({ message: '请输入您的登录密码', duration: 1000 })
return
}
this.selectedButtonAndContent(1,this.inputContent)
} else {
this.selectedButton(1)
}
})
}.margin({ top: 20, bottom: 20 })
}
@@ -14,6 +14,7 @@ export class BasicConstant {
static readonly wxUrl = "https://dev-wx.igandan.com/";
static readonly polvId = "11";//保利威视学员id
static readonly urlApp="https://dev-app.igandan.com/app/"
static readonly expertPay = "https://dev-app.igandan.com/app/expertPay/"
//正式环境
// static readonly urlExpertAPI = "https://app.igandan.com/app/expertAPI/";
// static readonly urlExpertApp = "http://app.igandan.com/app/expertApp/"
@@ -23,7 +24,9 @@ export class BasicConstant {
// static readonly wxUrl = "https://wx.igandan.com/";// 微信服务器地址
// static readonly polvId = "21";//保利威视学员id
// static readonly urlApp="http://app.igandan.com/app/"
// static readonly expertPay = "https://app.igandan.com/app/expertPay/"
static readonly getSystemTime = BasicConstant.urlApp + 'manager/' + 'getSystemTime'
static readonly getSystemTimeStamp = BasicConstant.urlApp+'manager/getSystemTimeStamp'
static readonly addBonusPoints = BasicConstant.urlExpertApp+'addBonusPoints'
static readonly indexV2 = BasicConstant.urlExpertAPI+'indexV2';//首页轮播
@@ -72,12 +75,33 @@ export class BasicConstant {
static readonly deleteComment = BasicConstant.urlExpertApp+'deleteComment';//删除评论
static readonly meetingHistoryList = BasicConstant.urlExpertAPI + "meetingHistoryList";
static readonly videoRoll = BasicConstant.urlExpertAPI + "videoRoll";
static readonly newsRollNew = BasicConstant.urlExpert + 'newsRollNew'
static readonly newsTagList = BasicConstant.urlExpert + 'newsTagList'
static readonly newsListNew = BasicConstant.urlExpert + 'newsListNew'
static readonly defaultNewsListNew = BasicConstant.urlExpert + 'defaultNewsListNew'
static readonly expertVideoTypeList = BasicConstant.urlExpertAPI + "expertVideoTypeList";
static readonly patientVideoNew = BasicConstant.urlExpertApp + 'patientVideoNew';
static readonly videoByKeyWordsNew = BasicConstant.urlExpertApp + "videoByKeyWordsNew";
static readonly patientVideoByKeyWordsNew = BasicConstant.urlExpertApp + 'patientVideoByKeyWordsNew'
static readonly feedBack = BasicConstant.urlExpert+'feedBack'
static readonly ganDanFileByKeyWords = BasicConstant.urlExpertAPI+'ganDanFileByKeyWords'//肝胆课件
static readonly getKePuCollection = BasicConstant.urlExpert+'getKePuCollection'
static readonly discollection = BasicConstant.urlExpert + 'discollection'
static readonly collection = BasicConstant.urlExpert + 'collection'
static readonly disagree = BasicConstant.urlExpert + 'disagree'
static readonly agree = BasicConstant.urlExpert + 'agree'
static readonly ganDanFileDetials = BasicConstant.urlExpertAPI + 'ganDanFileDetials'
static readonly newsDetial = BasicConstant.urlExpert + 'newsDetial'
static readonly createGanDanFileOrder = BasicConstant.expertPay + 'createGanDanFileOrder'
static readonly getBalance = BasicConstant.expertPay + 'getBalance'
static readonly payGanDanFileOrder = BasicConstant.expertPay + 'payGanDanFileOrder'
static readonly getOrderStatus = BasicConstant.expertPay + 'getOrderStatus'
static readonly useWelfareNum = BasicConstant.urlExpertAPI + 'useWelfareNum'
static readonly allXinyiList = BasicConstant.expertPay + 'allXinyiList'
static readonly xinyiPrice = BasicConstant.expertPay + 'xinyiPrice'
static readonly createXinYiOrder = BasicConstant.expertPay + 'createXinYiOrder'
static readonly payXinYiOrder = BasicConstant.expertPay + 'payXinYiOrder'
static readonly getFlowerList = BasicConstant.expertPay + 'getFlowerList'
static readonly tagList = BasicConstant.urlExpertApp + "tagList";
static readonly meetingListBySearch = BasicConstant.urlExpertAPI + "meetingListBySearch";
static readonly videoBySearchNew = BasicConstant.urlExpertApp+'videoBySearchNew';//搜索肝胆视频列表
@@ -135,6 +159,8 @@ export class BasicConstant {
static readonly notification_back_refreshData = 250529;//返回上页通知刷新数据
//首页tabContent切换事件通知
static readonly notification_home_tab_change = 25060413;
//课件支付成功返回上一页下载科技
static readonly notification_kejian_pay_success = 202509041128;
static readonly YX_accid='YX_accid'//云信
@@ -0,0 +1,2 @@
export const APP_ID = "wxbf3658f5e674667c"
export const APP_SECRET = "c4505a04a9910c65efea8e11ffc93f92"
@@ -0,0 +1,51 @@
// 文件信息接口
export interface FileInfo {
fileId: string;
fileName: string;
fileType: string;
fileSize: number;
fileUrl: string;
filePath?: string;
downloadStatus: 'pending' | 'downloading' | 'downloaded' | 'failed' | 'cancelled';
downloadProgress: number;
createTime: number;
updateTime: number;
}
// 下载进度回调
export interface DownloadCallback {
onProgress?: (progress: number) => void;
onSuccess?: (filePath: string) => void;
onFailed?: (error: string) => void;
onCancelled?: () => void;
}
// 文件预览配置
export interface PreviewConfig {
enableCache?: boolean;
maxCacheSize?: number;
supportedTypes?: string[];
}
// 定义下载状态枚举
export enum DownloadStatus {
PENDING = 'pending', // 等待中
DOWNLOADING = 'downloading', // 下载中
PAUSED = 'paused', // 已暂停
COMPLETED = 'completed', // 已完成
FAILED = 'failed', // 失败
}
// 定义文件项接口
export interface DownloadFileItem {
id: string; // 文件唯一标识
name: string; // 文件名
url: string; // 下载地址
localPath: string; // 本地存储路径
size: number; // 文件大小(字节)
downloadedSize: number; // 已下载大小(字节)
status: DownloadStatus; // 下载状态
createTime: number; // 创建时间
finishTime?: number; // 完成时间
error?: string; // 错误信息(失败时)
}
@@ -0,0 +1,44 @@
import * as wxopensdk from '@tencent/wechat_open_sdk';
import { APP_ID } from '../constants/Constants';
export type OnWXReq = (req: wxopensdk.BaseReq) => void
export type OnWXResp = (resp: wxopensdk.BaseResp) => void
const kTag = "WXApiEventHandlerImpl"
class WXApiEventHandlerImpl implements wxopensdk.WXApiEventHandler {
private onReqCallbacks: Map<OnWXReq, OnWXReq> = new Map
private onRespCallbacks: Map<OnWXResp, OnWXResp> = new Map
registerOnWXReqCallback(on: OnWXReq) {
this.onReqCallbacks.set(on, on)
}
unregisterOnWXReqCallback(on: OnWXReq) {
this.onReqCallbacks.delete(on)
}
registerOnWXRespCallback(on: OnWXResp) {
this.onRespCallbacks.set(on, on)
}
unregisterOnWXRespCallback(on: OnWXResp) {
this.onRespCallbacks.delete(on)
}
onReq(req: wxopensdk.BaseReq): void {
wxopensdk.Log.i(kTag, "onReq:%s", JSON.stringify(req))
this.onReqCallbacks.forEach((on) => {
on(req)
})
}
onResp(resp: wxopensdk.BaseResp): void {
wxopensdk.Log.i(kTag, "onResp:%s", JSON.stringify(resp))
this.onRespCallbacks.forEach((on) => {
on(resp)
})
}
}
export const WXApi = wxopensdk.WXAPIFactory.createWXAPI(APP_ID)
export const WXEventHandler = new WXApiEventHandlerImpl
@@ -111,7 +111,7 @@ export class AESEncryptionDecryption {
globalResult = AESEncryptionDecryption.uint8ArrayToString(result.data);
console.info('解密后的明文:' + globalResult);
} catch (err) {
console.info(err.message);
console.info('解密失败:',err.message);
}
}
@@ -3,6 +3,7 @@ import { BusinessError } from '@ohos.base'
import { KeyboardAvoidMode, window } from '@kit.ArkUI'
import { resourceManager } from '@kit.LocalizationKit'
import { common } from '@kit.AbilityKit'
import display from '@ohos.display'
export class AppUtil {
@@ -236,6 +237,22 @@ export class AppUtil {
}
/**
* display width
* @returns width in px
*/
static getDisplayWindowWidth(): Pixels {
return px(display.getDefaultDisplaySync().width)
}
/**
* display height
* @returns height in px
*/
static getDisplayWindowHeight(): Pixels {
return px(display.getDefaultDisplaySync().height)
}
/**
* 设置沉浸式状态栏
* @param isLayoutFullScreen 窗口的布局是否为沉浸式布局(该沉浸式布局状态栏、导航栏仍然显示)。true表示沉浸式布局;false表示非沉浸式布局。
@@ -342,6 +359,29 @@ export class AppUtil {
AppUtil.getContext().terminateSelf();
AppUtil.getContext().getApplicationContext().killAllProcesses();
}
}
export function px(value: number) {
return new Pixels(value)
}
export class Pixels {
value: number;
constructor(value: number) {
this.value = value;
}
get px() {
return this.value
}
get vp() {
return px2vp(this.value)
}
toString() {
return `${this.value}px`
}
}
@@ -315,6 +315,18 @@ export class ChangeUtil {
return height
}
static formatPrice(priceStr: string): string {
let priceInFen = parseFloat(priceStr);
let priceInYuan = priceInFen / 100;
return `${priceInYuan.toFixed(2)}`;
}
static formatPrice2(priceStr: string,discount: string): string {
let priceInFen = parseFloat(priceStr);
let priceInYuan = priceInFen / 100 * parseFloat(discount);
return `${priceInYuan.toFixed(2)}`;
}
static Logout(phone:string)
{
authStore.delUser();
@@ -0,0 +1,366 @@
import fileIO from '@ohos.fileio';
import request from '@ohos.request';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
/**
* 文件下载状态枚举
*/
export enum DownloadStatus {
DOWNLOADING = 'downloading',
COMPLETED = 'completed',
FAILED = 'failed'
}
/**
* 文件信息接口
*/
export interface FileInfo {
id: string;
name: string;
url: string;
format: string;
size: number;
status: DownloadStatus;
localPath: string;
downloadTime: number;
}
/**
* 文件下载管理类
*/
export class FileDownloadManager {
private static instance: FileDownloadManager;
private downloadQueue: Map<string, FileInfo> = new Map();
private downloadTasks: Map<string, request.DownloadTask> = new Map();
private context: common.UIAbilityContext;
private downloadDir: string = '';
private constructor(context: common.UIAbilityContext) {
this.context = context;
this.initDownloadDirectory();
}
/**
* 获取单例实例
*/
public static getInstance(context: common.UIAbilityContext): FileDownloadManager {
if (!FileDownloadManager.instance) {
FileDownloadManager.instance = new FileDownloadManager(context);
}
return FileDownloadManager.instance;
}
/**
* 初始化下载目录
*/
private async initDownloadDirectory(): Promise<void> {
try {
const filesDir = this.context.filesDir;
this.downloadDir = `${filesDir}/downloads`;
// 检查并创建下载目录
try {
await fileIO.access(this.downloadDir);
} catch {
await fileIO.mkdir(this.downloadDir);
}
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '初始化下载目录失败: %{public}s', String(error));
}
}
/**
* 开始下载文件
*/
public async downloadFile(url: string,fileId:string, fileName: string, fileType:string): Promise<string> {
try {
// 从URL中提取文件格式
const format = this.extractFileFormat(url);
// 生成文件名
const finalFileName = `${fileName}/${fileId}.${fileType}`;
// 创建文件信息
const fileInfo: FileInfo = {
id: fileId,
name: finalFileName,
url: url,
format: format,
size: 0,
status: DownloadStatus.DOWNLOADING,
localPath: `${this.downloadDir}/${finalFileName}`,
downloadTime: Date.now()
};
// 添加到下载队列
this.downloadQueue.set(fileId, fileInfo);
// 开始下载
await this.startDownload(fileInfo);
return fileId;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '下载文件失败: %{public}s', String(error));
throw new Error(String(error));
}
}
/**
* 开始下载任务
*/
private async startDownload(fileInfo: FileInfo): Promise<void> {
try {
const config: request.DownloadConfig = {
url: fileInfo.url,
title: fileInfo.name,
description: '文件下载中...',
filePath: fileInfo.localPath,
header: {
'User-Agent': 'HarmonyOS File Downloader'
}
};
const downloadTask = await request.downloadFile(this.context, config);
this.downloadTasks.set(fileInfo.id, downloadTask);
// 监听下载进度
downloadTask.on('progress', (receivedSize: number, totalSize: number) => {
fileInfo.size = totalSize;
this.updateFileInfo(fileInfo);
});
// 监听下载完成
downloadTask.on('complete', () => {
fileInfo.status = DownloadStatus.COMPLETED;
this.updateFileInfo(fileInfo);
this.downloadTasks.delete(fileInfo.id);
});
// 监听下载失败
downloadTask.on('fail', (err: number) => {
fileInfo.status = DownloadStatus.FAILED;
this.updateFileInfo(fileInfo);
this.downloadTasks.delete(fileInfo.id);
hilog.error(0x0000, 'FileDownloadManager', '下载失败: %{public}d', err);
});
} catch (error) {
fileInfo.status = DownloadStatus.FAILED;
this.updateFileInfo(fileInfo);
throw new Error(String(error));
}
}
/**
* 更新文件信息
*/
private updateFileInfo(fileInfo: FileInfo): void {
this.downloadQueue.set(fileInfo.id, fileInfo);
}
/**
* 从URL提取文件格式
*/
private extractFileFormat(url: string): string {
const supportedFormats = ['docx', 'txt', 'ppt', 'pdf', 'doc', 'pptx', 'xls', 'xlsx'];
const urlParts = url.split('.');
const format = urlParts[urlParts.length - 1].toLowerCase();
return supportedFormats.includes(format) ? format : 'bin';
}
/**
* 获取下载状态
*/
public getDownloadStatus(fileId: string): DownloadStatus | null {
const fileInfo = this.downloadQueue.get(fileId);
return fileInfo ? fileInfo.status : null;
}
/**
* 获取文件信息
*/
public getFileInfo(fileId: string): FileInfo | null {
return this.downloadQueue.get(fileId) || null;
}
/**
* 获取所有文件信息
*/
public getAllFiles(): FileInfo[] {
return Array.from(this.downloadQueue.values());
}
/**
* 获取指定状态的文件
*/
public getFilesByStatus(status: DownloadStatus): FileInfo[] {
return Array.from(this.downloadQueue.values()).filter(file => file.status === status);
}
/**
* 查看单个文件
*/
public async viewFile(fileId: string): Promise<boolean> {
try {
const fileInfo = this.downloadQueue.get(fileId);
if (!fileInfo || fileInfo.status !== DownloadStatus.COMPLETED) {
throw new Error('文件不存在或下载未完成');
}
// 检查文件是否存在
try {
await fileIO.access(fileInfo.localPath);
} catch {
throw new Error('文件不存在');
}
// 获取文件信息
const stat = await fileIO.stat(fileInfo.localPath);
hilog.info(0x0000, 'FileDownloadManager', '文件信息: %{public}s', JSON.stringify(stat));
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '查看文件失败: %{public}s', String(error));
return false;
}
}
/**
* 查看所有文件
*/
public async viewAllFiles(): Promise<FileInfo[]> {
try {
const allFiles = this.getAllFiles();
const completedFiles: FileInfo[] = [];
for (const file of allFiles) {
if (file.status === DownloadStatus.COMPLETED) {
try {
await fileIO.access(file.localPath);
completedFiles.push(file);
} catch {
// 文件不存在,跳过
}
}
}
return completedFiles;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '查看所有文件失败: %{public}s', String(error));
return [];
}
}
/**
* 删除单个文件
*/
public async deleteFile(fileId: string): Promise<boolean> {
try {
const fileInfo = this.downloadQueue.get(fileId);
if (!fileInfo) {
throw new Error('文件不存在');
}
// 取消下载任务
const downloadTask = this.downloadTasks.get(fileId);
if (downloadTask) {
downloadTask.off('progress');
downloadTask.off('complete');
downloadTask.off('fail');
this.downloadTasks.delete(fileId);
}
// 删除本地文件
if (fileInfo.status === DownloadStatus.COMPLETED) {
try {
await fileIO.unlink(fileInfo.localPath);
} catch {
// 文件可能不存在,忽略错误
}
}
// 从队列中移除
this.downloadQueue.delete(fileId);
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '删除文件失败: %{public}s', String(error));
return false;
}
}
/**
* 删除所有文件
*/
public async deleteAllFiles(): Promise<boolean> {
try {
const allFiles = Array.from(this.downloadQueue.keys());
for (const fileId of allFiles) {
await this.deleteFile(fileId);
}
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '删除所有文件失败: %{public}s', String(error));
return false;
}
}
/**
* 暂停下载
*/
public pauseDownload(fileId: string): boolean {
const downloadTask = this.downloadTasks.get(fileId);
if (downloadTask) {
downloadTask.off('progress');
downloadTask.off('complete');
downloadTask.off('fail');
this.downloadTasks.delete(fileId);
return true;
}
return false;
}
/**
* 恢复下载
*/
public async resumeDownload(fileId: string): Promise<boolean> {
try {
const fileInfo = this.downloadQueue.get(fileId);
if (!fileInfo || fileInfo.status !== DownloadStatus.DOWNLOADING) {
return false;
}
await this.startDownload(fileInfo);
return true;
} catch (error) {
hilog.error(0x0000, 'FileDownloadManager', '恢复下载失败: %{public}s', String(error));
return false;
}
}
/**
* 清理下载目录
*/
// public async cleanDownloadDirectory(): Promise<boolean> {
// try {
// // 使用fileIO.listDir替代readdir
// const files = await fileIO.listDir(this.downloadDir);
// for (const file of files) {
// const filePath = `${this.downloadDir}/${file}`;
// try {
// await fileIO.unlink(filePath);
// } catch {
// // 忽略删除失败的文件
// }
// }
// return true;
// } catch (error) {
// hilog.error(0x0000, 'FileDownloadManager', '清理下载目录失败: %{public}s', String(error));
// return false;
// }
// }
}
@@ -0,0 +1,799 @@
import { http } from '@kit.NetworkKit';
import { promptAction, router } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import { fileIo, fileUri } from '@kit.CoreFileKit';
import { FileInfo,DownloadCallback } from '../models/FileModel'
import preferences from '@ohos.data.preferences';
import { Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { filePreview } from '@kit.PreviewKit';
import request from '@ohos.request';
// 下载任务接口
export interface DownloadTask {
httpRequest: http.HttpRequest;
filePath: string;
}
// 文件统计信息接口
export interface FileStat {
size: number;
ctime: number;
mtime: number;
}
/**
* 文件管理类 - 提供下载、查询、预览等功能
*/
export class FileManager {
private context: common.Context;
private downloadTasks: Map<string, DownloadTask> = new Map();
private downloadCallbacks: Map<string, DownloadCallback> = new Map();
private fileCache: Map<string, FileInfo> = new Map();
private downloadDir: string;
private isDownloading: boolean = false;
private preferencesHelper: preferences.Preferences | null = null;
private readonly STORE_NAME = 'file_manager_store';
private readonly FILE_INFO_KEY = 'file_info_cache';
constructor(context: common.Context) {
this.context = context;
this.downloadDir = `${this.context.filesDir}/downloads`;
this.initDownloadDirectory();
this.initPreferences();
this.loadFileInfoFromStorage();
}
/**
* 初始化偏好设置存储
*/
private async initPreferences(): Promise<void> {
try {
this.preferencesHelper = await preferences.getPreferences(this.context, this.STORE_NAME);
} catch (error) {
console.error('初始化偏好设置失败:', error);
}
}
/**
* 从存储加载文件信息
*/
private async loadFileInfoFromStorage(): Promise<void> {
try {
if (this.preferencesHelper) {
const fileInfoJson = await this.preferencesHelper.get(this.FILE_INFO_KEY, '[]');
const fileInfoArray: FileInfo[] = JSON.parse(fileInfoJson as string);
// 恢复缓存
for (const fileInfo of fileInfoArray) {
this.fileCache.set(fileInfo.fileId, fileInfo);
}
console.info(`从存储加载了 ${fileInfoArray.length} 个文件信息`);
}
} catch (error) {
console.error('加载文件信息失败:', error);
}
}
/**
* 保存文件信息到存储
*/
private async saveFileInfoToStorage(): Promise<void> {
try {
if (this.preferencesHelper) {
const fileInfoArray = Array.from(this.fileCache.values());
const fileInfoJson = JSON.stringify(fileInfoArray);
await this.preferencesHelper.put(this.FILE_INFO_KEY, fileInfoJson);
await this.preferencesHelper.flush();
}
} catch (error) {
console.error('保存文件信息失败:', error);
}
}
/**
* 初始化下载目录
*/
private async initDownloadDirectory(): Promise<void> {
try {
if (!fileIo.accessSync(this.downloadDir)) {
fileIo.mkdirSync(this.downloadDir);
}
} catch (error) {
console.error('创建下载目录失败:', error);
}
}
/**
* 下载文件
* @param fileInfo 文件信息
* @param callback 下载回调
* @returns Promise<boolean>
*/
async downloadFile(fileInfo: FileInfo, callback?: DownloadCallback): Promise<boolean> {
try {
// 检查是否正在下载
if (this.downloadTasks.has(fileInfo.fileId)) {
promptAction.showToast({ message: '文件正在下载中,请勿重复操作', duration: 2000 });
return false;
}
// 检查文件是否已下载
if (await this.isFileDownloaded(fileInfo.fileId)) {
promptAction.showToast({ message: '文件已下载', duration: 2000 });
return true;
}
// 设置下载状态
this.isDownloading = true;
fileInfo.downloadStatus = 'downloading';
fileInfo.downloadProgress = 0;
// 保存回调
if (callback) {
this.downloadCallbacks.set(fileInfo.fileId, callback);
}
// 创建下载任务
const downloadTask = await this.createDownloadTask(fileInfo);
this.downloadTasks.set(fileInfo.fileId, downloadTask);
// 开始下载
const success = await this.startDownload(downloadTask, fileInfo);
if (success) {
fileInfo.downloadStatus = 'downloaded';
fileInfo.downloadProgress = 100;
this.onDownloadSuccess(fileInfo);
} else {
fileInfo.downloadStatus = 'failed';
this.onDownloadFailed(fileInfo, '下载失败');
}
return success;
} catch (error) {
console.error('下载文件失败:', error);
fileInfo.downloadStatus = 'failed';
this.onDownloadFailed(fileInfo, '下载失败');
return false;
} finally {
this.isDownloading = false;
this.downloadTasks.delete(fileInfo.fileId);
}
}
/**
* 创建下载任务
*/
private async createDownloadTask(fileInfo: FileInfo): Promise<DownloadTask> {
try {
// 创建文件名目录
const fileNameDir = `${fileInfo.fileName}`;
const fileNameDirPath = `${this.downloadDir}/${fileNameDir}`;
// 确保目录存在
if (!fileIo.accessSync(fileNameDirPath)) {
fileIo.mkdirSync(fileNameDirPath);
}
// 设置文件保存路径 - 格式:文件名/文件ID.文件格式
const fileName = `${fileInfo.fileId}.${fileInfo.fileType}`;
const filePath = `${fileNameDirPath}/${fileName}`;
fileInfo.filePath = filePath;
// 创建HTTP请求
const httpRequest = http.createHttp();
const task: DownloadTask = {
httpRequest: httpRequest,
filePath: filePath
};
return task;
} catch (error) {
console.error('创建下载任务失败:', error);
throw new Error('创建下载任务失败');
}
}
/**
* 开始下载
*/
private async startDownload(downloadTask: DownloadTask, fileInfo: FileInfo): Promise<boolean> {
//
// let contexta = getContext(this) as common.UIAbilityContext;
// request.downloadFile(contexta.getApplicationContext(), {
// url: fileInfo.fileUrl,
// filePath: downloadTask.filePath,
// enableMetered:true
// }).then((downloadTask: request.DownloadTask) => {
// let progresCallback = (receivedSize: number, totalSize: number) => {
// console.info("downloadddd1 receivedSize:" + receivedSize + " totalSize:" + totalSize);
// };
// let pauseCallback = () => {
// console.info('Downloadddd task pause.');
// };
// //开启进度回调
// downloadTask.on('progress', progresCallback);
// //开启暂停回调
// downloadTask.on('pause', pauseCallback);
// //开启下载完成回调
// downloadTask.on('complete', async () => {
// const taskInfo = await downloadTask.getTaskInfo();
// console.info('downloaddddTask1 complete:'+`status: ${taskInfo.status}`);
// // downloadTask.getTaskInfo();
//
// })
// return true
// }).catch((err: BusinessError) => {
// console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
// return false;
// })
try {
const httpRequest = downloadTask.httpRequest;
const filePath = downloadTask.filePath;
// 配置下载选项 - 参考iOS代码的请求头设置
const options: http.HttpRequestOptions = {
method: http.RequestMethod.GET,
header: {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HarmonyOS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
'Accept-Language': 'zh-CN,zh;q=0.9',
},
expectDataType: http.HttpDataType.ARRAY_BUFFER,
usingCache: false,
connectTimeout: 60000,
readTimeout: 300000
};
console.info(`开始下载文件: ${fileInfo.fileUrl}`);
console.info(`目标路径: ${filePath}`);
// 发起下载请求
const response = await httpRequest.request(fileInfo.fileUrl, options);
console.info(`响应状态码: ${response.responseCode}`);
console.info(`响应头: ${JSON.stringify(response.header)}`);
if (response.responseCode === 200) {
try {
// 验证响应数据
if (!response.result) {
console.error('下载响应数据为空');
return false;
}
// 检查数据类型和大小
if (!(response.result instanceof ArrayBuffer)) {
console.error('响应数据不是ArrayBuffer类型:', typeof response.result);
return false;
}
const dataSize = response.result.byteLength;
console.info(`下载数据大小: ${dataSize} 字节`);
if (dataSize <= 10) {
console.error('文件有误:',response.result)
return false
}
// 确保目标目录存在
const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
if (!fileIo.accessSync(dirPath)) {
fileIo.mkdirSync(dirPath);
}
// 将下载的数据写入文件
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
try {
// 写入文件数据
const bytesWritten = fileIo.writeSync(file.fd, response.result);
console.info(`写入文件成功,写入字节数: ${bytesWritten}`);
// 验证写入的字节数
if (bytesWritten !== dataSize) {
console.error(`写入字节数不匹配: 期望 ${dataSize}, 实际 ${bytesWritten}`);
fileIo.closeSync(file);
fileIo.unlinkSync(filePath); // 删除损坏的文件
return false;
}
// 同步文件到磁盘
fileIo.fsyncSync(file.fd);
// 关闭文件
fileIo.closeSync(file);
// 验证文件是否创建成功
if (!fileIo.accessSync(filePath)) {
console.error('文件创建失败');
return false;
}
// 获取文件统计信息
const stat = fileIo.statSync(filePath);
console.info(`文件创建成功: ${filePath}, 大小: ${stat.size} 字节`);
// 验证文件大小
if (stat.size !== dataSize) {
console.error(`文件大小不匹配: 期望 ${dataSize}, 实际 ${stat.size}`);
fileIo.unlinkSync(filePath);
return false;
}
// 更新文件信息
fileInfo.fileSize = stat.size;
await this.saveFileInfo(fileInfo);
return true;
} catch (writeError) {
console.error('写入文件失败:', writeError);
fileIo.closeSync(file);
// 清理可能创建的文件
try {
if (fileIo.accessSync(filePath)) {
fileIo.unlinkSync(filePath);
}
} catch (cleanupError) {
console.error('清理文件失败:', cleanupError);
}
return false;
}
} catch (writeError) {
console.error('文件写入过程失败:', writeError);
return false;
}
} else {
console.error(`下载失败,状态码: ${response.responseCode}`);
return false;
}
} catch (error) {
console.error('下载执行失败:', error);
return false;
} finally {
// 销毁HTTP请求
downloadTask.httpRequest.destroy();
}
}
/**
* 取消下载
*/
async cancelDownload(fileId: string): Promise<boolean> {
try {
const task = this.downloadTasks.get(fileId);
if (task) {
// 销毁HTTP请求
task.httpRequest.destroy();
this.downloadTasks.delete(fileId);
// 更新状态
const fileInfo = this.fileCache.get(fileId);
if (fileInfo) {
fileInfo.downloadStatus = 'cancelled';
fileInfo.downloadProgress = 0;
this.onDownloadCancelled(fileInfo);
}
return true;
}
return false;
} catch (error) {
console.error('取消下载失败:', error);
return false;
}
}
/**
* 查询已下载文件
*/
async getDownloadedFiles(): Promise<FileInfo[]> {
try {
const files: FileInfo[] = [];
// 从缓存获取
for (const fileInfo of this.fileCache.values()) {
if (fileInfo.downloadStatus === 'downloaded') {
files.push(fileInfo);
}
}
// 从本地目录扫描
const localFiles = await this.scanLocalFiles();
files.push(...localFiles);
// 按时间排序
return files.sort((a, b) => b.updateTime - a.updateTime);
} catch (error) {
console.error('查询下载文件失败:', error);
return [];
}
}
/**
* 扫描本地文件
*/
private async scanLocalFiles(): Promise<FileInfo[]> {
try {
const files: FileInfo[] = [];
if (!fileIo.accessSync(this.downloadDir)) {
return files;
}
const entryList = fileIo.listFileSync(this.downloadDir);
for (let i = 0; i < entryList.length; i++) {
const entryName = entryList[i];
const entryPath = `${this.downloadDir}/${entryName}`;
// 优先按目录处理(当前下载结构是 按文件名建目录,目录下放置 文件ID.扩展名)
let childNames: string[] | null = null;
try {
childNames = fileIo.listFileSync(entryPath);
} catch (_e) {
childNames = null;
}
if (childNames && childNames.length > 0) {
// 目录:遍历子文件
for (let j = 0; j < childNames.length; j++) {
const childName = childNames[j];
const childPath = `${entryPath}/${childName}`;
try {
const childStat = fileIo.statSync(childPath);
const fileInfo = this.parseFileName(childName, childPath, childStat);
if (fileInfo) {
files.push(fileInfo);
}
} catch (childErr) {
console.error('读取子文件失败:', childErr);
}
}
} else {
// 非目录项(兼容旧结构,直接作为文件解析)
try {
const stat = fileIo.statSync(entryPath);
const fileInfo = this.parseFileName(entryName, entryPath, stat);
if (fileInfo) {
files.push(fileInfo);
}
} catch (fileErr) {
console.error('读取文件失败:', fileErr);
}
}
}
return files;
} catch (error) {
console.error('扫描本地文件失败:', error);
return [];
}
}
/**
* 解析文件名
*/
private parseFileName(fileName: string, filePath: string, stat: fileIo.Stat): FileInfo | null {
try {
// 新的文件名格式: 文件名/文件ID.扩展名
const pathParts = filePath.split('/');
if (pathParts.length < 2) return null;
const fileNameDir = pathParts[pathParts.length - 2]; // 文件名目录
const fileNameWithExt = pathParts[pathParts.length - 1]; // 文件ID.扩展名
const fileIdParts = fileNameWithExt.split('.');
if (fileIdParts.length < 2) return null;
const fileId = fileIdParts[0];
const extension = fileIdParts[1];
return {
fileId,
fileName: fileNameDir, // 使用目录名作为文件名
fileType: extension,
fileSize: stat.size,
fileUrl: '',
filePath: filePath,
downloadStatus: 'downloaded',
downloadProgress: 100,
createTime: stat.ctime,
updateTime: stat.mtime
};
} catch (error) {
console.error('解析文件名失败:', error);
return null;
}
}
/**
* 检查文件是否已下载
*/
private async isFileDownloaded(fileId: string): Promise<boolean> {
try {
// 检查缓存
const cachedFile = this.fileCache.get(fileId);
if (cachedFile && cachedFile.downloadStatus === 'downloaded') {
return true;
}
// 检查本地文件
const localFiles = await this.scanLocalFiles();
for (let i = 0; i < localFiles.length; i++) {
if (localFiles[i].fileId === fileId) {
return true;
}
}
return false;
} catch (error) {
console.error('检查文件下载状态失败:', error);
return false;
}
}
/**
* 预览文件 - 使用系统原生方法
*/
async previewFile(identifier: string) {
try {
// 1) 优先按 uuid 在缓存中查找
let target: FileInfo | null = null;
const cachedById = this.fileCache.get(identifier);
if (cachedById) {
target = cachedById;
}
// 2) 若未命中,再按文件名在缓存中查找(取最近更新)
if (!target) {
let latest: FileInfo | null = null;
for (const info of this.fileCache.values()) {
if (info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
// 3) 若缓存仍未命中,扫描本地目录
if (!target) {
const localFiles = await this.scanLocalFiles();
let latest: FileInfo | null = null;
for (const info of localFiles) {
if (info.fileId === identifier || info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
if (!target || !target.filePath) {
promptAction.showToast({ message: '未找到对应文件', duration: 2000 });
return;
}
const filePath = target.filePath;
const fileType = target.fileType || '';
// 文件是否存在
if (!fileIo.accessSync(filePath)) {
promptAction.showToast({ message: '文件不存在', duration: 2000 });
return;
}
let uri = fileUri.getUriFromPath(filePath);
filePreview.canPreview(this.context, uri).then((result) => { // 传入支持的文件类型且项目存在时会返回true
console.info(`Succeeded in obtaining the result of whether it can be previewed. result = ${result}`);
}).catch((err: BusinessError) => {
console.error(`Failed to obtain the result of whether it can be previewed, err.code = ${err.code}, err.message = ${err.message}`);
});
let fileInfo: filePreview.PreviewInfo = {
title: target.fileName,
uri: uri,
mimeType: this.getMimeType(fileType)
};
let files: Array<filePreview.PreviewInfo> = new Array();
files.push(fileInfo);
filePreview.openPreview(this.context, files, 0).then(() => {
console.info('Succeeded in opening preview');
}).catch((err: BusinessError) => {
console.error(`Failed to open preview, err.code = ${err.code}, err.message = ${err.message}`);
});
} catch (error) {
console.error('打开文件异常:', error);
}
}
/**
* 获取MIME类型
*/
private getMimeType(fileType: string): string {
const mimeTypes: Record<string, string> = {
'pdf': 'application/pdf',
'doc': 'application/msword',
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'ppt': 'application/vnd.ms-powerpoint',
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'xls': 'application/vnd.ms-excel',
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'txt': 'text/plain',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'bmp': 'image/bmp',
'mp4': 'video/mp4',
'avi': 'video/x-msvideo',
'mov': 'video/quicktime',
'wmv': 'video/x-ms-wmv'
};
return mimeTypes[fileType.toLowerCase()] || '*/*';
}
/**
* 删除文件
*/
async deleteFile(identifier: string): Promise<boolean> {
try {
// 1) 优先按 fileId 在缓存中查找
let target: FileInfo | null = null;
const cachedById = this.fileCache.get(identifier);
if (cachedById) {
target = cachedById;
}
// 2) 若未命中,再按文件名在缓存中查找(取最近更新)
if (!target) {
let latest: FileInfo | null = null;
for (const info of this.fileCache.values()) {
if (info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
// 3) 若缓存仍未命中,扫描本地目录
if (!target) {
const localFiles = await this.scanLocalFiles();
let latest: FileInfo | null = null;
for (const info of localFiles) {
if (info.fileId === identifier || info.fileName === identifier) {
if (!latest || info.updateTime > latest.updateTime) {
latest = info;
}
}
}
target = latest;
}
if (!target || !target.filePath) {
console.error('未找到要删除的文件:', identifier);
return false;
}
// 删除本地文件
if (fileIo.accessSync(target.filePath)) {
fileIo.unlinkSync(target.filePath);
console.info(`成功删除文件: ${target.filePath}`);
// 删除文件后,检查并删除空的文件夹
await this.cleanupEmptyDirectory(target.filePath);
}
// 从缓存中移除
this.fileCache.delete(target.fileId);
// 更新持久化存储
await this.saveFileInfoToStorage();
return true;
} catch (error) {
console.error('删除文件失败:', error);
return false;
}
}
/**
* 清理空文件夹
*/
private async cleanupEmptyDirectory(filePath: string): Promise<void> {
try {
// 获取文件所在目录
const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
// 检查目录是否存在
if (!fileIo.accessSync(dirPath)) {
return;
}
// 检查目录是否为空
const files = fileIo.listFileSync(dirPath);
if (files.length === 0) {
// 目录为空,删除目录
fileIo.rmdirSync(dirPath);
console.info(`成功删除空文件夹: ${dirPath}`);
// 递归检查父目录是否也为空(但不要删除下载根目录)
const parentDir = dirPath.substring(0, dirPath.lastIndexOf('/'));
if (parentDir !== this.downloadDir && fileIo.accessSync(parentDir)) {
const parentFiles = fileIo.listFileSync(parentDir);
if (parentFiles.length === 0) {
fileIo.rmdirSync(parentDir);
console.info(`成功删除空父文件夹: ${parentDir}`);
}
}
}
} catch (error) {
console.error('清理空文件夹失败:', error);
}
}
/**
* 获取下载状态
*/
isDownloadingFile(): boolean {
return this.isDownloading;
}
/**
* 保存文件信息
*/
private async saveFileInfo(fileInfo: FileInfo): Promise<void> {
try {
this.fileCache.set(fileInfo.fileId, fileInfo);
// 保存到持久化存储
await this.saveFileInfoToStorage();
} catch (error) {
console.error('保存文件信息失败:', error);
}
}
/**
* 下载成功回调
*/
private onDownloadSuccess(fileInfo: FileInfo): void {
const callback = this.downloadCallbacks.get(fileInfo.fileId);
if (callback && callback.onSuccess) {
callback.onSuccess(fileInfo.filePath || '');
}
this.downloadCallbacks.delete(fileInfo.fileId);
promptAction.showToast({ message: '下载完成', duration: 2000 });
}
/**
* 下载失败回调
*/
private onDownloadFailed(fileInfo: FileInfo, error: string): void {
const callback = this.downloadCallbacks.get(fileInfo.fileId);
if (callback && callback.onFailed) {
callback.onFailed(error);
}
this.downloadCallbacks.delete(fileInfo.fileId);
promptAction.showToast({ message: error, duration: 2000 });
}
/**
* 下载取消回调
*/
private onDownloadCancelled(fileInfo: FileInfo): void {
const callback = this.downloadCallbacks.get(fileInfo.fileId);
if (callback && callback.onCancelled) {
callback.onCancelled();
}
this.downloadCallbacks.delete(fileInfo.fileId);
promptAction.showToast({ message: '下载已取消', duration: 2000 });
}
}
@@ -0,0 +1,346 @@
// 导入必要的鸿蒙模块
import http from '@ohos.net.http';
import fs from '@ohos.file.fs';
import relationalStore from '@ohos.data.relationalStore';
import common from '@ohos.app.ability.common';
import security from '@ohos.security.cryptoFramework';
import util from '@ohos.util';
import { BusinessError } from '@ohos.base';
import promptAction from '@ohos.promptAction';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { authStore } from './auth';
import { AESEncryptionDecryption } from './AESEncryptionDecryption';
import { BasicConstant } from '../constants/BasicConstant';
import { rcp } from '@kit.RemoteCommunicationKit';
import fileIo from '@ohos.file.fs';
// 1. 定义数据模型和数据库结构
// 文件信息实体
class WPSFile {
fileId: string = ''; // 对应fileID
fileTitle: string = ''; // 对应fileTitle
fileType: string = ''; // 对应fileType
filePath: string = ''; // 本地存储路径
downloadType: string = ''; // 下载状态: 'downloading', 'downloadFalse', 'downloaded'
md5?: string = ''; // 文件MD5值
}
// 数据库管理器
export class DBManager {
private rdbStore: relationalStore.RdbStore | null = null;
private readonly TABLE_NAME: string = 'WPSFILELIST';
private readonly STORE_CONFIG: relationalStore.StoreConfig = {
name: 'WPSFileDB.db',
securityLevel: relationalStore.SecurityLevel.S1,
};
private context: common.Context;
constructor(context: common.Context) {
this.context = context;
}
// 初始化数据库
async initializeDB(): Promise<void> {
try {
this.rdbStore = await relationalStore.getRdbStore(this.context, this.STORE_CONFIG);
// 创建表
const sql = `
CREATE TABLE IF NOT EXISTS ${this.TABLE_NAME} (
fileId TEXT PRIMARY KEY,
fileTitle TEXT NOT NULL,
fileType TEXT NOT NULL,
filePath TEXT,
downloadType TEXT NOT NULL,
md5 TEXT
)`;
await this.rdbStore.executeSql(sql);
console.log('数据库初始化成功');
} catch (err) {
console.error(`数据库初始化失败: ${err.code}, ${err.message}`);
}
}
// 插入文件记录
async insertFile(file: WPSFile): Promise<boolean> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
const valueBucket: relationalStore.ValuesBucket = {
'fileId': file.fileId,
'fileTitle': file.fileTitle,
'fileType': file.fileType,
'filePath': file.filePath,
'downloadType': file.downloadType,
'md5': file.md5 || ''
};
await this.rdbStore?.insert(this.TABLE_NAME, valueBucket);
console.log('文件记录插入成功');
return true;
} catch (err) {
console.error(`插入文件记录失败: ${err.code}, ${err.message}`);
return false;
}
}
// 更新下载状态
async updateDownloadStatus(fileId: string, status: string): Promise<boolean> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
const valueBucket: relationalStore.ValuesBucket = {
'downloadType': status
};
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
predicates.equalTo('fileId', fileId);
await this.rdbStore?.update(valueBucket, predicates);
console.log('下载状态更新成功');
return true;
} catch (err) {
console.error(`更新下载状态失败: ${err.code}, ${err.message}`);
return false;
}
}
// 删除文件记录
async deleteFile(fileId: string): Promise<boolean> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
predicates.equalTo('fileId', fileId);
await this.rdbStore?.delete(predicates);
console.log('文件记录删除成功');
return true;
} catch (err) {
console.error(`删除文件记录失败: ${err.code}, ${err.message}`);
return false;
}
}
// 根据ID查询文件
async getFileById(fileId: string): Promise<WPSFile | null> {
if (!this.rdbStore) {
await this.initializeDB();
}
try {
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
predicates.equalTo('fileId', fileId);
let resultSet = await this.rdbStore?.query(predicates, ['fileId', 'fileTitle', 'fileType', 'filePath', 'downloadType', 'md5']);
if (resultSet && resultSet.rowCount > 0) {
await resultSet.goToFirstRow();
let file = new WPSFile();
file.fileId = resultSet.getString(resultSet.getColumnIndex('fileId')) || '';
file.fileTitle = resultSet.getString(resultSet.getColumnIndex('fileTitle')) || '';
file.fileType = resultSet.getString(resultSet.getColumnIndex('fileType')) || '';
file.filePath = resultSet.getString(resultSet.getColumnIndex('filePath')) || '';
file.downloadType = resultSet.getString(resultSet.getColumnIndex('downloadType')) || '';
file.md5 = resultSet.getString(resultSet.getColumnIndex('md5')) || '';
resultSet.close();
return file;
}
return null;
} catch (err) {
console.error(`查询文件失败: ${err.code}, ${err.message}`);
return null;
}
}
}
// 1. 计算文件 MD5 (使用路径创建流)
async function calculateFileMD5(filePath: string): Promise<string> {
let stream: fs.Stream = fs.createStreamSync(filePath, 'r');
let md5AlgName: string = "MD5";
let hash: cryptoFramework.Md = cryptoFramework.createMd(md5AlgName);
let dataBuff: ArrayBuffer = new ArrayBuffer(4096);
let readCount: number = stream.readSync(dataBuff);
while (readCount > 0) {
let messageData: Uint8Array = new Uint8Array(dataBuff.slice(0, readCount));
let updateMessageBlob: cryptoFramework.DataBlob = { data: messageData };
hash.updateSync(updateMessageBlob);
readCount = stream.readSync(dataBuff);
}
stream.closeSync();
let md5Result: cryptoFramework.DataBlob = hash.digestSync();
let md5Hex: string = Array.from(md5Result.data).map(byte => byte.toString(16).padStart(2, '0')).join('');
return md5Hex;
}
// 2. 文件下载管理器
export class FileManagerCopy {
private httpRequest: http.HttpRequest = http.createHttp();
private context: common.Context;
private dbManager: DBManager;
constructor(context: common.Context) {
this.context = context;
this.dbManager = new DBManager(context);
}
private generateRandomString(): string {
// 生成1到10之间的随机整数作为字符串长度[6,8](@ref)
const minLength: number = 1;
const maxLength: number = 10;
const targetLength: number = Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength;
// 初始化一个空数组用于存储字符
let charArray: string[] = [];
// 循环生成指定数量的随机大写字母[6](@ref)
for (let i = 0; i < targetLength; i++) {
// 生成65('A')到90('Z')之间的随机数[6](@ref)
const randomCharCode: number = Math.floor(Math.random() * 26) + 65;
// 将Unicode编码转换为字符并添加到数组
charArray.push(String.fromCharCode(randomCharCode));
}
return charArray.join('');
}
async downloadFile(wpsUuid: string, wpsTitle: string, wpsType: string, fileMd5: string, order_id:string , orderStatus: number, timestamp: string) {
// 检查是否正在下载
let existingFile = await this.dbManager.getFileById(wpsUuid);
if (existingFile && existingFile.downloadType === 'downloading') {
promptAction.showToast({ message: '正在下载,请勿重复点击', duration: 2000 });
// return false;
}
let daijiami:string = ''
if (orderStatus == 1) {//免费
daijiami = `${wpsUuid}|${order_id}|${authStore.getUser().uuid}|${timestamp}`
}
// else if (orderStatus == 8) {//复制iOS代码逻辑,有这样的内容
// daijiami = `${wpsUuid}|${orderStatus}|${authStore.getUser().uuid}|${timestamp}`
// }
else {
daijiami = `${wpsUuid}|${order_id}|${authStore.getUser().uuid}|${timestamp}`
}
// f6f25104fa0345b38074710c9356948b|USEWELFARENUM|1CBMDQbuOX3xbxAcxE5|2025-09-01 11:02:42
//ios-//f6f25104fa0345b38074710c9356948b|USEWELFARENUM|GA5LeMOXChsKxMrqFnL|2025-08-29 17:25:54
const scanData = await AESEncryptionDecryption.aesEncrypt(daijiami,BasicConstant.ExpertAesKey)
//hGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS/916uiWz8iNWCZMYLa4iKXw3
const encodedString = encodeURIComponent(scanData)
// hGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F916uiWz8iNWCZMYLa4iKXw3
//ios-//hGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv
let pinString = 'X'+encodedString
//XhGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F90X6zMHwPph2jDuPjTMzXqP
//ios-//XhGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv
const targetLength: number = Math.floor(Math.random() * (10 - 1 + 1)) + 1;
let downloadUrl = `${BasicConstant.urlExpertApp}downloadGanDanFile?&gdf=${pinString}&a=${targetLength}`
//https://dev-app.igandan.com/app/expertApp/downloadGanDanFile?&gdf=XhGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F90X6zMHwPph2jDuPjTMzXqP&a=10
//ios-//https://dev-app.igandan.com/app/expertApp/downloadGanDanFile?&gdf=XhGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv&a=1
// 创建下载记录
let wpsFile = new WPSFile();
wpsFile.fileId = wpsUuid;
wpsFile.fileTitle = wpsTitle;
wpsFile.fileType = wpsType;
wpsFile.downloadType = 'downloading';
wpsFile.md5 = fileMd5;
//下载文件保存的文件夹路径,仅为示例,请按需求进行替换。
const DOWNLOAD_TO_PATH = `/data/storage/el2/base/haps/entry/files`;
// 创建了一个安全配置对象,其中remoteValidation设置为'skip',表示将跳过远程验证。
const securityConfig: rcp.SecurityConfiguration = {
remoteValidation: 'skip'
}
// 创建了一个下载配置对象,其中kind设置为'folder',表示下载的目标是文件夹,path设置为之前定义的DOWNLOAD_TO_PATH。
let downloadToFile: rcp.DownloadToFile = {
kind: 'folder',
path: DOWNLOAD_TO_PATH
}
// 创建一个HTTP会话,其中请求配置包括传输超时设置和安全配置(配置可自定义)
const session = rcp.createSession({
requestConfiguration: {
transfer: { timeout: { connectMs: 6000, transferMs: 6000, inactivityMs: 6000 } },
security: securityConfig
}
})
// 检查目标路径是否存在
if (fileIo.accessSync(DOWNLOAD_TO_PATH)) {
fileIo.rmdirSync(DOWNLOAD_TO_PATH);
}
// 发起请求,执行下载操作
session.downloadToFile(downloadUrl, downloadToFile)
.then((response: rcp.Response) => {
console.info(`Successfully received the response, statusCode: ${JSON.stringify(response.statusCode)}`);
}).catch((err: BusinessError) => {
console.error(`Failed, the error message is ${JSON.stringify(err)}`)
})
// // 文件保存路径
// let filesDir = this.context.filesDir;
// wpsFile.filePath = `${filesDir}/${wpsUuid}.${wpsType}`;
//
// // 插入数据库记录
// let insertSuccess = await this.dbManager.insertFile(wpsFile);
// if (!insertSuccess) {
// promptAction.showToast({ message: '下载初始化失败', duration: 2000 });
// return false;
// }
//
// promptAction.showToast({ message: '开始下载,请勿重复点击', duration: 2000 });
//
// try {
// // 配置下载选项
// let options: http.HttpRequestOptions = {
// method: http.RequestMethod.GET,
// header: {
// 'Content-Type': 'application/octet-stream'
// },
// expectDataType: http.HttpDataType.ARRAY_BUFFER,
// usingCache: false,
// };
//
// // 执行下载请求
// let response = await this.httpRequest.request(downloadUrl, options);
// if (response.responseCode === 200) {
// // 保存文件到本地
// let file = fs.openSync(wpsFile.filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// fs.writeSync(file.fd, response.result as ArrayBuffer);
// fs.closeSync(file);
//
// // 验证MD5
// let localMd5 = await calculateFileMD5(wpsFile.filePath)
// if (localMd5 === fileMd5) {
// // 更新数据库状态为下载成功
// await this.dbManager.updateDownloadStatus(wpsUuid, 'downloaded');
// promptAction.showToast({ message: '下载完成', duration: 2000 });
// return true;
// } else {
// // MD5不匹配,删除文件
// fs.unlinkSync(wpsFile.filePath);
// await this.dbManager.deleteFile(wpsUuid);
// promptAction.showToast({ message: '下载失败,文件校验错误', duration: 2000 });
// return false;
// }
// } else {
// // 下载失败
// await this.dbManager.deleteFile(wpsUuid);
// promptAction.showToast({ message: '下载失败,请重试', duration: 2000 });
// return false;
// }
// } catch (err) {
// // 异常处理
// await this.dbManager.deleteFile(wpsUuid);
// console.error(`下载失败: ${err.code}, ${err.message}`);
// promptAction.showToast({ message: '下载失败,请重试', duration: 2000 });
// return false;
// }
}
}
+32 -1
View File
@@ -12,6 +12,7 @@ import { deviceInfo } from '@kit.BasicServicesKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { rcp } from '@kit.RemoteCommunicationKit';
import { BasicConstant } from '../../../../Index';
import { connection } from '@kit.NetworkKit';
interface HdRequestOptions {
baseURL?: string
@@ -26,8 +27,14 @@ export interface HdResponse<T> {
}
export interface TimestampBean {
timestamp:string
}
export enum NetworkStatus {
type_default = 0,//无网络
type_wifi = 1,//wifi
type_traffic = 2,//蜂窝网络类型(2G/3G/4G/5G)
type_unknown = 3,//未知网络
type_error = -1,//失败
}
class HdHttp {
@@ -431,6 +438,30 @@ class HdHttp {
return result === 0 ? (a < b ? -1 : 1) : result;
});
}
getNetworkType():number {
try {
let netHandle = connection.getDefaultNetSync()
if (!netHandle || netHandle.netId === 0) {
console.info('当前网络状态:无网络')
return NetworkStatus.type_default;
}
let netCap = connection.getNetCapabilitiesSync(netHandle);
if (netCap.bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
console.info('当前网络状态:wifi')
return NetworkStatus.type_wifi;
} else if (netCap.bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
console.info('当前网络状态:2G/3G/4G/5G')
return NetworkStatus.type_traffic;
} else {
console.info('当前网络状态:未知网络')
return NetworkStatus.type_unknown;
}
} catch (error) {
console.error("Get net type failed, error: " + JSON.stringify(error));
return NetworkStatus.type_error;
}
}
}
interface DataBlob {
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB