迁移session和userinfo

This commit is contained in:
Cheliangzhao 2024-05-27 18:33:51 +08:00
parent 466d2fc789
commit cb90b7cee9
7 changed files with 325 additions and 79 deletions

View File

@ -1,32 +1,32 @@
{
"app": {
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"certpath": "C:\\Users\\wps\\.ohos\\config\\default_DataMigration_nuljKEfmRXHB8PLGEfuYN4YV1lGSGbA9v0wpbuwfmqk=.cer",
"storePassword": "0000001B5E9448F6AD9BADDA4C83D493A5E5F48217711679002A2C5CD29AC48C63630FE6471EB67921842A",
"keyAlias": "debugKey",
"keyPassword": "0000001B0EAED5D90BF4B127B068A11A8BABCB79E7B088CF931A3B521BC9C308CE7E18162699E1E1A760DC",
"profile": "C:\\Users\\wps\\.ohos\\config\\default_DataMigration_nuljKEfmRXHB8PLGEfuYN4YV1lGSGbA9v0wpbuwfmqk=.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "C:\\Users\\wps\\.ohos\\config\\default_DataMigration_nuljKEfmRXHB8PLGEfuYN4YV1lGSGbA9v0wpbuwfmqk=.p12"
}
}
],
"products": [
{
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "5.0.0(12)",
"runtimeOS": "HarmonyOS",
"runtimeOS": "HarmonyOS"
}
],
"buildModeSet": [
{
"name": "release"
}
],
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"certpath": "C:\\Users\\wps\\.ohos\\config\\default_data-migration_S_iyoHO2Pe6xZO7IdhIarmJTXCDCp-PziZ7vAZHB4rE=.cer",
"storePassword": "0000001B0DD271B6C65A9B107348E6ABEBD224452AC1A56999CE4DA489364C4F3E5325F88275CCC4B7BDFE",
"keyAlias": "debugKey",
"keyPassword": "0000001B0A39C50188A1F19EA6524A1E411EFA6758940BDC65B8DBD0365861504659BD66440F0CFAAB96AE",
"profile": "C:\\Users\\wps\\.ohos\\config\\default_data-migration_S_iyoHO2Pe6xZO7IdhIarmJTXCDCp-PziZ7vAZHB4rE=.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "C:\\Users\\wps\\.ohos\\config\\default_data-migration_S_iyoHO2Pe6xZO7IdhIarmJTXCDCp-PziZ7vAZHB4rE=.p12"
}
}
]
},
"modules": [

View File

@ -1,9 +1,9 @@
import { BackupExtensionAbility, BundleVersion, fileUri } from '@kit.CoreFileKit';
import fs from '@ohos.file.fs';
import { preferences, relationalStore } from '@kit.ArkData';
import { bundleManager } from '@kit.AbilityKit';
import { JSON, util } from '@kit.ArkTS';
import File from '@system.file';
import { UserDataTransfer } from './transfers/UserDataTransfer';
import { SettingDataTransfer } from './transfers/SettingDataTransfer';
import { SpTransferManager } from './SpTransferManager';
import { MmkvTransferManager } from './MmkvTransferManager';
const TAG = `BackupExtensionAbility1`
const BundleName = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT).name
@ -31,26 +31,36 @@ export default class BackupExtension extends BackupExtensionAbility {
}
async restoreData() {
const databasePath = `/data/storage/el2/backup/restore/${BundleName}` + '/ce/databases/qingsdk2.db'
console.log(TAG, '1' + fs.statSync(databasePath).size)
if (!fs.accessSync(DatabaseDir)) {
fs.mkdirSync(DatabaseDir, true)
}
console.log(TAG, '2' + fs.accessSync(DatabaseDir))
const dbFile = fs.openSync(databasePath, fs.OpenMode.READ_ONLY)
// fs.copyFileSync(dbFile.fd, DatabaseDir + '/qingsdk2.db')
// fs.closeSync(dbFile.fd)
console.log(TAG, '3' + fs.accessSync(DatabaseDir + '/qingsdk2.db'))
const rdbStore = await relationalStore.getRdbStore(this.context.getApplicationContext(), {
name: 'qingsdk2.db',
securityLevel: relationalStore.SecurityLevel.S1,
customDir: 'qingsdk2.db'
})
const pre = new relationalStore.RdbPredicates('roaming_list')
pre.equalTo('userid', '1510872069')
rdbStore.query(pre, (err, res) => {
console.log(TAG, JSON.stringify(res))
})
const context = this.context.getApplicationContext()
const mSpTransferManager = new SpTransferManager()
const mMmkvTransferManager = new MmkvTransferManager()
const mUserDataTransfer = new UserDataTransfer(context, mMmkvTransferManager, mSpTransferManager)
const mSettingDataTransfer = new SettingDataTransfer(context, mMmkvTransferManager, mSpTransferManager)
mUserDataTransfer.execute()
mSettingDataTransfer.execute()
// const databasePath = `/data/storage/el2/backup/restore/${BundleName}` + '/ce/databases/qingsdk2.db'
// console.log(TAG, '1' + fs.statSync(databasePath).size)
// if (!fs.accessSync(DatabaseDir)) {
// fs.mkdirSync(DatabaseDir, true)
// }
// console.log(TAG, '2' + fs.accessSync(DatabaseDir))
// const dbFile = fs.openSync(databasePath, fs.OpenMode.READ_ONLY)
// // fs.copyFileSync(dbFile.fd, DatabaseDir + '/qingsdk2.db')
// // fs.closeSync(dbFile.fd)
// console.log(TAG, '3' + fs.accessSync(DatabaseDir + '/qingsdk2.db'))
// const rdbStore = await relationalStore.getRdbStore(this.context.getApplicationContext(), {
// name: 'qingsdk2.db',
// securityLevel: relationalStore.SecurityLevel.S1,
// customDir: 'qingsdk2.db'
// })
// const pre = new relationalStore.RdbPredicates('roaming_list')
// pre.equalTo('userid', '1510872069')
// rdbStore.query(pre, (err, res) => {
// console.log(TAG, JSON.stringify(res))
// })
// const fileRoot = backupDir + '/A/data/.Cloud/cn/'
// const userIds = fs.listFileSync(fileRoot)
// const docDir = this.context.filesDir + '/document'

View File

@ -6,8 +6,10 @@ import { MMKV } from '@ohos/MMKV';
// mmkv值类型定义
type MmkvValueType = number | Set<string> | string | boolean;
export class MmkvTransferManager extends BaseTransferManager {
private TAG: string = "MmkvTransferManager";
private crpKey = 'DEj%^34g'
/**
* 移动双框架mmkv文件到单框架指定目录
* 文件参考@link FileTransferManager.moveFiles
@ -15,7 +17,7 @@ export class MmkvTransferManager extends BaseTransferManager {
* @param context context上下文
* @return 成功返回true, 否则false
*/
private async moveMmkvFile(context: common.Context): Promise<boolean> {
moveMmkvFile(context: common.Context) {
try {
// 双升单数据迁移后mmkv默认在files/mmkv目录下
let sourceMmkvPath: string = `/data/storage/el2/backup/restore/${this.BundleName}/ce/files/mmkv`;
@ -51,7 +53,6 @@ export class MmkvTransferManager extends BaseTransferManager {
}
}
/**
* 解析双框架mmkv文件
* 当前例子中:
@ -72,17 +73,19 @@ export class MmkvTransferManager extends BaseTransferManager {
// mmkv文件名
let mmapID: string = 'source_mmkv';
// 秘钥,根据实际情况指定秘钥, 当前未设置秘钥
let crpKey: string = '';
let crpKey: string = '44456a255e333467';
mmkv = MMKV.getBackedUpMMKVWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, '');
// 注当前mmkv对于双框架mmkv设置的int long float的类型暂不支持待后续更新
// dataMap.set('intData', mmkv.decodeNumber('intData', 0));
// dataMap.set('longData', mmkv.decodeNumber('longData', 0));
// dataMap.set('floatData', mmkv.decodeNumber('floatData', 0));
dataMap.set('doubleData', mmkv.decodeNumber('doubleData', 0));
dataMap.set('setData', mmkv.decodeSet('setData', new Set<string>()));
dataMap.set('boolData', mmkv.decodeBool('boolData', true));
dataMap.set('stringData', mmkv.decodeString('stringData', ''));
} catch (error) {
// 异常处理逻辑,自行实现
} finally {
@ -91,6 +94,21 @@ export class MmkvTransferManager extends BaseTransferManager {
return dataMap;
}
/**
* 解析双框架 单个mmkv文件
*/
parseSingleSourceMmkvInfo(context: common.Context, mmapID: string): MMKV {
let mmkv: MMKV;
const rootPath = `/data/storage/el2/backup/restore/${this.BundleName}/ce/files/mmkv`;
// const rootPath = context.filesDir + '/mmkv'
const cacheDir = context.cacheDir + '/mmkv'
MMKV.initialize(rootPath, cacheDir)
mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, this.crpKey)
return mmkv;
}
/**
* 插入数据到单的mmkv中
*
@ -106,7 +124,7 @@ export class MmkvTransferManager extends BaseTransferManager {
// mmkv文件名
let mmapID: string = 'target_mmkv';
// 秘钥,根据实际情况指定秘钥, 当前未设置秘钥
let crpKey: string = '';
let crpKey: string = '44456a255e333467';
mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, '');
// 设置请求参数中的值
@ -143,7 +161,7 @@ export class MmkvTransferManager extends BaseTransferManager {
// mmkv文件名
let mmapID: string = 'target_mmkv';
// 秘钥,根据实际情况指定秘钥, 当前未设置秘钥
let crpKey: string = '';
let crpKey: string = '44456a255e333467';
mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, '');
// 取值根据mmkv中实际存在的key来取值

View File

@ -21,6 +21,8 @@ interface XmlElement {
}
export class SpTransferManager extends BaseTransferManager {
private TAG: string = 'SpTransferManager';
/**
* 双框架sp文件移动到单框架
* 文件迁移参考@link FileTransferManager.moveFiles
@ -28,7 +30,7 @@ export class SpTransferManager extends BaseTransferManager {
* @param context context上下文
* @return 成功返回true, 否则false
*/
private moveSpFile(context: common.Context): boolean {
moveSpFile(context: common.Context): boolean {
try {
// 双升单数据迁移后sp文件默认在shared_prefs目录下
let sourceSpPath = `/data/storage/el2/backup/restore/${this.BundleName}/ce/shared_prefs`;
@ -60,7 +62,6 @@ export class SpTransferManager extends BaseTransferManager {
}
}
/**
* 读取双的sp xml文件的文本内容
* 当前例子中:
@ -115,6 +116,53 @@ export class SpTransferManager extends BaseTransferManager {
return xmlContent;
}
private getSourceXmlStrFromPath(context: common.Context, sourcePath: string): string {
let xmlContent: string = '';
try {
// 双sp经过moveSpFile后的文件路径
let sourceSpFilePath: string = `${sourcePath}`;
// 得到双sp xml文本内容
let fileStream = fs.createStreamSync(sourceSpFilePath, 'r');
if (!fileStream) {
return xmlContent;
}
let bufArray: ArrayBuffer[] = [];
let totalLength = 0;
const READ_DATA_SIZE = 1024 * 1024;
let buffer = new ArrayBuffer(READ_DATA_SIZE);
let len = fileStream.readSync(buffer);
while (len !== 0) {
totalLength += len;
if (len < READ_DATA_SIZE) {
buffer = buffer.slice(0, len);
bufArray.push(buffer);
break;
}
bufArray.push(buffer);
buffer = new ArrayBuffer(READ_DATA_SIZE);
len = fs.readSync(Number(fileStream), buffer);
}
let contentBuffer = new Uint8Array(totalLength);
let offset = 0;
bufArray.forEach(bufferArr => {
let uInt8Arr = new Uint8Array(bufferArr);
contentBuffer.set(uInt8Arr, offset);
offset += uInt8Arr.byteLength;
});
let textDecoder = util.TextDecoder.create('utf-8', {
ignoreBOM: false
})
xmlContent = textDecoder.decodeWithStream(new Uint8Array(contentBuffer), {
stream: false
});
} catch (error) {
// 异常处理
}
return xmlContent;
}
/**
* 转换双框架sp xml的文本内容为XmlElement对象
* 详情请参考https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/arkts-utils/xmlconversion.md
@ -226,34 +274,6 @@ export class SpTransferManager extends BaseTransferManager {
return dataMap;
}
/**
* 将数据插入到单的sp中
* 当前例子中单的sp文件名为target_sp.xml
*
* @param context context上下文
* @param dataMap 待插入的数据
* @return boolean 成功返回true, 否则false
*/
private async insertDataToTargetSp(context: Context, dataMap: Map<string,
preferences.ValueType>): Promise<boolean> {
try {
// 获取单框架sp (可以自行提供工具类获取sp对象用于初始化进行读、写)当前例子中单的sp名为
// target_sp.xml没有则创建以实际为准
let targetSp = await preferences.getPreferences(context, 'target_sp.xml');
dataMap.forEach((value, key) => {
targetSp.put(key, value);
})
// 插入sp后需要调用flush保存到文件
targetSp.flush(() => {
// flush 成功的回调逻辑,自行实现
});
return true;
} catch (error) {
// 异常处理
return false;
}
}
/**
* 从单的sp中读取数据
@ -304,4 +324,42 @@ export class SpTransferManager extends BaseTransferManager {
const androidId = preMap.get(key) as string
return androidId
}
getDataFromSourceSp(context: Context, spName: string) {
const sourceSpPath = `/data/storage/el2/backup/restore/${this.BundleName}/ce/shared_prefs`;
const spPath = `${sourceSpPath}/${spName}`
const spContent = this.getSourceXmlStrFromPath(context, spPath)
console.log(this.TAG, 'spContent: ' + spContent)
const spXmlElement = this.transferSourceXmlToXmlElement(spContent)
return this.parseXmlElement(spXmlElement)
}
/**
* 将数据插入到单的sp中
* 当前例子中单的sp文件名为target_sp.xml
*
* @param context context上下文
* @param dataMap 待插入的数据
* @return boolean 成功返回true, 否则false
*/
insertDataToTargetSp(context: Context, spName: string, dataMap: Map<string, preferences.ValueType>) {
try {
// 获取单框架sp (可以自行提供工具类获取sp对象用于初始化进行读、写)当前例子中单的sp名为
// target_sp.xml没有则创建以实际为准
const targetSp = preferences.getPreferencesSync(context, { name: spName });
dataMap.forEach((value, key) => {
targetSp.putSync(key, value);
})
// 插入sp后需要调用flush保存到文件
targetSp.flush(() => {
// flush 成功的回调逻辑,自行实现
});
return true;
} catch (error) {
// 异常处理
return false;
}
}
}

View File

@ -0,0 +1,38 @@
import { MmkvTransferManager } from '../MmkvTransferManager'
import { common } from '@kit.AbilityKit'
import { SpTransferManager } from '../SpTransferManager'
export class SettingDataTransfer {
private TAG = 'SettingDataTransfer'
private mMmkvTransferManager: MmkvTransferManager
private mSpTransferManager: SpTransferManager
private context: common.Context
constructor(context: common.Context, mMmkvTransferManager: MmkvTransferManager,
mSpTransferManager: SpTransferManager) {
this.context = context
this.mSpTransferManager = mSpTransferManager
this.mMmkvTransferManager = mMmkvTransferManager
}
execute() {
const mmkv_public_default = this.mMmkvTransferManager.parseSingleSourceMmkvInfo(this.context, 'public_default')
const fileradar_auto_upload_only_wifi_USERID = mmkv_public_default?.decodeBool('fileradar_auto_upload_only_wifi_USERID', false)
const sp_auto_upload_switch = this.mSpTransferManager.getDataFromSourceSp(this.context, 'auto_upload_switch.xml')
// write local preferences
const spMap = new Map<string, boolean>()
for (const key of sp_auto_upload_switch.keys()) {
const userId = key
const auto_upload_switch: boolean = sp_auto_upload_switch.get(userId) as boolean
spMap.set(`${userId}/docSync`, auto_upload_switch)
spMap.set(`${userId}/uploadOnlyWlan`, fileradar_auto_upload_only_wifi_USERID)
}
this.mSpTransferManager.insertDataToTargetSp(this.context, 'setting', spMap)
mmkv_public_default?.close()
}
}

View File

@ -0,0 +1,122 @@
import { common } from '@kit.AbilityKit'
import { MmkvTransferManager } from '../MmkvTransferManager'
import { SpTransferManager } from '../SpTransferManager'
import { preferences } from '@kit.ArkData'
import { buffer } from '@kit.ArkTS'
export class UserDataTransfer {
private TAG = 'UserDataTransfer'
private mMmkvTransferManager: MmkvTransferManager
private mSpTransferManager: SpTransferManager
private context: common.Context
constructor(context: common.Context, mMmkvTransferManager: MmkvTransferManager,
mSpTransferManager: SpTransferManager) {
this.context = context
this.mMmkvTransferManager = mMmkvTransferManager
this.mSpTransferManager = mSpTransferManager
}
execute() {
// 读取mmkv
const mmkv_login_config = this.mMmkvTransferManager.parseSingleSourceMmkvInfo(this.context, 'login_config')
console.log(this.TAG, 'keys ' + mmkv_login_config.getAllKeys())
const CLOUD_QING_USER_WORKSPACE = mmkv_login_config.decodeString('CLOUD_QING_USER_WORKSPACE', '')
const CLOUD_QING_USER_ID = mmkv_login_config.decodeString('CLOUD_QING_USER_ID', '')
const CLOUD_QING_USER_WORKSPACE_INFO_xxxx = mmkv_login_config.decodeString('CLOUD_QING_USER_WORKSPACE_INFO_41000207', '') // company id
const CLOUD_QING_SESSION = mmkv_login_config.decodeString('CLOUD_QING_SESSION', '')
const CLOUD_QING_ACCOUNT_TYPE = mmkv_login_config.decodeString('CLOUD_QING_ACCOUNT_TYPE', '')
const CLOUD_QING_WPS_USERINFO = mmkv_login_config.decodeString('CLOUD_QING_WPS_USERINFO', '')
const CLOUD_LOGIN_USERS = mmkv_login_config.decodeString('CLOUD_LOGIN_USERS', '')
const CLOUD_QING_MULTI_ACCOUNT = mmkv_login_config.decodeString('CLOUD_QING_MULTI_ACCOUNT', '')
const user_name = mmkv_login_config.decodeString('user_name', '')
const user_source_login_type = mmkv_login_config.decodeString('user_source_login_type', '')
const user_id = mmkv_login_config.decodeString('user_id', '')
const user_login_type = mmkv_login_config.decodeString('user_login_type', '')
const user_wpssids = mmkv_login_config.decodeString('user_wpssids', '')
const user_wpssid = mmkv_login_config.decodeString('user_wpssid', '')
const user_avatar_url = mmkv_login_config.decodeString('user_avatar_url', '')
const not_account_request_time = mmkv_login_config.decodeString('not_account_request_time', '')
const not_account_request_num = mmkv_login_config.decodeString('not_account_request_num', '')
const login_limit_account = mmkv_login_config.decodeNumber('login_limit_account', 1)
mmkv_login_config.close()
// process data
// get session
const sessionRow = CLOUD_QING_SESSION
const sessionJsonStr = buffer.from(sessionRow, "base64").toString("utf-8");
const sessionJsonObj: Record<string, string | number | boolean | object> = JSON.parse(sessionJsonStr)
const sessionStr = this.session2String(sessionJsonObj)
console.log(this.TAG, 'sessionStr ' + JSON.stringify(sessionStr))
// get userInfo
const loginUsers: Record<string, string | number | boolean>[] = JSON.parse(CLOUD_LOGIN_USERS)
const currentUser = loginUsers.find(user => user['is_current']) ?? {}
console.log(this.TAG, 'currentUser ' + JSON.stringify(currentUser))
const userInfo: Record<string, preferences.ValueType> = {
"account": "",
"account_num": 1,
"address": "",
"city": "",
"companyid": currentUser['company_id'],
"country": "",
"current_companyid": 0,
"departmentid": "",
"email": "",
"firstname": "",
"is_company_account": currentUser['is_company_account'],
"is_plus": true,
"lastname": "",
"loginmode": "phone_sms:+86***862",
"nickname": user_name,
"phonenumber": "130***862",
"pic": user_avatar_url,
"postal": "",
"province": "",
"regtime": 1566368398,
"result": "ok",
"role": ["user"],
"sex": "male",
"status": "active",
"userid": user_id,
"uzone": "CN-HN"
}
// write into preferences
const accountSpData: Map<string, preferences.ValueType> = new Map()
accountSpData.set('session', sessionStr)
accountSpData.set('userInfo', JSON.stringify(userInfo))
const authSpData = new Map<string, preferences.ValueType>()
authSpData.set('auth_policy', true)
this.mSpTransferManager.insertDataToTargetSp(this.context, 'account', accountSpData)
this.mSpTransferManager.insertDataToTargetSp(this.context, 'auth', authSpData)
}
private session2String(session: Record<string, string | number | boolean | object>) {
const authkeypair: Record<string, string> = session['authkeypair'] as Record<string, string>
session['accessid'] = authkeypair['access_id']
session['secretkey'] = authkeypair['secret_key']
session['authkeypair'] = ''
const strings: string[] = []
Object.getOwnPropertyNames(session).forEach(key => {
strings.push(`${key}=${session[key]}`)
})
return strings.join('; ')
}
private processUserWpssids(wpssids: string) {
const wpssidList = wpssids.trim().split('\n')
return wpssidList.join(',')
}
private processUserWpsid(wpssid: string) {
return wpssid.trim()
}
}

View File

@ -7,7 +7,7 @@
"author": "",
"license": "",
"dependencies": {
"@ohos/mmkv": "^2.0.3"
"@ohos/mmkv": "2.0.4-rc.4"
},
"devDependencies": {
"@ohos/hypium": "1.0.17",