chore: save current workspace changes
@@ -0,0 +1,21 @@
|
||||
import identifier from '@ohos.identifier.oaid';
|
||||
import hilog from '@ohos.hilog';
|
||||
import { BusinessError } from '@ohos.base';
|
||||
|
||||
export class DeviceInfoHolder {
|
||||
|
||||
init() {
|
||||
try {
|
||||
identifier.getOAID().then((data) => {
|
||||
const oaid: string = data;
|
||||
hilog.info(0x0000, 'mhy', '%{public}s', `get oaid by promise success, oaid: ${oaid}`);
|
||||
}).catch((err: BusinessError) => {
|
||||
hilog.info(0x0000, 'mhy', '%{public}s', `get oaid by promise failed, code: ${err.code}, message: ${err.message}`);
|
||||
})
|
||||
|
||||
} catch(err) {
|
||||
let e: BusinessError = err as BusinessError;
|
||||
console.error('createDeviceManager errCode:' + e.code + ',errMessage:' + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* 交易日志转换管理器
|
||||
* 负责将推送数据库中的数据转换到交易日志数据库
|
||||
*/
|
||||
|
||||
import { TradeDailyPushDataManager } from './TradeDailyPushDataManager';
|
||||
import { TradeDailyPushModel } from './TradeDailyPushModel';
|
||||
import { TradeDailyDataManager, TradeDailyModel, TradeMsgUniqueIdManager } from 'biz_trade';
|
||||
import { PushType } from 'biz_trade/src/main/ets/service/push/PushType';
|
||||
import { AppUtil } from '@pura/harmony-utils';
|
||||
import { HXLog } from 'biz_common';
|
||||
import { CodeTableManager } from 'lib_codetablemanager';
|
||||
import { AbilityConstant, ApplicationStateChangeCallback, common, Context } from '@kit.AbilityKit';
|
||||
|
||||
const TAG = 'TradeDailyConvertManager';
|
||||
|
||||
/**
|
||||
* 解析结果接口
|
||||
*/
|
||||
interface ParseResult {
|
||||
dailyTitle: string;
|
||||
dailyContent: string;
|
||||
dailyTime: string;
|
||||
}
|
||||
|
||||
export class TradeDailyConvertManager {
|
||||
private static instance: TradeDailyConvertManager;
|
||||
|
||||
private applicationStateChangeCallBack: ApplicationStateChangeCallback = {
|
||||
onApplicationForeground: (): void => {
|
||||
this.convertDailyFromPush();
|
||||
},
|
||||
onApplicationBackground: (): void => {
|
||||
}
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
static getInstance(): TradeDailyConvertManager {
|
||||
if (!TradeDailyConvertManager.instance) {
|
||||
TradeDailyConvertManager.instance = new TradeDailyConvertManager();
|
||||
}
|
||||
return TradeDailyConvertManager.instance;
|
||||
}
|
||||
|
||||
async registerForeground(context: Context): Promise<void> {
|
||||
context.getApplicationContext().on('applicationStateChange', this.applicationStateChangeCallBack)
|
||||
await this.convertDailyFromPush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用进入前台时转换推送数据
|
||||
* 将推送数据库中的数据转换到交易日志数据库
|
||||
*/
|
||||
async convertDailyFromPush(): Promise<void> {
|
||||
try {
|
||||
HXLog.i(TAG, '开始转换推送数据到交易日志');
|
||||
|
||||
// 查询所有推送日志
|
||||
const pushDailys = await TradeDailyPushDataManager.getInstance().queryDaily();
|
||||
|
||||
if (pushDailys.length === 0) {
|
||||
HXLog.i(TAG, '没有需要转换的推送数据');
|
||||
return;
|
||||
}
|
||||
|
||||
HXLog.i(TAG, `查询到 ${pushDailys.length} 条推送数据`);
|
||||
|
||||
// 转换每条推送日志
|
||||
for (const pushDaily of pushDailys) {
|
||||
// 过滤放弃行权的推送(单号不变,无法去重)
|
||||
if (this.shouldSkipPushDaily(pushDaily)) {
|
||||
HXLog.i(TAG, `跳过放弃行权推送: ${pushDaily.body}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await this.convertPushDailyToTradeDaily(pushDaily);
|
||||
}
|
||||
|
||||
// 删除已转换的推送日志
|
||||
await TradeDailyPushDataManager.getInstance().deleteDaily(pushDailys);
|
||||
HXLog.i(TAG, '推送数据转换完成');
|
||||
} catch (error) {
|
||||
HXLog.e(TAG, `转换推送数据失败: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否应该跳过该推送日志
|
||||
* @param pushDaily 推送日志
|
||||
*/
|
||||
private shouldSkipPushDaily(pushDaily: TradeDailyPushModel): boolean {
|
||||
const type = pushDaily.type
|
||||
|
||||
// 放弃行权push不记录(单号不变,无法去重)
|
||||
const abandonExercise = AppUtil.getContext().resourceManager.getStringSync($r('app.string.trade_daily_abandon_exercise'));
|
||||
if ((type == PushType.TY_OPTION_EXEC_WEITUO ||
|
||||
type == PushType.TY_OPTION_EXEC_CHEDAN) &&
|
||||
pushDaily.body.includes(abandonExercise)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将推送日志转换为交易日志
|
||||
* @param pushDaily 推送日志
|
||||
*/
|
||||
private async convertPushDailyToTradeDaily(pushDaily: TradeDailyPushModel): Promise<void> {
|
||||
try {
|
||||
const isSame = await TradeMsgUniqueIdManager.getInstance().isSameIdentify(
|
||||
pushDaily.time,
|
||||
pushDaily.iconType,
|
||||
pushDaily.type,
|
||||
pushDaily.orderNo
|
||||
);
|
||||
|
||||
if (isSame) {
|
||||
HXLog.i(TAG, `推送已存在,跳过: ${pushDaily.tradeDate} ${pushDaily.time}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const body = pushDaily.body;
|
||||
const title = pushDaily.title;
|
||||
|
||||
// 解析推送内容
|
||||
const parseResult = await this.parsePushContent(body, title, pushDaily);
|
||||
const dailyTitle = parseResult.dailyTitle;
|
||||
const dailyContent = parseResult.dailyContent;
|
||||
const dailyTime = parseResult.dailyTime;
|
||||
|
||||
// 创建交易日志模型
|
||||
const dailyModel = TradeDailyModel.createWithDateTime(
|
||||
pushDaily.tradeDate,
|
||||
dailyTime,
|
||||
dailyTitle,
|
||||
dailyContent
|
||||
);
|
||||
|
||||
// 插入到所有匹配的账户表中
|
||||
await this.insertToMatchingAccounts(dailyModel, pushDaily.tradeNo, pushDaily.qsid);
|
||||
|
||||
HXLog.i(TAG, `转换推送日志成功: ${pushDaily.tradeDate} ${dailyTime} ${dailyTitle}`);
|
||||
} catch (error) {
|
||||
HXLog.e(TAG, `转换推送日志失败: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析推送内容
|
||||
* @param body 推送内容
|
||||
* @param title 推送标题
|
||||
* @param pushDaily 推送日志
|
||||
*/
|
||||
private async parsePushContent(body: string, title: string, pushDaily: TradeDailyPushModel): Promise<ParseResult> {
|
||||
let dailyTitle = '';
|
||||
let dailyContent = '';
|
||||
let dailyTime = pushDaily.tradeTime;
|
||||
|
||||
const iconType = pushDaily.iconType;
|
||||
const orderNo = this.getOrderNo(pushDaily);
|
||||
const orderNoPrefix = AppUtil.getContext().resourceManager.getStringSync($r('app.string.trade_daily_order_no_prefix_with_comma'));
|
||||
const transactionReport = AppUtil.getContext().resourceManager.getStringSync($r('app.string.trade_daily_transaction_report'));
|
||||
|
||||
// 错误类型处理(iconType == 3 表示错误)
|
||||
if (iconType == '3') {
|
||||
dailyContent = body;
|
||||
dailyTime = pushDaily.tradeTime;
|
||||
|
||||
// 提取时间 [15:01:30]
|
||||
const firstIndex = body.indexOf('[');
|
||||
const lastIndex = body.indexOf(']');
|
||||
if (firstIndex > -1 && lastIndex > -1) {
|
||||
const timeStr = body.substring(firstIndex + 1, lastIndex);
|
||||
dailyTime = timeStr.replace(/:/g, '');
|
||||
// 从 body 末��减去时间部分(包括括号)
|
||||
dailyContent = body.substring(0, body.length - timeStr.length - 4);
|
||||
}
|
||||
|
||||
if (orderNo.length > 0) {
|
||||
dailyContent += orderNoPrefix + orderNo;
|
||||
}
|
||||
} else {
|
||||
// 正常类型处理
|
||||
const firstIndex = body.indexOf('(');
|
||||
const lastIndex = body.indexOf(')');
|
||||
|
||||
if (firstIndex > -1 && lastIndex > -1) {
|
||||
// 标题处理
|
||||
if (!title.includes(transactionReport)) {
|
||||
dailyTitle += title;
|
||||
}
|
||||
dailyTitle += body.substring(0, firstIndex);
|
||||
|
||||
// 内容处理
|
||||
const alertContent = body.substring(firstIndex + 1, lastIndex);
|
||||
const alertContentArr = alertContent.split(',');
|
||||
|
||||
if (alertContentArr.length > 0) {
|
||||
const tradeCode = alertContentArr[0];
|
||||
const stockInfo = await CodeTableManager.get().getStockInfoByTradeCode(tradeCode);
|
||||
const stockName = stockInfo?.stockName;
|
||||
|
||||
if (stockName && stockName.length > 0) {
|
||||
// 将合约代码替换为合约名
|
||||
dailyContent += stockName;
|
||||
for (let i = 1; i < alertContentArr.length; i++) {
|
||||
dailyContent += ',' + alertContentArr[i];
|
||||
}
|
||||
} else {
|
||||
dailyContent = alertContent;
|
||||
}
|
||||
} else {
|
||||
dailyContent = alertContent;
|
||||
}
|
||||
|
||||
if (orderNo.length > 0) {
|
||||
dailyContent += orderNoPrefix + orderNo;
|
||||
}
|
||||
|
||||
// 时间处理
|
||||
// iOS: timeFromBody = body.substring(loc: last + 2, len: body.count - last - 3)
|
||||
dailyTime = pushDaily.tradeTime;
|
||||
if (dailyTime.length === 0 && lastIndex + 2 < body.length && body.length - lastIndex - 3 > 0) {
|
||||
const startPos = lastIndex + 2;
|
||||
const length = body.length - lastIndex - 3;
|
||||
dailyTime = body.substring(startPos, startPos + length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 移除时间中的冒号
|
||||
dailyTime = dailyTime.replace(/:/g, '');
|
||||
|
||||
return { dailyTitle, dailyContent, dailyTime };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单号
|
||||
* @param pushDaily 推送日志
|
||||
*/
|
||||
private getOrderNo(pushDaily: TradeDailyPushModel): string {
|
||||
const type = pushDaily.type;
|
||||
let orderNo = '';
|
||||
|
||||
// 期货成交2,期权成交8,需要用orderSysId
|
||||
if (type == PushType.TY_JIAOYI_CHENGGONG || type == PushType.TY_OPTION_CHEGNJIAO) {
|
||||
orderNo = pushDaily.orderSysId || pushDaily.orderNo;
|
||||
}
|
||||
// 条件单触发,preOrderNo有值则是附加条件单,用母单编号
|
||||
else if (type == PushType.TY_CONDITION && pushDaily.preOrderNo) {
|
||||
orderNo = pushDaily.preOrderNo;
|
||||
}
|
||||
else {
|
||||
orderNo = pushDaily.orderNo;
|
||||
}
|
||||
|
||||
// 移除空格
|
||||
return orderNo.replace(/\s/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入到所有匹配的账户表中
|
||||
* @param model 交易日志模型
|
||||
* @param accountId 账户ID
|
||||
* @param qsId 券商ID
|
||||
*/
|
||||
private async insertToMatchingAccounts(
|
||||
model: TradeDailyModel,
|
||||
accountId: string,
|
||||
qsId: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
// 获取所有表名
|
||||
const tableNames = await TradeDailyDataManager.getInstance().getAllTableNames();
|
||||
|
||||
// 匹配 trade_daily_{accountId}_{qsId}_{wtId} 格式
|
||||
const prefix = `trade_daily_${accountId}_${qsId}_`;
|
||||
|
||||
for (const tableName of tableNames) {
|
||||
if (tableName.startsWith(prefix)) {
|
||||
const wtId = tableName.substring(prefix.length);
|
||||
await TradeDailyDataManager.getInstance().insertDailyToAccount(
|
||||
model,
|
||||
accountId,
|
||||
qsId,
|
||||
wtId
|
||||
);
|
||||
HXLog.i(TAG, `插入到账户表: ${tableName}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
HXLog.e(TAG, `插入到匹配账户失败: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* 交易推送日志数据管理器
|
||||
*/
|
||||
|
||||
import relationalStore from '@ohos.data.relationalStore';
|
||||
import { TradeDailyPushModel } from './TradeDailyPushModel';
|
||||
import { BusinessError } from '@ohos.base';
|
||||
import hilog from '@ohos.hilog';
|
||||
|
||||
const DB_NAME = 'trade_push_daily.db';
|
||||
const TAG = 'TradeDailyPushDataManager'
|
||||
const DOMAIN = 0xFF01
|
||||
const FORMAT = '%{public}s'
|
||||
|
||||
export class TradeDailyPushDataManager {
|
||||
private static instance: TradeDailyPushDataManager;
|
||||
private rdbStore: relationalStore.RdbStore | null = null;
|
||||
private isInitialized: boolean = false;
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
static getInstance(): TradeDailyPushDataManager {
|
||||
if (!TradeDailyPushDataManager.instance) {
|
||||
TradeDailyPushDataManager.instance = new TradeDailyPushDataManager();
|
||||
}
|
||||
return TradeDailyPushDataManager.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据库
|
||||
*/
|
||||
async initDB(context: ESObject): Promise<void> {
|
||||
if (this.isInitialized && this.rdbStore) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const config: relationalStore.StoreConfig = {
|
||||
name: DB_NAME,
|
||||
securityLevel: relationalStore.SecurityLevel.S1,
|
||||
customDir: 'trade'
|
||||
};
|
||||
|
||||
this.rdbStore = await relationalStore.getRdbStore(context, config);
|
||||
await this.createTable();
|
||||
this.isInitialized = true;
|
||||
} catch (err) {
|
||||
const error = err as BusinessError;
|
||||
hilog.error(DOMAIN, TAG, FORMAT, `初始化数据库失败: ${error.code}, ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
*/
|
||||
private async createTable(): Promise<void> {
|
||||
if (!this.rdbStore) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const sql = TradeDailyPushModel.getTableSQL();
|
||||
await this.rdbStore.executeSql(sql);
|
||||
} catch (err) {
|
||||
const error = err as BusinessError;
|
||||
hilog.error(DOMAIN, TAG, FORMAT, `创建表失败: ${error.code}, ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入数据
|
||||
*/
|
||||
async insertDaily(daily: TradeDailyPushModel): Promise<void> {
|
||||
if (!this.rdbStore) {
|
||||
hilog.error(DOMAIN, TAG, FORMAT, '数据库未初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const valueBucket = daily.toDbRecord();
|
||||
await this.rdbStore.insert(TradeDailyPushModel.getTableName(), valueBucket);
|
||||
hilog.info(DOMAIN, TAG, FORMAT, '插入数据成功')
|
||||
} catch (err) {
|
||||
const error = err as BusinessError;
|
||||
hilog.error(DOMAIN, TAG, FORMAT, `插入数据失败: ${error.code}, ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
*/
|
||||
async deleteDaily(models: TradeDailyPushModel[]): Promise<void> {
|
||||
if (!this.rdbStore) {
|
||||
hilog.error(DOMAIN, TAG, FORMAT, '数据库未初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const tableName = TradeDailyPushModel.getTableName();
|
||||
|
||||
for (const model of models) {
|
||||
if (!model.uniqueId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const predicates = new relationalStore.RdbPredicates(tableName);
|
||||
predicates.equalTo('uniqueId', model.uniqueId);
|
||||
await this.rdbStore.delete(predicates);
|
||||
}
|
||||
} catch (err) {
|
||||
const error = err as BusinessError;
|
||||
hilog.error(DOMAIN, TAG, FORMAT, `删除数据失败: ${error.code}, ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有数据
|
||||
*/
|
||||
async queryDaily(): Promise<TradeDailyPushModel[]> {
|
||||
if (!this.rdbStore) {
|
||||
hilog.error(DOMAIN, TAG, FORMAT, '数据库未初始化');
|
||||
return [];
|
||||
}
|
||||
|
||||
let resultSet: relationalStore.ResultSet | null = null;
|
||||
try {
|
||||
const tableName = TradeDailyPushModel.getTableName();
|
||||
const predicates = new relationalStore.RdbPredicates(tableName);
|
||||
resultSet = await this.rdbStore.query(predicates, TradeDailyPushModel.getColumns());
|
||||
|
||||
const models: TradeDailyPushModel[] = [];
|
||||
|
||||
if (resultSet.rowCount === 0) {
|
||||
return models;
|
||||
}
|
||||
|
||||
resultSet.goToFirstRow();
|
||||
do {
|
||||
const record: relationalStore.ValuesBucket = {};
|
||||
const columns = TradeDailyPushModel.getColumns();
|
||||
|
||||
for (const column of columns) {
|
||||
const columnIndex = resultSet.getColumnIndex(column);
|
||||
if (columnIndex >= 0) {
|
||||
record[column] = resultSet.getString(columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
const model = TradeDailyPushModel.fromDbRecord(record);
|
||||
models.push(model);
|
||||
} while (resultSet.goToNextRow());
|
||||
|
||||
return models;
|
||||
} catch (err) {
|
||||
const error = err as BusinessError;
|
||||
hilog.error(DOMAIN, TAG, FORMAT, `查询数据失败: ${error.code}, ${error.message}`);
|
||||
return [];
|
||||
} finally {
|
||||
resultSet?.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 pushInfo 转换为 TradeDailyPushModel
|
||||
* @param pushInfo 推送信息
|
||||
* @returns 转换后的模型,若转换失败则返回 null
|
||||
*/
|
||||
convertToTradeDailyPushModel(pushInfo: ESObject): TradeDailyPushModel | null {
|
||||
const body = pushInfo['DESC'] as string;
|
||||
const msg = pushInfo['APPMSG'] as Map<string, object>;
|
||||
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tradeNo = msg['tradeno'] as string;
|
||||
const qsid = msg['qsid'] as string;
|
||||
|
||||
if (!tradeNo || !qsid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const model = new TradeDailyPushModel();
|
||||
model.body = body ?? '';
|
||||
model.tradeNo = tradeNo;
|
||||
model.qsid = qsid;
|
||||
|
||||
const orderNo = msg['orderNumber'] as string;
|
||||
if (orderNo) {
|
||||
model.orderNo = orderNo;
|
||||
}
|
||||
|
||||
// 期货成交2,期权成交8,需要用orderSysId
|
||||
const orderSysId = msg['orderSysId'] as string;
|
||||
if (orderSysId) {
|
||||
model.orderSysId = orderSysId;
|
||||
}
|
||||
|
||||
// 条件单触发, preOrderNo有值则是附加条件单,用母单编号
|
||||
const preOrderNo = msg['preOrderNumber'] as string;
|
||||
if (preOrderNo) {
|
||||
model.preOrderNo = preOrderNo;
|
||||
}
|
||||
|
||||
const uniqueid = msg['uniqueid'] as string;
|
||||
if (uniqueid) {
|
||||
model.uniqueId = uniqueid;
|
||||
}
|
||||
|
||||
const tradeTime = msg['ST2'] as string;
|
||||
if (tradeTime) {
|
||||
model.tradeTime = tradeTime;
|
||||
model.time = tradeTime;
|
||||
}
|
||||
|
||||
// 日期用收到通知的日期
|
||||
const now = new Date();
|
||||
model.tradeDate = this.formatDate(now, 'yyyyMMdd');
|
||||
|
||||
const iconType = msg['IC'] as string;
|
||||
if (iconType) {
|
||||
model.iconType = iconType;
|
||||
}
|
||||
|
||||
const type = msg['TY'] as string;
|
||||
if (type) {
|
||||
model.type = type;
|
||||
// 期货撤单3、期权撤单9、期权行权撤单52,这几种后端给的委托时间,不对,需要用收到回报的时间
|
||||
const typeNum = parseInt(type);
|
||||
if (typeNum === 3 || typeNum === 9 || typeNum === 52) {
|
||||
model.tradeTime = this.formatDate(now, 'HH:mm:ss');
|
||||
// 推送中的已撤单给过滤了,和标题重复,产品不想要
|
||||
model.body = body.replace(',已撤单', '');
|
||||
}
|
||||
}
|
||||
|
||||
const title = msg['ST'] as string;
|
||||
if (title) {
|
||||
model.title = title;
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录推送日志
|
||||
*/
|
||||
async recordPushDaily(model: TradeDailyPushModel): Promise<void> {
|
||||
hilog.info(DOMAIN, TAG, FORMAT, `[daily] notification-extension push daily: ${model.tradeDate} ${model.time} ${model.title} ${model.body}`);
|
||||
|
||||
const type = parseInt(model.type);
|
||||
// 过滤条件单(5)和盈亏(6)类型
|
||||
if (type === 5 || type === 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.insertDaily(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
*/
|
||||
private formatDate(date: Date, format: string): string {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
if (format === 'yyyyMMdd') {
|
||||
return `${year}${month}${day}`;
|
||||
} else if (format === 'HH:mm:ss') {
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* 交易推送日报数据模型
|
||||
* Created by wenzhe on 2022/6/6.
|
||||
* Copyright © 2022 hexin. All rights reserved.
|
||||
*/
|
||||
|
||||
import relationalStore from '@ohos.data.relationalStore';
|
||||
|
||||
export class TradeDailyPushModel {
|
||||
/** 交易账号 */
|
||||
tradeNo: string = '';
|
||||
/** qsId */
|
||||
qsid: string = '';
|
||||
/** 订单号 */
|
||||
orderNo: string = '';
|
||||
/** 订单号(已成交) */
|
||||
orderSysId: string = '';
|
||||
/** 母单号(附加条件单) */
|
||||
preOrderNo: string = '';
|
||||
/** 消息唯一号 */
|
||||
uniqueId: string = '';
|
||||
/** 交易类型 */
|
||||
type: string = '';
|
||||
/** 交易时间(app显示) */
|
||||
tradeTime: string = '';
|
||||
/** 交易时间(后端推的时间) */
|
||||
time: string = '';
|
||||
/** 交易日期 */
|
||||
tradeDate: string = '';
|
||||
/** 标题 */
|
||||
title: string = '';
|
||||
/** 消息内容 */
|
||||
body: string = '';
|
||||
/** icon类型 */
|
||||
iconType: string = '';
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表名
|
||||
*/
|
||||
static getTableName(): string {
|
||||
return 'trade_push_daily';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表结构SQL
|
||||
*/
|
||||
static getTableSQL(): string {
|
||||
return `CREATE TABLE IF NOT EXISTS ${TradeDailyPushModel.getTableName()} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
body TEXT,
|
||||
tradeNo TEXT,
|
||||
qsid TEXT,
|
||||
orderNo TEXT,
|
||||
orderSysId TEXT,
|
||||
preOrderNo TEXT,
|
||||
uniqueId TEXT,
|
||||
type TEXT,
|
||||
tradeTime TEXT,
|
||||
time TEXT,
|
||||
tradeDate TEXT,
|
||||
title TEXT,
|
||||
iconType TEXT
|
||||
)`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有列名
|
||||
*/
|
||||
static getColumns(): string[] {
|
||||
return [
|
||||
'id',
|
||||
'body',
|
||||
'tradeNo',
|
||||
'qsid',
|
||||
'orderNo',
|
||||
'orderSysId',
|
||||
'preOrderNo',
|
||||
'uniqueId',
|
||||
'type',
|
||||
'tradeTime',
|
||||
'time',
|
||||
'tradeDate',
|
||||
'title',
|
||||
'iconType'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库记录转换为模型对象
|
||||
*/
|
||||
static fromDbRecord(record: relationalStore.ValuesBucket): TradeDailyPushModel {
|
||||
const model = new TradeDailyPushModel();
|
||||
model.body = (record['body'] as string) ?? '';
|
||||
model.tradeNo = (record['tradeNo'] as string) ?? '';
|
||||
model.qsid = (record['qsid'] as string) ?? '';
|
||||
model.orderNo = (record['orderNo'] as string) ?? '';
|
||||
model.orderSysId = (record['orderSysId'] as string) ?? '';
|
||||
model.preOrderNo = (record['preOrderNo'] as string) ?? '';
|
||||
model.uniqueId = (record['uniqueId'] as string) ?? '';
|
||||
model.type = (record['type'] as string) ?? '';
|
||||
model.tradeTime = (record['tradeTime'] as string) ?? '';
|
||||
model.time = (record['time'] as string) ?? '';
|
||||
model.tradeDate = (record['tradeDate'] as string) ?? '';
|
||||
model.title = (record['title'] as string) ?? '';
|
||||
model.iconType = (record['iconType'] as string) ?? '';
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为数据库记录
|
||||
*/
|
||||
toDbRecord(): relationalStore.ValuesBucket {
|
||||
const record: relationalStore.ValuesBucket = {};
|
||||
record['body'] = this.body;
|
||||
record['tradeNo'] = this.tradeNo;
|
||||
record['qsid'] = this.qsid;
|
||||
record['orderNo'] = this.orderNo;
|
||||
record['orderSysId'] = this.orderSysId;
|
||||
record['preOrderNo'] = this.preOrderNo;
|
||||
record['uniqueId'] = this.uniqueId;
|
||||
record['type'] = this.type;
|
||||
record['tradeTime'] = this.tradeTime;
|
||||
record['time'] = this.time;
|
||||
record['tradeDate'] = this.tradeDate;
|
||||
record['title'] = this.title;
|
||||
record['iconType'] = this.iconType;
|
||||
return record;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { HXLog, PreferenceService } from 'biz_common';
|
||||
import { JSBaseEvent } from 'biz_webview';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
|
||||
/**
|
||||
* 撤回同意隐私政策协议
|
||||
*/
|
||||
export class PrivacyWithDrawEvent extends JSBaseEvent {
|
||||
|
||||
getName(): string {
|
||||
return "privacyWithdraw";
|
||||
}
|
||||
|
||||
getHandler(): Function {
|
||||
return (data: object, handler: Function) => {
|
||||
//TODO这里提了ir单确认能否改成自定义弹窗,否则UI效果不统一
|
||||
AlertDialog.show(
|
||||
{
|
||||
title: '撤回同意将退出同花顺,我们无法再为您提供服务',
|
||||
//subtitle: 'subtitle',
|
||||
message: '',
|
||||
autoCancel: true,
|
||||
alignment: DialogAlignment.Center,
|
||||
gridCount: 4,
|
||||
showInSubWindow: true,
|
||||
isModal: true,
|
||||
offset: { dx: 0, dy: -20 },
|
||||
buttonDirection: DialogButtonDirection.HORIZONTAL,
|
||||
buttons: [
|
||||
{
|
||||
value: '撤回并退出',
|
||||
action: () => {
|
||||
PreferenceService.putVal('Privacy', 'isUserAgree', false, true);
|
||||
(getContext(this) as common.UIAbilityContext).terminateSelf(() => {});
|
||||
}
|
||||
},
|
||||
{
|
||||
value: '继续使用',
|
||||
action: () => {
|
||||
|
||||
}
|
||||
},
|
||||
],
|
||||
cancel: () => {
|
||||
console.info('Closed callbacks')
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dialogController: CustomDialogController = new CustomDialogController({
|
||||
builder: CustomDialogExample({
|
||||
cancel: ()=> { this.onCancel() },
|
||||
confirm: ()=> { this.onAccept() },
|
||||
}),
|
||||
})
|
||||
|
||||
onCancel() {
|
||||
console.info('Callback when the first button is clicked')
|
||||
}
|
||||
|
||||
onAccept() {
|
||||
console.info('Callback when the second button is clicked')
|
||||
}
|
||||
}
|
||||
|
||||
@CustomDialog
|
||||
struct CustomDialogExample {
|
||||
cancel?: () => void
|
||||
confirm?: () => void
|
||||
controller: CustomDialogController
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text('我是内容').fontSize(20).margin({ top: 10, bottom: 10 })
|
||||
Flex({ justifyContent: FlexAlign.SpaceAround }) {
|
||||
Button('cancel')
|
||||
.onClick(() => {
|
||||
this.controller.close()
|
||||
if (this.cancel) {
|
||||
this.cancel()
|
||||
}
|
||||
}).backgroundColor(0xffffff).fontColor(Color.Black)
|
||||
Button('confirm')
|
||||
.onClick(() => {
|
||||
this.controller.close()
|
||||
if (this.confirm) {
|
||||
this.confirm()
|
||||
}
|
||||
}).backgroundColor(0xffffff).fontColor(Color.Red)
|
||||
}.margin({ bottom: 10 })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import BackupExtensionAbility, {BundleVersion} from '@ohos.application.BackupExtensionAbility';
|
||||
import MmkvTransferManager from './MMKVManager'
|
||||
|
||||
const TAG = `FileBackupExtensionAbility`;
|
||||
export default class BackupExtension extends BackupExtensionAbility {
|
||||
async onBackup () {
|
||||
console.info(TAG, `onBackup ok`);
|
||||
}
|
||||
|
||||
async onRestore (bundleVersion : BundleVersion) {
|
||||
console.info(TAG, `onRestore ok ${JSON.stringify(bundleVersion)}`);
|
||||
console.info(TAG, `onRestore end`);
|
||||
new MmkvTransferManager().transfer(this.context)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
import common from '@ohos.app.ability.common';
|
||||
import fs from '@ohos.file.fs';
|
||||
import { MMKV} from '@ohos/mmkv'
|
||||
import dataPreferences from '@ohos.data.preferences';
|
||||
|
||||
// mmkv值类型定义
|
||||
|
||||
type MmkvValueType = number | Set<string> | string | boolean;
|
||||
|
||||
/**
|
||||
* 双->单 mmkv文件迁移转换管理
|
||||
*
|
||||
* @since 2024-04-20
|
||||
*/
|
||||
export default class MmkvTransferManager {
|
||||
/**
|
||||
* 双->单 mmkv文件转换
|
||||
*当前例子中:
|
||||
*
|
||||
XXAJmmkvX177): source_mmkv source_
|
||||
mmkv. crc
|
||||
* 单的mmkv文件名为:target_
|
||||
mmkv
|
||||
*
|
||||
* @param context context上下文
|
||||
*/
|
||||
public async transfer(context: common.Context): Promise<void> {
|
||||
//第1步,move双框架mmkv文件到单框架指定目录
|
||||
let result = await this.moveMmkvFile(context);
|
||||
console.log("FileBackupExtensionAbility", "startMove")
|
||||
if (!result) {
|
||||
console.log("FileBackupExtensionAbility", "moveFailed")
|
||||
return;
|
||||
}
|
||||
// 第2步,解析双的mmkv的内容
|
||||
let dataMap = this.parseSourceMmkvInfo(context);
|
||||
console.log("FileBackupExtensionAbility", "dataMap" + JSON.stringify(dataMap.get("stringData")));
|
||||
// 第3步,将解析的双的mmkv中的内容保存到单的mmkv中
|
||||
if (!this.insertToTargetMmkv(context, dataMap)) {
|
||||
console.log("FileBackupExtensionAbility", "failed to insert target")
|
||||
// 数据插入失败的处理逻辑,自行实现
|
||||
return;
|
||||
}
|
||||
// 第4步,从单的mmkv中读取数据
|
||||
let resultMap = this.getDataFromTarget(context);
|
||||
let mmkvData = resultMap.get("legacyUserData")
|
||||
if (typeof mmkvData === 'string') {
|
||||
|
||||
// // 新建并打开文件
|
||||
// let file = fs.openSync(context.filesDir + '/userinfo.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
|
||||
// // 写入一段内容至文件
|
||||
// fs.writeSync(file.fd, mmkvData as string);
|
||||
|
||||
let options: dataPreferences.Options = { name: 'backupUserInfo' };
|
||||
let preferences = dataPreferences.getPreferencesSync(context.getApplicationContext(), options);
|
||||
preferences.putSync('data', mmkvData as string);
|
||||
preferences.flush()
|
||||
}
|
||||
// 处理查询结果,根据情况自行实现
|
||||
console.log("FileBackupExtensionAbility", "result" + JSON.stringify(resultMap.get("stringData")))
|
||||
}
|
||||
|
||||
//双mmkv文件搬迁
|
||||
/**
|
||||
* 移动双框架mmkv文件到单框架指定目录
|
||||
* 文件参考@link FileTransferManager.moveFiles
|
||||
*
|
||||
* @param context context上下文
|
||||
* @return 成功返回true, 否则false
|
||||
*/
|
||||
private async moveMmkvFile(context: common.Context): Promise<boolean> {
|
||||
try {
|
||||
if (fs.accessSync("/data/storage/el2/backup/restore/com.hexin.plat.android/ce/files/mmkv_migratable")) {
|
||||
fs.listFileSync("/data/storage/el2/backup/restore/com.hexin.plat.android/ce/files/mmkv_migratable", {
|
||||
recursion: true
|
||||
}).forEach(item => {
|
||||
console.log("FileBackupExtensionAbility", "name = " + item);
|
||||
})
|
||||
}
|
||||
// 双升单数据迁移后,mmkv默认在files/mmkv目录下
|
||||
let sourceMmkvPath: string = '/data/storage/el2/backup/restore/com.hexin.plat.android/ce/files/mmkv_migratable';
|
||||
if (!fs.accessSync(sourceMmkvPath)) {
|
||||
console.log("FileBackupExtensionAbility", "fsaccess failed")
|
||||
return false;
|
||||
}
|
||||
// 得到源路径下面所有的文件和目录
|
||||
let listFileNames: string[] = fs.listFileSync(sourceMmkvPath);
|
||||
if (!listFileNames || listFileNames.length === 0) {
|
||||
console.log("FileBackupExtensionAbility", "listFile empty")
|
||||
return false;
|
||||
}
|
||||
|
||||
// 单mmkv路径, 根据实际路径指定
|
||||
let targetPath: string = context.filesDir + '/mmkv';
|
||||
// 目标目录不存在,则需要创建
|
||||
if (!fs.accessSync(targetPath)) {
|
||||
fs.mkdirSync(targetPath);
|
||||
}
|
||||
|
||||
// 遍历源路径下面的文件或目录,进行移动
|
||||
listFileNames.forEach((fileName: string) => {
|
||||
let srcPath: string = `${sourceMmkvPath}/${fileName}`;
|
||||
let destPath: string = `${targetPath}/${fileName}`;
|
||||
if (fs.statSync(srcPath).isFile()) {
|
||||
fs.moveFileSync(srcPath, destPath);
|
||||
} else {
|
||||
fs.moveDirSync(srcPath, targetPath);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
// 异常处理逻辑,自行实现
|
||||
console.log("FileBackupExtensionAbility", "catch error")
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析双框架mmkv文件
|
||||
* 当前例子中:
|
||||
* 双的mmkv文件名为:source_mmkv, source_mmkv.crc
|
||||
* 其中设置的属性有:intData 整型, boolData bool型, stringData 字符串型,floatData 浮点型,doubleData 双精度浮点型, setData Set<string>类型
|
||||
* 单的mmkv文件名为:target_mmkv
|
||||
*
|
||||
* @param context context上下文
|
||||
* @return map 包含了双mmkv中的数据
|
||||
*/
|
||||
private parseSourceMmkvInfo(context: common.Context): Map<string, MmkvValueType> {
|
||||
let dataMap = new Map<string, MmkvValueType>();
|
||||
let mmkv: MMKV | null = null;
|
||||
try {
|
||||
// 初始化双的mmkv, mmkv文件移动到context.filesDir + '/mmkv' 目录下了
|
||||
MMKV.initialize(context.filesDir + '/mmkv', context.cacheDir + '/mmkv');
|
||||
// mmkv文件名
|
||||
let files = this.findDeseriredFile(context);
|
||||
let mmapID: string = files[0];
|
||||
console.log("FileBackupExtensionAbility", "mmapId = " + mmapID);
|
||||
// 秘钥,根据实际情况指定秘钥, 当前未设置秘钥
|
||||
let crpKey: string = '';
|
||||
mmkv = MMKV.getBackedUpMMKVWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, '');
|
||||
// 注:当前mmkv对于双框架mmkv设置的int long float的类型暂不支持,待后续更新
|
||||
dataMap.set('stringData', mmkv.decodeString('key_history_user_info', ''));
|
||||
dataMap.forEach((value, key, map) => {
|
||||
console.log("FileBackupExtensionAbility", "datamap key = " + key + "value = " + value.toString());
|
||||
})
|
||||
} catch (error) {
|
||||
// 异常处理逻辑,自行实现
|
||||
} finally {
|
||||
mmkv?.close();
|
||||
}
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从mmkv_migratable目录中找到所有的文件名
|
||||
*/
|
||||
private findDeseriredFile(context: common.Context) : string[] {
|
||||
let result : string[] = [];
|
||||
let mmkvFilePath = context.filesDir + '/mmkv';
|
||||
let listFileNames: string[] = fs.listFileSync(mmkvFilePath);
|
||||
for (let index = 0; index < listFileNames.length; index++) {
|
||||
const element = listFileNames[index];
|
||||
console.log("FileBackupExtensionAbility", "mmkv files are = " + element)
|
||||
if (element.endsWith(".crc")) {
|
||||
const filePrefix = element.split(".crc")[0];
|
||||
result.push(filePrefix);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入数据到单的mmkv中
|
||||
*
|
||||
* @param context context上下文
|
||||
* @param dataMap 待插入的数据
|
||||
* @return 成功返回true, 否则false
|
||||
*/
|
||||
private insertToTargetMmkv(context: Context, dataMap: Map<string, MmkvValueType>): boolean {
|
||||
let mmkv: MMKV | null = null;
|
||||
try {
|
||||
// 初始化单框架mmkv, 获取mmkv的实例可以根据实际情况自行封装成工具类调用
|
||||
MMKV.initialize(context.filesDir + '/mmkv', context.cacheDir + '/mmkv');
|
||||
// mmkv文件名
|
||||
let mmapID: string = 'history_user_info';
|
||||
// 秘钥,根据实际情况指定秘钥, 当前未设置秘钥
|
||||
let crpKey: string = '';
|
||||
mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, '');
|
||||
// // 设置请求参数中的值
|
||||
mmkv.encodeString('history_user_info', dataMap.get('stringData') as string);
|
||||
} catch (error) {
|
||||
console.log("FileBackupExtensionAbility", "insertError" + JSON.stringify(error))
|
||||
// 异常处理逻辑,自行实现
|
||||
return false;
|
||||
} finally {
|
||||
mmkv?.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取单框架中mmkv中的数据
|
||||
*
|
||||
* @param context context上下文
|
||||
* @return map 包含单框架mmkv中的数据
|
||||
*/
|
||||
private getDataFromTarget(context: common.Context): Map<string, MmkvValueType> {
|
||||
let dataMap = new Map<string, MmkvValueType>();
|
||||
let mmkv: MMKV | null = null;
|
||||
try {
|
||||
// 初始化单框架mmkv
|
||||
MMKV.initialize(context.filesDir + '/mmkv', context.cacheDir + '/mmkv');
|
||||
// mmkv文件名
|
||||
let files = this.findDeseriredFile(context);
|
||||
let mmapID: string = "history_user_info";
|
||||
console.log("FileBackupExtensionAbility", "mmapId = " + mmapID);
|
||||
// 秘钥,根据实际情况指定秘钥, 当前未设置秘钥
|
||||
let crpKey: string = '';
|
||||
mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, '');
|
||||
|
||||
// 取值,根据mmkv中实际存在的key来取值
|
||||
dataMap.set('legacyUserData', mmkv.decodeString('history_user_info', ''));
|
||||
} catch (error) {
|
||||
// 异常处理逻辑,自行实现
|
||||
} finally {
|
||||
mmkv?.close();
|
||||
}
|
||||
return dataMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
export const DEFAULT_CONNECT_SITE = '116.63.213.206';
|
||||
export const DEFAULT_CONNECT_SITE_PORT = 9528;
|
||||
|
||||
export const TEST_ENV_CONNECT_SITE = '121.52.252.9';
|
||||
export const TEST_ENV_CONNECT_SITE_PORT = 9628;
|
||||
|
||||
export const TEST_ENV_SITE = '101.71.41.211';
|
||||
export const TEST_ENV_PORT = 9628;
|
||||
@@ -0,0 +1,63 @@
|
||||
import { ConnectionServiceHelper, SiteEntity, SiteManager } from "@kernel/lib_communication";
|
||||
import { AppInfoConfig, PreferenceService } from "biz_common";
|
||||
import { DebugToolUtil } from "biz_debug";
|
||||
import { DEBUG_TOOL, ENABLE_FIX_SERVER,
|
||||
SERVER_FIX_ADDRESS,
|
||||
SERVER_FIX_PORT} from "biz_debug/src/main/ets/debug/servertool/constant";
|
||||
import WTInit from "biz_trade/src/main/ets/utils/WTInit";
|
||||
import BuildProfile from "BuildProfile";
|
||||
import { ConfigInitializer } from "../initialize/ConfigInitializor";
|
||||
import { TEST_ENV_PORT, TEST_ENV_SITE } from "./Config";
|
||||
import { ServiceHandlerImpl } from "./impl/ServiceHandlerImpl";
|
||||
import deviceInfo from "@ohos.deviceInfo";
|
||||
|
||||
export class ConnectionInitiator {
|
||||
|
||||
/**
|
||||
* 初始化连接库相关配置
|
||||
*/
|
||||
static initConnectionService(context : Context) {
|
||||
ConfigInitializer.init();
|
||||
|
||||
// SiteManager.getInstance().init(TEST_ENV_CONNECT_SITE, TEST_ENV_CONNECT_SITE_PORT)
|
||||
//SiteManager.getInstance().init(DEFAULT_CONNECT_SITE, DEFAULT_CONNECT_SITE_PORT)
|
||||
//SiteManager.getInstance().setDomainHost(AppInfoConfig.FUTURES_DOMAIN_HOST);
|
||||
|
||||
const initEntity = SiteManager.getInstance().getHQSiteEntity()
|
||||
initEntity?.setDomainHost(AppInfoConfig.FUTURES_DOMAIN_HOST)
|
||||
initEntity?.setSitePort(AppInfoConfig.FUTURES_HOST_PORT)
|
||||
if (DebugToolUtil.isInnerNetSimulateMode()) {
|
||||
// debug模拟器自动切内网环境
|
||||
ConnectionInitiator.updateServerOnDebugMode();
|
||||
}
|
||||
ConnectionInitiator.updateServerByDebugTool();
|
||||
ConnectionServiceHelper.getInstance().init(new ServiceHandlerImpl());
|
||||
WTInit.init(context.getApplicationContext());
|
||||
|
||||
}
|
||||
|
||||
private static updateServerOnDebugMode() {
|
||||
if (isDebugMode()) {
|
||||
let siteEntity = new SiteEntity();
|
||||
siteEntity.init(TEST_ENV_SITE, TEST_ENV_PORT);
|
||||
SiteManager.getInstance().updateHQSiteEntity(siteEntity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据调试工具保存的站点信息初始化行情站点
|
||||
*/
|
||||
private static updateServerByDebugTool() {
|
||||
if (PreferenceService.getBooleanValueSync(DEBUG_TOOL, ENABLE_FIX_SERVER, false)) {
|
||||
let siteEntity = new SiteEntity();
|
||||
const siteIp = PreferenceService.getStringValueSync(DEBUG_TOOL, SERVER_FIX_ADDRESS, TEST_ENV_SITE)
|
||||
const sitePort = PreferenceService.getNumberValueSync(DEBUG_TOOL, SERVER_FIX_PORT, TEST_ENV_PORT);
|
||||
siteEntity.init(siteIp, sitePort);
|
||||
SiteManager.getInstance().updateHQSiteEntity(siteEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isDebugMode() {
|
||||
return BuildProfile.BUILD_MODE_NAME.toString() === 'debug'
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
import {
|
||||
CBASConnectionConfig,
|
||||
HqConnectionConfig,
|
||||
ITcpApmMonitorProxy,
|
||||
SocketConnection,
|
||||
TcpApmMonitorProxyHolder,
|
||||
TcpLogConnectionInfo
|
||||
} from '@kernel/lib_communication';
|
||||
import {
|
||||
IConnectionService,
|
||||
IServiceHandler,
|
||||
ServiceConfig
|
||||
} from '@kernel/lib_communication/src/main/ets/connection/IConnectionService';
|
||||
import hilog from '@ohos.hilog';
|
||||
// import TradeJsonStructObserver from 'biz_trade/src/main/ets/features/wtmodule/TradeJsonStructObserver'
|
||||
import { AuthJsonStructObserver } from '../interceptors/AuthJsonStructObserver';
|
||||
import { HXAuthManager } from 'biz_auth';
|
||||
import { RealDataStructObserver } from '../../groupsub/RealDataStructObserver';
|
||||
import { SelfCodeChangeObserver } from '../interceptors/SelfCodeChangeObserver';
|
||||
import { SidInstanceIdReceiver } from '../interceptors/SidInstanceIdReceiver';
|
||||
import { BaseTcpLogInfo, getTcpService, TCPConnectLogInfo } from '@kernel/app_monitor_tcp';
|
||||
import { DEFAULT_SITE } from '@kernel/lib_communication/src/main/ets/site/SiteConstants';
|
||||
import connection from '@ohos.net.connection';
|
||||
import { TradeELKLogStructObserver } from 'biz_trade';
|
||||
import { HXEventMonitor } from '@kernel/app_monitor_event';
|
||||
import { Action, HxCBASAgentProvider, logMapValue } from '@b2c-spi/hx_cbas_api';
|
||||
import { HXLog } from 'biz_common';
|
||||
|
||||
|
||||
export class ServiceHandlerImpl implements IServiceHandler {
|
||||
onConfig(serviceConfig: ServiceConfig): void {
|
||||
let hqConnectionConfig = new HqConnectionConfig();
|
||||
hqConnectionConfig.setAuth(new HXAuthManager());
|
||||
hqConnectionConfig.addStructReceiver(new SidInstanceIdReceiver());
|
||||
hqConnectionConfig.addStructReceiver(new AuthJsonStructObserver());
|
||||
// hqConnectionConfig.addStructReceiver(new TradeJsonStructObserver());
|
||||
hqConnectionConfig.addStructReceiver(new RealDataStructObserver());
|
||||
//hqConnectionConfig.addStructReceiver(new SelfCodeChangeObserver());
|
||||
serviceConfig.registerConnection(hqConnectionConfig);
|
||||
|
||||
let cbasConnectionConfig = new CBASConnectionConfig();
|
||||
cbasConnectionConfig.setAuth(new HXAuthManager());
|
||||
cbasConnectionConfig.addStructReceiver(new TradeELKLogStructObserver());
|
||||
serviceConfig.registerConnection(cbasConnectionConfig);
|
||||
|
||||
this.registerTcpApmProxy()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册tcp的APM监控代理
|
||||
*/
|
||||
registerTcpApmProxy() {
|
||||
TcpApmMonitorProxyHolder.get().tcpLogMonitorProxy = new HXTcpApmMonitorProxy()
|
||||
}
|
||||
|
||||
onPreInit(connService: IConnectionService): void {
|
||||
initCommonConfig()
|
||||
}
|
||||
|
||||
onInitEnd(connService: IConnectionService): void {
|
||||
hilog.info(0xff11, "ServiceHandlerImpl", "onInitEnd")
|
||||
}
|
||||
|
||||
onDestroy(connService: IConnectionService): void {
|
||||
hilog.info(0xff11, "ServiceHandlerImpl", "onDestroy")
|
||||
}
|
||||
}
|
||||
|
||||
function initCommonConfig() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* tcp APM性能监控代理类
|
||||
*/
|
||||
class HXTcpApmMonitorProxy implements ITcpApmMonitorProxy {
|
||||
private TCP_LOG_MONITOR_MODULE = 'tcp-log';
|
||||
|
||||
/**
|
||||
* 开始解析dns
|
||||
* @param domainName
|
||||
*/
|
||||
dnsStart(domainName: string): void {
|
||||
const logMap: Map<string, logMapValue> = new Map()
|
||||
logMap.set('result', domainName)
|
||||
HxCBASAgentProvider.getInstance().doEvent("mob_systemgeneral_dnsresolve_start", Action.SHOW, logMap)
|
||||
HXLog.d("HXDNS", `dnsStart,domainName=${domainName}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* dns解析结束
|
||||
*/
|
||||
dnsEnd(isSuccess: boolean, domainName: string, ipList: connection.NetAddress[] | null, cost: number,
|
||||
errorMsg: string): void {
|
||||
const logMap: Map<string, logMapValue> = new Map()
|
||||
logMap.set('type', isSuccess ? "1" : "0")
|
||||
//dns解析结果
|
||||
const result = `domainName=${domainName}^ipList=${this.convertNetAddressToIpListString(ipList)}^cost=${cost}^errorMsg=${errorMsg}`
|
||||
logMap.set('result', result)
|
||||
HxCBASAgentProvider.getInstance().doEvent("mob_systemgeneral_dnsresolve_result", Action.SHOW, logMap)
|
||||
HXLog.d("HXDNS", `dnsEnd,success=${isSuccess},result=${result}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换ip列表字符串格式
|
||||
*/
|
||||
private convertNetAddressToIpListString(ipList: connection.NetAddress[] | null): string {
|
||||
if (ipList == null || ipList.length == 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const ipArray = ipList.map((addr) => addr.address)
|
||||
//去重
|
||||
const uniqueIps = Array.from(new Set(ipArray))
|
||||
return uniqueIps.join(",")
|
||||
}
|
||||
|
||||
setSas(sas: string | null): void {
|
||||
getTcpService()?.setSas(sas)
|
||||
}
|
||||
|
||||
sendTcpConnectLog(info: TcpLogConnectionInfo): void {
|
||||
let cost = info.finishTime - info.startTime
|
||||
if (cost < 0) {
|
||||
cost = 0
|
||||
}
|
||||
//这里需要转换一下
|
||||
const logInfo = new TCPConnectLogInfo(
|
||||
cost, //连接耗时
|
||||
this.getTcpConnectResult(info.state, cost), //统一result字段
|
||||
this.getTcpConnectErrorCode(info.state, cost), //统一errorCode字段
|
||||
info.errorCode.toString(), //errorMsg
|
||||
0, //连接类型 目前没有竞速,默认0
|
||||
info.siteEntity.getSiteAddress(), //连接的服务器地址
|
||||
0, //网络栈,目前没有判断
|
||||
false, //是否ipv6
|
||||
false, //是否v6优先连接
|
||||
info.connectSeq, //站点类型
|
||||
0,
|
||||
info.siteEntity.getSiteAddress() === DEFAULT_SITE ? 1 : 0// 是否为默认站点
|
||||
)
|
||||
getTcpService()?.getTcpConnectLogMonitor()?.addUploadInfo(logInfo)
|
||||
}
|
||||
|
||||
getTcpConnectErrorCode(state: SocketConnection, cost: number): number | undefined {
|
||||
switch (state) {
|
||||
case SocketConnection.CONNECTED:
|
||||
return 4
|
||||
case SocketConnection.ERROR:
|
||||
if (cost > 10000) {
|
||||
return 10
|
||||
} else {
|
||||
return 8
|
||||
}
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
getTcpConnectResult(state: SocketConnection, cost: number): string | null | undefined {
|
||||
switch (state) {
|
||||
case SocketConnection.CONNECTED:
|
||||
return BaseTcpLogInfo.SUCCESS
|
||||
case SocketConnection.ERROR:
|
||||
if (cost > 10000) {
|
||||
return BaseTcpLogInfo.TIMEOUT
|
||||
} else {
|
||||
return BaseTcpLogInfo.FAIL
|
||||
}
|
||||
default:
|
||||
return BaseTcpLogInfo.FAIL
|
||||
}
|
||||
}
|
||||
|
||||
buildRequest(frameId: number, packageId: number, traceId: string | null,
|
||||
pageIdMappingInstanceId: Map<number, number> | null): void {
|
||||
getTcpService()?.getTcpReqMonitor()?.buildRequest(frameId, packageId, traceId, pageIdMappingInstanceId)
|
||||
}
|
||||
|
||||
addRequestToQueue(packageId: number): void {
|
||||
getTcpService()?.getTcpReqMonitor()?.addRequestToQueue(packageId)
|
||||
}
|
||||
|
||||
sendBegin(packageId: number): void {
|
||||
getTcpService()?.getTcpReqMonitor()?.sendBegin(packageId)
|
||||
}
|
||||
|
||||
sendEnd(packageId: number): void {
|
||||
getTcpService()?.getTcpReqMonitor()?.sendEnd(packageId)
|
||||
}
|
||||
|
||||
receiveResponse(packageId: number, instanceId: number, rst: number): void {
|
||||
getTcpService()?.getTcpReqMonitor()?.receiveResponse(packageId, instanceId, rst)
|
||||
}
|
||||
|
||||
addRspToQueue(packageId: number, instanceId: number): void {
|
||||
getTcpService()?.getTcpReqMonitor()?.addRspToQueue(packageId, instanceId)
|
||||
}
|
||||
|
||||
processReceiveData(packageId: number, instanceId: number): void {
|
||||
getTcpService()?.getTcpReqMonitor()?.processReceiveData(packageId, instanceId)
|
||||
}
|
||||
|
||||
sendErrorLog(tag: string, msg: string): void {
|
||||
//在这里将tcp的错误日志发送到apm
|
||||
HXEventMonitor.onEvent(this.TCP_LOG_MONITOR_MODULE, tag, Object({
|
||||
errMsg: msg
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { getConnectionManager, HXLog } from 'biz_common';
|
||||
import {
|
||||
IStructReceiver,
|
||||
ProtocolDef,
|
||||
StuffBaseStruct,
|
||||
StuffResourceStruct,
|
||||
ConnectionServiceHelper
|
||||
} from '@kernel/lib_communication';
|
||||
import { StringUtils } from 'hxutil';
|
||||
import { ConnectionSeq } from '@kernel/lib_communication';
|
||||
import { ServerResourceManager, ServerTimeProvider } from 'biz_hxservice';
|
||||
import { getTcpService } from '@kernel/app_monitor_tcp';
|
||||
import { APMHelper } from '../../monitor/APMHelper';
|
||||
|
||||
/**
|
||||
* 认证包拦截器
|
||||
* 这个拦截器用于接受认证后的session数据包以及事件包
|
||||
* @author menghuiyuan@myhexin.com
|
||||
*/
|
||||
export class AuthJsonStructObserver implements IStructReceiver {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
onIntercept(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
if (stuffBaseStruct == null) {
|
||||
return false;
|
||||
}
|
||||
const headId = stuffBaseStruct.packageId;
|
||||
if (headId == ProtocolDef.DATAID_NEW_REALTIME) {
|
||||
const dataType = stuffBaseStruct.headType & ProtocolDef.DATA_TYPE_MASK;
|
||||
if (dataType === ProtocolDef.DATA_TYPE_JSON) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
handleStruct(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
if (!(stuffBaseStruct instanceof StuffResourceStruct)) {
|
||||
return false;
|
||||
}
|
||||
let tmpBuffer = stuffBaseStruct.buffer;
|
||||
const decodedStr = StringUtils.decode(tmpBuffer, "GBK")
|
||||
|
||||
try {
|
||||
const parsedObj: AuthStruct = JSON.parse(decodedStr)
|
||||
ServerResourceManager.getInstance().parseResource(decodedStr);
|
||||
if (parsedObj.srvTime && (typeof parsedObj.srvTime === 'number')) {
|
||||
ServerTimeProvider.getInstance().initServiceTime(parseInt(parsedObj.srvTime) * 1000)
|
||||
}
|
||||
const hasSessionId = StringUtils.isNotEmpty(parsedObj.sessionId);
|
||||
//处理sessionId
|
||||
if (hasSessionId) {
|
||||
ConnectionServiceHelper.getInstance()
|
||||
.getConnectionService()
|
||||
.setSessionId(ConnectionSeq.HQ, parsedObj.sessionId)
|
||||
|
||||
//设置tcp监控的sas
|
||||
getTcpService()?.setSas(`${parsedObj.sessionId}_${getConnectionManager()?.getConnectedIp()}`)
|
||||
}
|
||||
const hasClientIP = StringUtils.isNotEmpty(parsedObj.clientIp);
|
||||
if (hasClientIP) {
|
||||
APMHelper.clientIp = parsedObj.clientIp
|
||||
AppStorage.setOrCreate('clientIp', parsedObj.clientIp)
|
||||
}
|
||||
if (hasSessionId || hasClientIP) {
|
||||
//WTLoginAdapterService.receiveSessionIdHandle()
|
||||
}
|
||||
//todo 处理账户事件,如强制下线等
|
||||
if (StringUtils.isNotEmpty(parsedObj.events)) {
|
||||
HXLog.i("AuthJSONStruct", "error parsing")
|
||||
}
|
||||
} catch (e) {
|
||||
HXLog.e("AuthJSONStruct", "Error parsing " + JSON.stringify(e))
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
getReceiverId(): string {
|
||||
return "AuthJsonStructObserver"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证包结构
|
||||
*/
|
||||
export class AuthStruct {
|
||||
srvTime: string = "";
|
||||
clientIp: string = "";
|
||||
clientPort: string = "";
|
||||
serverIp: string = "";
|
||||
releaseName: string = "";
|
||||
AuthCountryCodeMd5: string = "";
|
||||
events: string = "";
|
||||
sessionId: string = "";
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { StringUtils } from 'hxutil';
|
||||
import { IStructReceiver, ITCPClient, ProtocolDef, StuffBaseStruct, StuffResourceStruct } from '@kernel/lib_communication';
|
||||
import { SelfCodeEvent } from '../../selfcode/SelfCodeEvents';
|
||||
import { UgcClient } from '../../register/ugcregister/UgcClient';
|
||||
|
||||
/**
|
||||
* 自选股更新订阅拦截
|
||||
* @author menghuiyuan@myhexin.com
|
||||
*/
|
||||
export class SelfCodeChangeObserver implements IStructReceiver {
|
||||
|
||||
constructor() { }
|
||||
|
||||
onIntercept(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
if (stuffBaseStruct == null) {
|
||||
return false;
|
||||
}
|
||||
const headId = stuffBaseStruct.packageId;
|
||||
if (headId == ProtocolDef.DATAID_NEW_REALTIME) {
|
||||
const dataType = stuffBaseStruct.headType & ProtocolDef.DATA_TYPE_MASK;
|
||||
if (dataType === ProtocolDef.DATA_TYPE_JSON) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//自选股版本变更推送id
|
||||
private readonly UPDATE_SIG_VERSION = '00000009';
|
||||
//自选股更新强制推送id
|
||||
private readonly UPDATE_SIG_FORCE = '00000010';
|
||||
|
||||
handleStruct(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
if (!(stuffBaseStruct instanceof StuffResourceStruct)) {
|
||||
return false;
|
||||
}
|
||||
const resStruct = stuffBaseStruct as StuffResourceStruct;
|
||||
const tmpBuffer = resStruct.buffer;
|
||||
const decodedStr = StringUtils.decode(tmpBuffer, "GBK")
|
||||
try {
|
||||
const parsedObj : SelfCodeEvent = JSON.parse(decodedStr);
|
||||
if (parsedObj) {
|
||||
const eventArrayLen = parsedObj.events.length;
|
||||
for (let index = 0; index < eventArrayLen; index++) {
|
||||
const eventObj = parsedObj.events[index];
|
||||
const eventId = eventObj.eventID;
|
||||
switch (eventId) {
|
||||
//版本号推送
|
||||
case this.UPDATE_SIG_VERSION:
|
||||
const selfCodeVersionData = eventObj.eventData;
|
||||
//TODO 推送版本号:
|
||||
UgcClient.getInstance().onSelfCodeRemoteVersion(Number(selfCodeVersionData.version))
|
||||
return true;
|
||||
case this.UPDATE_SIG_FORCE:
|
||||
//TODO 推送强制更新版本
|
||||
const forceUpdateVersionData = JSON.stringify(eventObj.eventData);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getReceiverId(): string {
|
||||
return "SelfCodeChangeObserver";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
IStructReceiver,
|
||||
RequestQueueManagement,
|
||||
StuffBaseStruct,
|
||||
StuffResourceStruct
|
||||
} from '@kernel/lib_communication';
|
||||
import { SidPermissionRefreshClient } from 'biz_auth';
|
||||
|
||||
const USER_RESPONSE_TYPE_NOTHING = 0;
|
||||
|
||||
/**
|
||||
* SID 权限刷新特殊包接收器。
|
||||
*
|
||||
* 对齐 Android SIDInstanceIdReicever:主动刷新 SID 的回包使用固定 instanceId=13,
|
||||
* 服务端可能按 type=0 返回普通 body,连接库会先解成 StuffBaseStruct。
|
||||
* 这里只按包头条件识别该特殊包,把原始 body 包装成 LOGIN_AUTH 资源后继续投递给请求 client。
|
||||
*/
|
||||
export class SidInstanceIdReceiver implements IStructReceiver {
|
||||
onIntercept(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
return this.isSidRefreshBaseStruct(stuffBaseStruct);
|
||||
}
|
||||
|
||||
handleStruct(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
if (!this.isSidRefreshBaseStruct(stuffBaseStruct)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// IStructReceiver 返回 true 后会截断原始 StuffBaseStruct 的普通分发流程,
|
||||
// 这里需要把包装后的 LOGIN_AUTH 资源主动投递给注册在 REFRESH_SID 上的请求 client。
|
||||
const sidStruct = this.createSidResourceStruct(stuffBaseStruct);
|
||||
const clients = RequestQueueManagement.getInstance().getNetworkClient(SidPermissionRefreshClient.REFRESH_SID);
|
||||
if (!clients || clients.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const client of clients) {
|
||||
client?.receive(sidStruct);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
getReceiverId(): string {
|
||||
return 'SidInstanceIdReceiver';
|
||||
}
|
||||
|
||||
private isSidRefreshBaseStruct(stuffBaseStruct: StuffBaseStruct | null): boolean {
|
||||
if (!stuffBaseStruct || stuffBaseStruct instanceof StuffResourceStruct) {
|
||||
return false;
|
||||
}
|
||||
// 对齐 Android filter:只通过包头 type=0 和固定 instanceId=13 识别 SID 刷新特殊包。
|
||||
// SID 内容不参与判断,避免把权限字段格式差异误判为客户端解析问题。
|
||||
const rawBuffer = stuffBaseStruct.rawBuffer;
|
||||
return stuffBaseStruct.instanceId === SidPermissionRefreshClient.REFRESH_SID &&
|
||||
stuffBaseStruct.headType === USER_RESPONSE_TYPE_NOTHING &&
|
||||
rawBuffer !== null &&
|
||||
rawBuffer.length > 0;
|
||||
}
|
||||
|
||||
private createSidResourceStruct(stuffBaseStruct: StuffBaseStruct): StuffResourceStruct {
|
||||
// 这里不解码、不解析 SID,只复制主站原始 body 并包装成 LOGIN_AUTH。
|
||||
// 后续 SID 字符串解析和权限更新仍交给 SidPermissionRefreshClient 统一处理。
|
||||
const rawBuffer = stuffBaseStruct.rawBuffer as Uint8Array;
|
||||
const sidBuffer = rawBuffer.slice(0, rawBuffer.length);
|
||||
const sidStruct = new StuffResourceStruct(StuffResourceStruct.LOGIN_AUTH, sidBuffer);
|
||||
stuffBaseStruct.copyHead(sidStruct);
|
||||
sidStruct.connectionSeq = stuffBaseStruct.connectionSeq;
|
||||
sidStruct.rawBuffer = sidBuffer;
|
||||
return sidStruct;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { HXLog } from 'biz_common';
|
||||
import { ICheckCallback } from '@kernel/lib_communication/src/main/ets/connection/ICheckCallback';
|
||||
|
||||
/**
|
||||
* 连接库检查回调类
|
||||
* @author menghuiyuan@myhexin.com
|
||||
*/
|
||||
export class ConnectionCheckImpl implements ICheckCallback {
|
||||
|
||||
private readonly TAG = "SocketConnectionCheckImpl"
|
||||
onChannelOk(): void {
|
||||
HXLog.i(this.TAG, "onChannelOk");
|
||||
}
|
||||
|
||||
onChannelBad(badType: number): void {
|
||||
HXLog.i(this.TAG, "onChannelBad" + badType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要重新连接
|
||||
* @returns true,默认开启重连
|
||||
*/
|
||||
needReconnect(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { getConnectionManager } from 'biz_common';
|
||||
import { UgcClient } from '../../register/ugcregister/UgcClient';
|
||||
|
||||
/**
|
||||
* 主动关闭当前的socket连接
|
||||
*/
|
||||
export function closeSocketConnection() : void {
|
||||
if (getConnectionManager()) {
|
||||
getConnectionManager()?.disconnect(false);
|
||||
UgcClient.getInstance().onHXSocketClose()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { getConnectionManager, HXLog, RuntimeStatus } from 'biz_common';
|
||||
import { ConnectionCheckImpl } from './ConnectionCheckImpl';
|
||||
import { closeSocketConnection } from './ConnectionServiceUtil';
|
||||
|
||||
/**
|
||||
* socket的监控管理
|
||||
*/
|
||||
export class HXSocketMonitor {
|
||||
|
||||
private readonly TAG : string = "HXSocketMonitor";
|
||||
|
||||
//当前App是否处于前台
|
||||
private isAppForeground = false;
|
||||
|
||||
onAppForeground() {
|
||||
HXLog.i(this.TAG, "onAppForeground")
|
||||
this.isAppForeground = true;
|
||||
if (getConnectionManager()) {
|
||||
HXLog.i(this.TAG, "onAppForeground checkConnection")
|
||||
getConnectionManager()?.checkConnection(new ConnectionCheckImpl());
|
||||
}
|
||||
}
|
||||
|
||||
onAppBackground() {
|
||||
HXLog.i(this.TAG, "onAppBackground")
|
||||
this.isAppForeground = false;
|
||||
closeSocketConnection()
|
||||
RuntimeStatus.getInstance().setTcpDisConnected()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export class EntryConstant {
|
||||
//微信分享id
|
||||
static readonly WX_SHARE_ID = 'wx6ba37430b7916a86';
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
|
||||
import hilog from '@ohos.hilog';
|
||||
import UIAbility from '@ohos.app.ability.UIAbility';
|
||||
import Want from '@ohos.app.ability.Want';
|
||||
import window from '@ohos.window';
|
||||
import {
|
||||
AppConfigManager,
|
||||
buildHeader,
|
||||
CodeTableMonitor,
|
||||
GlobalContext,
|
||||
HXLog,
|
||||
jumpToQuote,
|
||||
PreferenceService,
|
||||
RuntimeStatus,
|
||||
ScreenUtil,
|
||||
StartManager,
|
||||
updateBreakpoint,
|
||||
updateCurrentWindowWith,
|
||||
WebJumpLinkWhiteManager
|
||||
} from 'biz_common';
|
||||
import { AuthProviderService } from 'biz_auth';
|
||||
import { ContextUtils, StringUtils } from 'hxutil';
|
||||
import { ContextUtils as ContextUtils2b } from '@b2b/hxutil';
|
||||
import { ConnectionSeq } from '@kernel/lib_communication/src/main/ets/data/CommunicationConstants';
|
||||
import { SelfCodeGroupHandler } from '../groupsub/business/SelfCodeGroupHandler';
|
||||
import { HXSocketMonitor } from '../communication/socketmonitor/HXSocketMonitor';
|
||||
import { Configuration, ConfigurationConstant, wantConstant } from '@kit.AbilityKit';
|
||||
import { UgcClient } from '../register/ugcregister/UgcClient';
|
||||
import { ContinuousDataHelper, CurveNaviModel } from 'biz_quote/src/main/ets/data/ContinuousDataHelper';
|
||||
import { DelayMarketManager, FutureMarketDataManager, HQTabManager, InactiveContractManager,
|
||||
MainContractGroupHandler } from 'biz_market';
|
||||
import { MainContractManager } from 'biz_market/src/main/ets/manager/MainContractManager';
|
||||
import { SelfCodeMainListHandler } from '../groupsub/business/SelfCodeMainListHandler';
|
||||
import { UgcAuthListener } from '../register/ugcregister/UgcAuthListener';
|
||||
import { AppUtil } from '@pura/harmony-utils';
|
||||
import { ElkUploadRegister } from '../register/elkregister/ElkUploadRegister';
|
||||
import { HexinVersionControl } from 'biz_trade/src/main/ets/utils/HexinVersionControl';
|
||||
import { SecurityMonitorInit } from '../monitor/SecurityMonitorInit';
|
||||
import { TabConfigManager } from '@b2c/biz_discover';
|
||||
import { ShareConfig } from '@b2c-spi/hx_share_api/src/main/ets/config/ShareConfig';
|
||||
import { HMRouterMgr } from '@hadss/hmrouter';
|
||||
import { EntryConstant } from '../constant/EntryConstant';
|
||||
import { ConnectionInitiator } from '../communication/ConnectionInitiator';
|
||||
import { APMHelper } from '../monitor/APMHelper';
|
||||
import { cbasService } from '../register/cbasregister/CbasServiceRegister';
|
||||
import { eventSubscription } from '../monitor/AppEvent';
|
||||
import { StockGroupConstants } from 'biz_selfcode';
|
||||
import { StockGroupHandler } from '../groupsub/business/StockGroupHandler';
|
||||
import { VarietyGroupHandler } from '../groupsub/business/VarietyGroupHandler';
|
||||
import { SidGroupHandler } from '../groupsub/business/SidGroupHandler';
|
||||
import { FuturesCompanyGroupHandler, YYBListGroupHandler, TradeSettingGroupHandler, TradeCloudSyncConfig,
|
||||
TradeMsgUniqueIdManager,
|
||||
TradeNotificationTrigger } from 'biz_trade';
|
||||
import { GroupSubManager } from 'base_mobilepush';
|
||||
import { CodeTableManager } from 'lib_codetablemanager';
|
||||
import BuildProfile from 'BuildProfile';
|
||||
import { GrayTestPushHandler } from '../groupsub/business/GrayTestPushHandler';
|
||||
import { AccountAnalysisLuaPushHandler } from '../groupsub/business/AccountAnalysisLuaPushHandler';
|
||||
import { QhStrategyAdPushHandler } from '../groupsub/business/QhStrategyAdPushHandler';
|
||||
import { GrayTestInitializor } from '../initialize/GrayTestInitializor';
|
||||
import { CloudStoreLogInitializor } from '../initialize/CloudStoreLogInitializor';
|
||||
import { HXPushService } from 'service_push';
|
||||
import { WantCacheManager } from './WantManager';
|
||||
import { pushService } from '@kit.PushKit';
|
||||
import { HXNetwork } from '../network/HXNetwork';
|
||||
import { AdvertisingMonitorService, KaihuFlagManager, ServerTimeProvider, UserSourceManager } from 'biz_hxservice';
|
||||
import { UnifyUrlManager } from '@b2c-f/url-modify-config';
|
||||
import { FontManager } from '../font/FontManager';
|
||||
import { BizType, IndicatorGroupHandler, CommonIndicatorsManage, IndicatorParamsManage } from 'biz_indicator';
|
||||
import { FigureRegistry } from 'biz_chart_data_processor';
|
||||
import { TradeInitializor } from '../initialize/TradeInitializor';
|
||||
import { AdsInitializer } from '../initialize/AdsInitializor';
|
||||
import { AIGroupClient } from 'service_aigroup'
|
||||
import { AIGroupPushHandler } from '../groupsub/business/AIGroupPushHandler';
|
||||
import { DrawLineGroupHandler } from 'biz_quote';
|
||||
import { CurveStorage } from 'biz_quote';
|
||||
import { OpenAccountManager } from 'biz_openaccount/src/main/ets/manager/OpenAccountManager';
|
||||
import { HttpUtils } from '@kernel/rcp';
|
||||
import { DebugToolUtil } from 'biz_debug';
|
||||
import { TradeDailyPushDataManager } from '../NotificationExtension/TradeDaily/TradeDailyPushDataManager';
|
||||
import { TradeDailyConvertManager } from '../NotificationExtension/TradeDaily/TradeDailyConvertManager';
|
||||
import { PushMessageManager } from 'biz_trade/src/main/ets/service/push/PushMessageManager';
|
||||
import { ColourModeStatus } from '@kernel/theme_manager';
|
||||
import { VarietyCodeTransformManager } from 'biz_chart_data_processor/src/main/ets/utils/VarietyCodeTransformManager';
|
||||
import { ContextUtils as ContextUtils2c } from '@b2c-m/ohos_hx_utils';
|
||||
import { FunctionAuthorityGroupHandler } from '../groupsub/business/FunctionAuthorityGroupHandler';
|
||||
import { FunctionAuthorityRequestManager } from '../functionauthority/FunctionAuthorityRequester';
|
||||
import { QhStrategyAdPagePushHandler } from '../groupsub/business/QhStrategyAdPagePushHandler';
|
||||
|
||||
/**
|
||||
* 应用的主Ability
|
||||
* @author menghuiyuan@myhexin.com
|
||||
*/
|
||||
export default class EntryAbility extends UIAbility {
|
||||
private readonly tag: string = "EntryAbility"
|
||||
private windowObj?: window.Window
|
||||
private curBp: string = ''
|
||||
//socket连接监控,需要接收应用前后台事件
|
||||
private socketMonitor: HXSocketMonitor = new HXSocketMonitor();
|
||||
private curNaviModel: CurveNaviModel | null = null;
|
||||
|
||||
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
|
||||
|
||||
pushService.receiveMessage('IM', this, (payload) => {
|
||||
// todo 应用前台弹窗接收push逻辑,待补充。
|
||||
try {
|
||||
const data = payload.data;
|
||||
const pushInfo: string = JSON.parse(data)?.data
|
||||
HXLog.d(this.tag, `Succeeded in getting notification,data=${JSON.stringify(pushInfo)}`);
|
||||
|
||||
const pushModel = TradeDailyPushDataManager.getInstance().convertToTradeDailyPushModel(JSON.parse(pushInfo))
|
||||
if (!pushModel) {
|
||||
return
|
||||
}
|
||||
// 推送去重判断
|
||||
TradeMsgUniqueIdManager.getInstance().isSameIdentify(
|
||||
pushModel.time,
|
||||
pushModel.iconType,
|
||||
pushModel.type,
|
||||
pushModel.orderNo
|
||||
).then((hasSame) => {
|
||||
if (!hasSame) {
|
||||
TradeDailyPushDataManager.getInstance().recordPushDaily(JSON.parse(pushInfo)).then(() => {
|
||||
TradeDailyConvertManager.getInstance().convertDailyFromPush()
|
||||
})
|
||||
PushMessageManager.getInstance().processReceiveHXPushMessage(pushInfo, false)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
HXLog.e(this.tag, `Parse push data failed: ${error}`)
|
||||
}
|
||||
})
|
||||
|
||||
hilog.info(0x0000, this.tag, '%{public}s', 'Ability onCreate');
|
||||
|
||||
// ========== 注册自定义 Figure ==========
|
||||
// 注册所有 iOS 迁移过来的自定义 Figure
|
||||
// 包括:bar, barDouble, polygon, tower, american, trade, yAxisRange
|
||||
FigureRegistry.registerAll()
|
||||
hilog.info(0x0000, this.tag, 'Custom Figures registered successfully');
|
||||
// =====================================
|
||||
|
||||
DebugToolUtil.TARGET_NAME = BuildProfile.TARGET_NAME
|
||||
DebugToolUtil.PRODUCT_NAME = BuildProfile.PRODUCT_NAME
|
||||
DebugToolUtil.BUILD_MODE_NAME = BuildProfile.BUILD_MODE_NAME
|
||||
// Context初始化前移
|
||||
GlobalContext.initContext(this.context.getApplicationContext())
|
||||
ContextUtils.initContext(this.context.getApplicationContext())
|
||||
ContextUtils2c.initContext(this.context.getApplicationContext())
|
||||
ContextUtils2b.initContext(this.context.getApplicationContext())
|
||||
AppUtil.init(this.context)
|
||||
// 交易通知触发器初始化
|
||||
TradeNotificationTrigger.getInstance().init(this.context)
|
||||
//崩溃保护代码
|
||||
SecurityMonitorInit.getInstance().init();
|
||||
GrayTestInitializor.initGrayTest()
|
||||
CloudStoreLogInitializor.initCloudStoreLog()
|
||||
HMRouterMgr.init({ context: this.context });
|
||||
UnifyUrlManager.getInstance().init(this.context);
|
||||
// 启动管理:是否为安装后第一次进入APP
|
||||
StartManager.getInstance().init()
|
||||
RuntimeStatus.getInstance().init()
|
||||
ElkUploadRegister.getInstance().init();
|
||||
UgcClient.getInstance().init()
|
||||
AuthProviderService.getInstance().setUgcAuthListener(UgcAuthListener.getInstance())
|
||||
this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
|
||||
console.info(`setMissionContinueState: ${JSON.stringify(result)}`);
|
||||
});
|
||||
eventSubscription()
|
||||
this.initRcp()
|
||||
ShareConfig.setAppId(EntryConstant.WX_SHARE_ID);
|
||||
this.initFuture()
|
||||
AdsInitializer.initAds()
|
||||
// 初始化 AI 分组客户端
|
||||
AIGroupClient.getInstance().init(DebugToolUtil.isDebugMode())
|
||||
WantCacheManager.getInstance().stashWant(want)
|
||||
|
||||
FontManager.getInstance().loadTHSFonts()
|
||||
HXLog.init()
|
||||
OpenAccountManager.getInstance().upCfmmcRecord()
|
||||
UserSourceManager.getInstance().initUserSource()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化Rcp库配置
|
||||
*/
|
||||
initRcp() {
|
||||
HttpUtils.setGlobalHeaders((url) => {
|
||||
return buildHeader(true)
|
||||
})
|
||||
}
|
||||
|
||||
initFuture() {
|
||||
//大内容tab配置初始化
|
||||
TabConfigManager.getInstance().initDiscoverTabConfig()
|
||||
// 初始化常用指标管理(参考 iOS MePool.mm:338 userIdLoginSuccess)
|
||||
CommonIndicatorsManage.getInstance().init(this.context)
|
||||
// 初始化指标参数管理(参考 iOS MePool.mm:340)
|
||||
IndicatorParamsManage.getInstance().init(this.context)
|
||||
FunctionAuthorityRequestManager.requestAuthorityDataAfterLocalLoad()
|
||||
//初始化码表
|
||||
const codeTableInstance = CodeTableManager.get()
|
||||
codeTableInstance.setInnerDebugMode(DebugToolUtil.isInnerNetSimulateMode())
|
||||
codeTableInstance.initCodeTable(this.context, HexinVersionControl.disableCodeTableIncrementUpdate(),
|
||||
CodeTableMonitor.getInstance(), () => {
|
||||
return ServerTimeProvider.getInstance().getServerTime()
|
||||
}, (isJson?: boolean) => {
|
||||
return buildHeader(isJson)
|
||||
}).then(() => {
|
||||
//主力合约初始化
|
||||
MainContractManager.get().init()
|
||||
//初始化期货行情页面的tab配置
|
||||
HQTabManager.get().init()
|
||||
})
|
||||
//期货市场配置
|
||||
FutureMarketDataManager.get().init()
|
||||
//不活跃合约
|
||||
InactiveContractManager.get().init()
|
||||
// 初始化延时市场配置
|
||||
DelayMarketManager.get().init()
|
||||
// 初始化品种码转换映射表(如果还没加载)
|
||||
VarietyCodeTransformManager.init()
|
||||
// 交易管理初始化
|
||||
TradeInitializor.init(this.context)
|
||||
// 分时持久化存储UI状态存储初始化
|
||||
CurveStorage.getInstance().initPersistentStorage()
|
||||
}
|
||||
|
||||
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
|
||||
HXLog.i(this.tag, `onNewWant ${AbilityConstant.LaunchReason.CONTINUATION}`)
|
||||
WantCacheManager.getInstance().stashWant(want)
|
||||
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
|
||||
// 将上述的保存的数据取出恢复
|
||||
let continueInput = '';
|
||||
if (want.parameters != undefined) {
|
||||
continueInput = want.parameters.curveData as string;
|
||||
|
||||
HXLog.i(this.tag, "onNewWant continue input = " + continueInput)
|
||||
this.curNaviModel = JSON.parse(continueInput) as CurveNaviModel;
|
||||
} else {
|
||||
HXLog.i(this.tag, "onNewWant continue input is empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
OpenAccountManager.getInstance().upCfmmcRecord()
|
||||
PreferenceService.putVal('Install', 'isFirstTimeEntering', false, true);
|
||||
hilog.info(0x0000, this.tag, '%{public}s', 'Ability onDestroy');
|
||||
}
|
||||
|
||||
onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult | Promise<AbilityConstant.OnContinueResult> {
|
||||
let versionDst = wantParam.version; // 获取迁移对端应用的版本号
|
||||
let versionSrc: number = 1000600; // 获取迁移源端即本端应用的版本号
|
||||
HXLog.i(this.tag, "versionDst = " + versionDst + "versionSrc = " + versionSrc)
|
||||
if (versionDst < versionSrc) { // 兼容性校验
|
||||
// 兼容性校验不满足
|
||||
HXLog.w(this.tag, "onContinue, dst not match the minumum version, abort continuation process")
|
||||
return AbilityConstant.OnContinueResult.MISMATCH;
|
||||
}
|
||||
if (ContinuousDataHelper.getInstance().getCurrentNavi()) {
|
||||
hilog.info(0x0000, this.tag, "onContinue set success = ");
|
||||
wantParam["curveData"] = JSON.stringify(ContinuousDataHelper.getInstance().getCurrentNavi()!);
|
||||
}
|
||||
wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
|
||||
return AbilityConstant.OnContinueResult.AGREE;
|
||||
}
|
||||
|
||||
onWindowStageCreate(windowStage: window.WindowStage) {
|
||||
//存储程序当前的 windowStage,用于创建子窗口。
|
||||
AppStorage.setOrCreate('EntryAbilityWindowStage', windowStage);
|
||||
// 埋点调试工具需要使用的 windowStage
|
||||
AppStorage.setOrCreate('windowStage', windowStage);
|
||||
|
||||
//初始化App的一些公共资源和窗口设置
|
||||
let windowClass: window.Window = windowStage.getMainWindowSync();
|
||||
let avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
|
||||
GlobalContext.initNavigationBarHeight(avoidArea.bottomRect.height)
|
||||
|
||||
|
||||
let statusBarHeight = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height;
|
||||
GlobalContext.initStatusBarHeight(statusBarHeight);
|
||||
// Main window is created, set main page for this ability
|
||||
hilog.info(0x0000, this.tag, '%{public}s', 'Ability onWindowStageCreate');
|
||||
windowStage.getMainWindow().then((windowObj) => {
|
||||
this.windowObj = windowObj
|
||||
// 获取应用启动时的窗口尺寸
|
||||
this.curBp = updateBreakpoint(windowObj.getWindowProperties().windowRect.width, this.curBp)
|
||||
// 注册回调函数,监听窗口尺寸变化
|
||||
windowObj.on('windowSizeChange', (windowSize) => {
|
||||
this.curBp = updateBreakpoint(windowSize.width, this.curBp)
|
||||
updateCurrentWindowWith(windowSize.width)
|
||||
})
|
||||
});
|
||||
this.registerGroupHandlers();
|
||||
ColourModeStatus.initColourModeSetting(this.context)
|
||||
HexinVersionControl.recordFirstInstallVersion();
|
||||
HexinVersionControl.getInstance().requestVersionConfig();
|
||||
|
||||
// 网络不可用
|
||||
const netAvailable = HXNetwork.getInstance().detectNet();
|
||||
if (!netAvailable) {
|
||||
this.loadNetworkAnomalyPage(windowStage);
|
||||
return;
|
||||
}
|
||||
|
||||
this.initializeCbasService().then(() => {
|
||||
// 正常流程
|
||||
}).catch((error: Error) => {
|
||||
// 即使失败也记录日志,但继续加载页面
|
||||
HXLog.e(this.tag, 'cbasService init failed: %{public}s');
|
||||
}).finally(() => {
|
||||
// 确保页面一定会加载
|
||||
if (!PreferenceService.getBooleanValueSync('Privacy', 'isUserAgree', false)) {
|
||||
this.loadPrivacyPage(windowStage);
|
||||
} else {
|
||||
this.loadIndexPage(windowStage);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onWindowStageRestore(windowStage: window.WindowStage): void {
|
||||
//应用窗口恢复时,如果有接续的数据就说明当前为接续开启应用,打开分时页
|
||||
HXLog.i(this.tag, "onWindowStageRestore")
|
||||
let parsedData: CurveNaviModel;
|
||||
if (this.curNaviModel) {
|
||||
HXLog.i(this.tag, "onWindowStageRestore data is valid")
|
||||
parsedData = this.curNaviModel;
|
||||
jumpToQuote(parsedData.code, parsedData.marketId, parsedData.codeList, parsedData.marketIdList);
|
||||
this.curNaviModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
private registerGroupHandlers() {
|
||||
GroupSubManager.getInstance().addUserGroupHandler(SelfCodeMainListHandler.GROUP_HANDLE_NAME, new SelfCodeMainListHandler());
|
||||
GroupSubManager.getInstance().addUserGroupHandler(SelfCodeGroupHandler.GROUP_HANDLE_NAME, new SelfCodeGroupHandler());
|
||||
GroupSubManager.getInstance().addUserGroupHandler(YYBListGroupHandler.GROUP_HANDLE_NAME,new YYBListGroupHandler())
|
||||
GroupSubManager.getInstance().addUserGroupHandler(TradeCloudSyncConfig.FILE_NAME, new TradeSettingGroupHandler())
|
||||
const indicatorChannels: string[] = [
|
||||
BizType.COMMON_INDICATORS,
|
||||
BizType.CUSTOM_INDICATORS,
|
||||
BizType.INDICATOR_PARAMS,
|
||||
BizType.SQUARE_INDICATORS
|
||||
]
|
||||
for (const channel of indicatorChannels) {
|
||||
GroupSubManager.getInstance().addUserGroupHandler(channel, new IndicatorGroupHandler(channel))
|
||||
}
|
||||
// 接入画线同步 push 分组,收到远端画线后驱动本地刷新与重绘。
|
||||
GroupSubManager.getInstance().addUserGroupHandler(DrawLineGroupHandler.GROUP_KEY, new DrawLineGroupHandler())
|
||||
GroupSubManager.getInstance().addUserGroupHandler(AIGroupPushHandler.GROUP_HANDLE_NAME, new AIGroupPushHandler())
|
||||
GroupSubManager.getInstance().addUserGroupHandler(SidGroupHandler.GROUP_HANDLE_NAME, new SidGroupHandler())
|
||||
GroupSubManager.getInstance().addUserGroupHandler(
|
||||
FunctionAuthorityGroupHandler.GROUP_HANDLE_NAME,
|
||||
new FunctionAuthorityGroupHandler()
|
||||
)
|
||||
GroupSubManager.getInstance().addUserGroupHandler(QhStrategyAdPushHandler.GROUP_HANDLE_NAME, new QhStrategyAdPushHandler())
|
||||
GroupSubManager.getInstance().addAllGroupHandler(QhStrategyAdPagePushHandler.GROUP_HANDLE_NAME, new QhStrategyAdPagePushHandler())
|
||||
GroupSubManager.getInstance().addAllGroupHandler(GrayTestPushHandler.GROUP_HANDLE_NAME, new GrayTestPushHandler());
|
||||
GroupSubManager.getInstance().addAllGroupHandler(AccountAnalysisLuaPushHandler.GROUP_HANDLE_NAME, new AccountAnalysisLuaPushHandler());
|
||||
GroupSubManager.getInstance().addAllGroupHandler(FuturesCompanyGroupHandler.GROUP_HANDLE_NAME,new FuturesCompanyGroupHandler())
|
||||
GroupSubManager.getInstance().addAllGroupHandler(StockGroupConstants.KEY_STOCK_STORAGE_UPDATE, new StockGroupHandler())
|
||||
GroupSubManager.getInstance().addAllGroupHandler(StockGroupConstants.KEY_VARIETY_STORAGE_UPDATE, new VarietyGroupHandler())
|
||||
GroupSubManager.getInstance().addAllGroupHandler(MainContractGroupHandler.GROUP_HANDLE_NAME, new MainContractGroupHandler())
|
||||
|
||||
}
|
||||
|
||||
onWindowStageDestroy() {
|
||||
hilog.info(0x0000, this.tag, '%{public}s', 'Ability onWindowStageDestroy');
|
||||
}
|
||||
|
||||
onForeground() {
|
||||
// Ability has brought to foreground
|
||||
AppStorage.setOrCreate('isAbilityForeGround', true);
|
||||
cbasService.onForeground()
|
||||
this.socketMonitor.onAppForeground();
|
||||
HXPushService.getInstance().checkPushPermissionStatus()
|
||||
ColourModeStatus.updateSystemColourModeOnAbilityForeground(this.context)
|
||||
}
|
||||
|
||||
onConfigurationUpdate(newConfig: Configuration): void {
|
||||
ColourModeStatus.onSystemConfigurationUpdate(newConfig.colorMode)
|
||||
}
|
||||
|
||||
onBackground() {
|
||||
// Ability has back to background
|
||||
AppStorage.setOrCreate('isAbilityForeGround', false);
|
||||
cbasService.onBackground()
|
||||
this.socketMonitor.onAppBackground();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载应用主页面
|
||||
* @param windowStage
|
||||
*/
|
||||
private loadIndexPage(windowStage: window.WindowStage) {
|
||||
//三方标题栏风险提示,白名单
|
||||
WebJumpLinkWhiteManager.getInstance().loadWhiteJumpLink()
|
||||
|
||||
windowStage.loadContent('pages/Index', (err, data) => {
|
||||
if (err.code) {
|
||||
hilog.error(0x0000, this.tag, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
|
||||
return;
|
||||
}
|
||||
onAppStartLoading(this.context,true);
|
||||
hilog.info(0x0000, this.tag, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载隐私政策页面
|
||||
* @param windowStage
|
||||
*/
|
||||
private loadPrivacyPage(windowStage: window.WindowStage) {
|
||||
windowStage.loadContent('pages/LauncherPage', (err, data) => {
|
||||
if (err.code) {
|
||||
hilog.error(0x0000, this.tag, 'Failed to load loadPrivacyPage. Cause: %{public}s', JSON.stringify(err) ?? '');
|
||||
return;
|
||||
}
|
||||
onAppStartLoading(this.context);
|
||||
hilog.info(0x0000, this.tag, 'Succeeded in loadPrivacyPage. Data: %{public}s', JSON.stringify(data) ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载网络异常页面
|
||||
* @param windowStage
|
||||
*/
|
||||
private loadNetworkAnomalyPage(windowStage: window.WindowStage) {
|
||||
windowStage.loadContent('network/NetworkAnomalyPage', (err, data) => {
|
||||
if (err.code) {
|
||||
hilog.error(0x0000, this.tag, 'Failed to load NetworkAnomalyPage. Cause: %{public}s', JSON.stringify(err) ?? '');
|
||||
return;
|
||||
}
|
||||
onAppStartLoading(this.context);
|
||||
hilog.info(0x0000, this.tag, 'Succeeded in NetworkAnomalyPage. Data: %{public}s', JSON.stringify(data) ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载埋点初始化
|
||||
*/
|
||||
private async initializeCbasService() {
|
||||
try {
|
||||
await AppConfigManager.getInstance().fetchIMEI()
|
||||
let udid = AppConfigManager.getInstance().getUdidSync()
|
||||
if(!StringUtils.isNotEmpty(udid)) {
|
||||
udid = AppConfigManager.UDID_NONE
|
||||
}
|
||||
await cbasService.init(udid)
|
||||
HXLog.i(this.tag, 'cbasService initialized successfully');
|
||||
} catch (error) {
|
||||
HXLog.i( this.tag, 'cbasService init failed');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function onAppStartLoading(context:Context,isAgreePrivacy:boolean = false) {
|
||||
ConnectionInitiator.initConnectionService(context)
|
||||
AppConfigManager.getInstance().fetchIMEI().then((isSuccess: boolean) => {
|
||||
if (isSuccess) {
|
||||
AuthProviderService.getInstance().sendAuthRequest(ConnectionSeq.HQ);
|
||||
}
|
||||
let udid = AppConfigManager.getInstance().getUdidSync()
|
||||
if(!StringUtils.isNotEmpty(udid)) {
|
||||
udid = AppConfigManager.UDID_NONE
|
||||
}
|
||||
cbasService.init(udid)
|
||||
if (isAgreePrivacy) {
|
||||
AdvertisingMonitorService.getInstance().init(context)
|
||||
}
|
||||
});
|
||||
//初始化APM
|
||||
APMHelper.init(context);
|
||||
//初始化窗口工具
|
||||
ScreenUtil.get().init();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { MDataModel, AppPushMessage } from "service_push";
|
||||
|
||||
export function ProtocolJumpTest() {
|
||||
let appMsg1: MDataModel = {
|
||||
'A': '10',
|
||||
'F': '2205',
|
||||
'C': 'FG2601',
|
||||
'ACT': '70',
|
||||
'TY': '20',
|
||||
'ST': '预警通知'
|
||||
}
|
||||
|
||||
let appMsg2: MDataModel = {
|
||||
'A': '10',
|
||||
'ACT': '70',
|
||||
"F": "2804",
|
||||
"U": "https://news.10jqka.com.cn/20251111/c672408397.shtml"
|
||||
}
|
||||
|
||||
let appMsg3: MDataModel = {
|
||||
// 跳转个股
|
||||
'A': '10',
|
||||
'F': '2205',
|
||||
'C': 'USDIND',
|
||||
'ACT': '60',
|
||||
'ST': '美元指数预警'
|
||||
}
|
||||
|
||||
let appMsg4: MDataModel = {
|
||||
// 跳转外部浏览器
|
||||
'A': '30',
|
||||
'ACT': '70',
|
||||
"F": "2804",
|
||||
"U": "https://news.10jqka.com.cn/20251111/c672408397.shtml"
|
||||
}
|
||||
|
||||
let appMsg5: MDataModel = {
|
||||
// 拉起其他应用
|
||||
'A': '40',
|
||||
'ACT': '70',
|
||||
'PN':'com.hexin.hmn.sjcg',
|
||||
'CN':'EntryAbility',
|
||||
}
|
||||
|
||||
let protocol1: AppPushMessage = {
|
||||
APPID: '207',
|
||||
ID: '604900',
|
||||
TIPTYPE: '1',
|
||||
TITLE: '标题',
|
||||
DESC: '内容',
|
||||
APPMSG: appMsg1
|
||||
}
|
||||
|
||||
let data: Record<string, string> = {};
|
||||
data['payload'] = JSON.stringify(protocol1);
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { RemoteNotificationExtensionAbility, pushCommon } from '@kit.PushKit';
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
import { TradeDailyPushDataManager } from '../NotificationExtension/TradeDaily/TradeDailyPushDataManager';
|
||||
|
||||
const DOMAIN = 0x0000;
|
||||
|
||||
export default class RemoteNotificationExtAbility extends RemoteNotificationExtensionAbility {
|
||||
async onReceiveMessage(remoteNotificationInfo: pushCommon.RemoteNotificationInfo):
|
||||
Promise<pushCommon.RemoteNotificationContent> {
|
||||
const data = remoteNotificationInfo.data
|
||||
try {
|
||||
const pushInfo: string = JSON.parse(data)?.data
|
||||
await TradeDailyPushDataManager.getInstance().initDB(this.context)
|
||||
const pushModel = TradeDailyPushDataManager.getInstance().convertToTradeDailyPushModel(JSON.parse(pushInfo))
|
||||
if (pushModel) {
|
||||
hilog.info(DOMAIN, 'RemoteNotification', 'info: %{public}s', pushModel.body);
|
||||
await TradeDailyPushDataManager.getInstance().recordPushDaily(pushModel)
|
||||
}
|
||||
} catch (e) {
|
||||
hilog.error(DOMAIN, 'RemoteNotification', 'error: %{public}s', e);
|
||||
}
|
||||
|
||||
await Promise.resolve();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
import Want from '@ohos.app.ability.Want';
|
||||
import { Queue } from '@kit.ArkTS';
|
||||
import { ClipUtils, GlobalContext, HXLog, jumpToWebPage } from 'biz_common';
|
||||
import { AppPushMessage, DoPushCbas, PushConstants, PushMessageManager } from 'service_push';
|
||||
import { ShareManager } from '@b2c-spi/hx_share_api/src/main/ets/manager/ShareManager';
|
||||
import { ELK_LEVEL, sendElk } from 'service_monitor';
|
||||
|
||||
const TAG: string = 'WantCacheManager'
|
||||
const BACK_ACTION: string = 'ohos.want.action.viewData'
|
||||
const BACK_ENTITY: string = 'entity.system.browsable'
|
||||
|
||||
|
||||
export const FIRST_PAGE_ONAPPEAR_EVENT : string = 'FirstPage OnAppear'
|
||||
/**
|
||||
* WantCacheManager 类用于管理 Want 对象的缓存和处理。
|
||||
* 采用单例模式,确保整个应用中只有一个实例。
|
||||
* 该管理器会缓存接收到的 Want 对象,并在合适的时机进行处理。
|
||||
*/
|
||||
export class WantCacheManager {
|
||||
// 单例模式下的唯一实例
|
||||
private static instance: WantCacheManager
|
||||
|
||||
private pushMessageManager : PushMessageManager = PushMessageManager.getInstance();
|
||||
private clipUtils: ClipUtils = ClipUtils.getInstance()
|
||||
|
||||
/**
|
||||
* 获取 WantCacheManager 的单例实例。
|
||||
* 如果实例不存在,则创建一个新实例。
|
||||
* @returns WantCacheManager 的单例实例
|
||||
*/
|
||||
public static getInstance(): WantCacheManager {
|
||||
if (!WantCacheManager.instance) {
|
||||
WantCacheManager.instance = new WantCacheManager()
|
||||
}
|
||||
return WantCacheManager.instance
|
||||
}
|
||||
|
||||
// 标志位,指示是否可以处理缓存的 Want 对象
|
||||
private enabled: boolean = false
|
||||
/**
|
||||
* 首页加载完成的回调函数。
|
||||
* 当首页加载完成后,将 enabled 标志位设置为 true,并尝试处理缓存的 Want 对象。
|
||||
*/
|
||||
private firstPageFinishedCb = () => {
|
||||
this.enabled = true
|
||||
this.popWant()
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有构造函数,确保只能通过 getInstance 方法获取实例。
|
||||
* 注册首页加载完成的事件监听器。
|
||||
*/
|
||||
private constructor() {
|
||||
// 监听首页加载完成事件,当事件触发时调用 firstPageFinishedCb 回调函数
|
||||
GlobalContext.get().eventHub.on(FIRST_PAGE_ONAPPEAR_EVENT, this.firstPageFinishedCb)
|
||||
}
|
||||
|
||||
// 存储 Want 对象的队列
|
||||
private wantQueue: Queue<Want> = new Queue()
|
||||
|
||||
/**
|
||||
* 存储 Want 对象到队列中。
|
||||
* 如果 enabled 标志位为 true,则尝试处理队列中的 Want 对象。
|
||||
* @param want 要存储的 Want 对象
|
||||
*/
|
||||
stashWant(want: Want) {
|
||||
// 将 Want 对象添加到队列中
|
||||
this.wantQueue.add(want)
|
||||
// 记录日志,提示接收到新的 Want 对象
|
||||
HXLog.i(TAG, ' New Want received, want.param : ')
|
||||
// 如果可以处理 Want 对象,则尝试处理队列中的元素
|
||||
if (this.enabled) {
|
||||
this.popWant()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从队列中取出并处理一个 Want 对象。
|
||||
* 如果队列不为空,则调用不同业务方进行处理,业务方处理保证不更改原有对象
|
||||
*/
|
||||
private popWant() {
|
||||
if (this.wantQueue.length > 0) {
|
||||
const w = this.wantQueue.pop()
|
||||
|
||||
// push协议
|
||||
this.handlePayload(w)
|
||||
|
||||
// 微信分享
|
||||
this.handleWeChat(w)
|
||||
|
||||
// 浏览器回流
|
||||
this.handleBackWash(w)
|
||||
|
||||
// appLinking回流
|
||||
this.handleBackWashByBrowsable(w)
|
||||
|
||||
this.handleReflow(w)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理推送协议对象。
|
||||
* 根据want中解析到的推送协议的来源,获取相应的策略并处理。
|
||||
* @param want 要处理的want
|
||||
*/
|
||||
private handlePayload(want : Want) {
|
||||
if (want?.action === 'com.push.action') {
|
||||
const param = want?.parameters
|
||||
if (param) {
|
||||
const payloadJson = param['payload'] as string
|
||||
HXLog.i(PushConstants.AM_PUSH, payloadJson)
|
||||
const payload = JSON.parse(payloadJson) as AppPushMessage
|
||||
DoPushCbas.pushArriveCbas(payload)
|
||||
DoPushCbas.pushShowCbas(payload)
|
||||
this.pushMessageManager.handlerPush(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 处理wechat分享协议对象。
|
||||
* 根据want中解析到的分享协议,进行处理
|
||||
* @param want 要处理的want
|
||||
*/
|
||||
private handleWeChat(want: Want) {
|
||||
// 判断service是否有效
|
||||
const shareService = ShareManager.getShareService()
|
||||
if (!shareService) {
|
||||
HXLog.e("HXShare", 'shareService is null');
|
||||
return
|
||||
}
|
||||
shareService.getWXApi().handleWant(want, ShareManager.getShareService()?.getWXEventHandler())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 端外跳转至期货通
|
||||
*/
|
||||
private async handleBackWash(want: Want) {
|
||||
if (want.action === BACK_ACTION && want.uri && want.entities?.length === 0) {
|
||||
const params = this.extractUrlParams(want.uri)
|
||||
if (params['url']) {
|
||||
jumpToWebPage(params)
|
||||
}
|
||||
await this.dealPasteboard()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async dealPasteboard(){
|
||||
if (this.clipUtils.getClipBoardSwitchState()) {
|
||||
// 请求剪切板权限后再读取
|
||||
const hasPermission = await this.clipUtils.checkAndRequestPermission()
|
||||
if (hasPermission) {
|
||||
const clipboardContent = this.clipUtils.getClipboardContent()
|
||||
if (this.clipUtils.isValidClipPassword(clipboardContent)) {
|
||||
this.clipUtils.parseClipboard(clipboardContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 浏览器跳转至期货通
|
||||
*/
|
||||
private handleBackWashByBrowsable(want: Want) {
|
||||
if (want.action === BACK_ACTION && want.uri && want.entities &&
|
||||
want.entities[0] === BACK_ENTITY) {
|
||||
const params = this.extractUrlParams(want.uri)
|
||||
if (params['url']) {
|
||||
jumpToWebPage(params)
|
||||
}
|
||||
this.dealPasteboard()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理回流埋点。
|
||||
* @param want 要处理的want
|
||||
*/
|
||||
private handleReflow(want: Want) {
|
||||
if (want.parameters) {
|
||||
const callerBundleName = want.parameters['ohos.aafwk.param.callerBundleName'] as string
|
||||
if (callerBundleName === 'com.hexin.hmn.sjcg') {
|
||||
// 手抄跳转
|
||||
sendElk(TAG, `callerBundleName ${callerBundleName}`, ELK_LEVEL.INFO, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extractUrlParams(input: string): Record<string, string> {
|
||||
const result: Record<string, string> = {};
|
||||
// 提取所有形如 key=value 的参数(以 &、^ 或字符串结尾为边界)
|
||||
const matches = Array.from(input.matchAll(/([a-zA-Z_][a-zA-Z0-9_]*)=([^&\^]*)/gi));
|
||||
if (matches.length === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 迭代解码(兼容含 & 的转义值 %26)
|
||||
matches.forEach(match => {
|
||||
const key = match[1];
|
||||
let decoded = match[2];
|
||||
let prev = '';
|
||||
do {
|
||||
prev = decoded;
|
||||
try {
|
||||
decoded = decodeURIComponent(decoded);
|
||||
} catch {
|
||||
break; // 非法编码终止
|
||||
}
|
||||
} while (decoded !== prev && /%[0-9A-Fa-f]{2}/.test(decoded));
|
||||
result[key] = decoded;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { TextMetricsMeasurer } from "@b2b/hq_table";
|
||||
import { text } from "@kit.ArkGraphics2D";
|
||||
import { HXLog } from "biz_common";
|
||||
|
||||
/**
|
||||
* author:liuqingliang
|
||||
* created on 2025/12/19
|
||||
* desc:全局字体管理
|
||||
*/
|
||||
export class FontManager {
|
||||
private static readonly TAG = "FontManager"
|
||||
private static instance: FontManager
|
||||
|
||||
public static getInstance(): FontManager {
|
||||
if (!FontManager.instance) {
|
||||
FontManager.instance = new FontManager()
|
||||
}
|
||||
return FontManager.instance
|
||||
}
|
||||
|
||||
registerFont(uiContext: UIContext) {
|
||||
try {
|
||||
let font = uiContext.getFont();
|
||||
font.registerFont({
|
||||
familyName: 'bold',
|
||||
familySrc: $rawfile('fonts/THSJinRongTi_bold.otf')
|
||||
});
|
||||
font.registerFont({
|
||||
familyName: 'Regular',
|
||||
familySrc: $rawfile('fonts/THSJinRongTi_Regular.otf')
|
||||
});
|
||||
font.registerFont({
|
||||
familyName: 'Medium',
|
||||
familySrc: $rawfile('fonts/THSJinRongTi_Medium.otf')
|
||||
});
|
||||
|
||||
TextMetricsMeasurer.getInstance().init()
|
||||
} catch (e) {
|
||||
HXLog.e(FontManager.TAG, "registerFont failed:" + JSON.stringify(e))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private loadFontSync(fontFamily: 'bold' | 'Regular' | 'Medium') {
|
||||
let fontCollection = text.FontCollection.getGlobalInstance()
|
||||
fontCollection.loadFontSync(fontFamily, $rawfile(`fonts/THSJinRongTi_${fontFamily}.otf`))
|
||||
}
|
||||
|
||||
loadTHSFonts() {
|
||||
this.loadFontSync('bold');
|
||||
this.loadFontSync('Regular');
|
||||
this.loadFontSync('Medium');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import {
|
||||
CookieManager,
|
||||
FunctionAuthorityRequester,
|
||||
HxServiceExportConstants,
|
||||
HXUserService,
|
||||
ProductAuthorityManager
|
||||
} from 'biz_hxservice'
|
||||
import { AppConfigManager } from 'biz_common'
|
||||
import { http } from '@kit.NetworkKit'
|
||||
import { JSON } from '@kit.ArkTS'
|
||||
import { UnifyUrlManager } from '@b2c-f/url-modify-config'
|
||||
import emitter from '@ohos.events.emitter'
|
||||
|
||||
interface EmptyFunctionAuthorityResponse {
|
||||
code: number
|
||||
data: Object[]
|
||||
}
|
||||
|
||||
class AppFunctionAuthorityRequester implements FunctionAuthorityRequester {
|
||||
|
||||
async requestAuthorityData(): Promise<object> {
|
||||
if (HXUserService.getInstance().isTemporaryUser()) {
|
||||
const response: EmptyFunctionAuthorityResponse = { code: 0, data: [] }
|
||||
return response
|
||||
}
|
||||
|
||||
const httpRequest = http.createHttp()
|
||||
try {
|
||||
const requestOptions: http.HttpRequestOptions = {
|
||||
method: http.RequestMethod.GET,
|
||||
header: this.buildHeaders(),
|
||||
connectTimeout: 30000,
|
||||
readTimeout: 30000,
|
||||
expectDataType: http.HttpDataType.STRING
|
||||
}
|
||||
const response = await httpRequest.request(UnifyUrlManager.getRealUrl('custom_authority_url'), requestOptions)
|
||||
if (response.responseCode !== http.ResponseCode.OK) {
|
||||
throw new Error(`http error: ${response.responseCode}`)
|
||||
}
|
||||
const parseOptions: JSON.ParseOptions = {
|
||||
bigIntMode: JSON.BigIntMode.PARSE_AS_BIGINT
|
||||
}
|
||||
return JSON.parse(response.result as string, null, parseOptions) as object
|
||||
} finally {
|
||||
httpRequest.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
private buildHeaders(): Record<string, string> {
|
||||
const headers: Record<string, string> = {}
|
||||
headers['Content-Type'] = 'application/json'
|
||||
const cookie = CookieManager.getInstance().getCookieSync()
|
||||
if (cookie) {
|
||||
headers['Cookie'] = cookie
|
||||
}
|
||||
return headers
|
||||
}
|
||||
}
|
||||
|
||||
export class FunctionAuthorityRequestManager {
|
||||
private static initialized: boolean = false
|
||||
private static cookieUpdateListener?: () => void
|
||||
private static requestingAfterCookieUpdate: boolean = false
|
||||
private static userId: string = ''
|
||||
|
||||
static loadAuthorityFromLocal(): boolean {
|
||||
FunctionAuthorityRequestManager.ensureInitialized()
|
||||
return ProductAuthorityManager.getInstance().loadAuthorityFromLocal()
|
||||
}
|
||||
|
||||
static requestAuthorityDataAfterLocalLoad(): Promise<boolean> {
|
||||
FunctionAuthorityRequestManager.loadAuthorityFromLocal()
|
||||
return FunctionAuthorityRequestManager.requestAuthorityData()
|
||||
}
|
||||
|
||||
static requestAuthorityData(): Promise<boolean> {
|
||||
FunctionAuthorityRequestManager.ensureInitialized()
|
||||
return ProductAuthorityManager.getInstance().requestAuthorityData()
|
||||
}
|
||||
|
||||
static requestAuthorityDataForPush(pushTimestamp: number): Promise<boolean> {
|
||||
FunctionAuthorityRequestManager.ensureInitialized()
|
||||
return ProductAuthorityManager.getInstance().requestAuthorityDataForPush(pushTimestamp)
|
||||
}
|
||||
|
||||
private static ensureInitialized(): void {
|
||||
if (FunctionAuthorityRequestManager.initialized) {
|
||||
return
|
||||
}
|
||||
ProductAuthorityManager.getInstance().setRequester(new AppFunctionAuthorityRequester())
|
||||
FunctionAuthorityRequestManager.registerCookieUpdateListener()
|
||||
FunctionAuthorityRequestManager.initialized = true
|
||||
}
|
||||
|
||||
private static registerCookieUpdateListener(): void {
|
||||
if (FunctionAuthorityRequestManager.cookieUpdateListener) {
|
||||
return
|
||||
}
|
||||
FunctionAuthorityRequestManager.cookieUpdateListener = () => {
|
||||
const newUserId = HXUserService.getInstance().getUserIdFromLocal()
|
||||
if (FunctionAuthorityRequestManager.requestingAfterCookieUpdate ||
|
||||
FunctionAuthorityRequestManager.userId == newUserId) {
|
||||
return
|
||||
}
|
||||
FunctionAuthorityRequestManager.userId = newUserId
|
||||
FunctionAuthorityRequestManager.requestingAfterCookieUpdate = true
|
||||
FunctionAuthorityRequestManager.requestAuthorityDataAfterLocalLoad()
|
||||
.finally(() => {
|
||||
FunctionAuthorityRequestManager.requestingAfterCookieUpdate = false
|
||||
})
|
||||
}
|
||||
emitter.on(HxServiceExportConstants.COOKIE_UPDATE, FunctionAuthorityRequestManager.cookieUpdateListener)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { StringUtils } from "@b2c-m/ohos_hx_utils";
|
||||
import { IStructReceiver, ProtocolDef, StuffBaseStruct, StuffResourceStruct } from '@kernel/lib_communication';
|
||||
import { GroupSubManager, L2DowngradeManager } from 'base_mobilepush';
|
||||
import { HashMap } from '@kit.ArkTS';
|
||||
import { emitter } from '@kit.BasicServicesKit';
|
||||
import { EmitterConstants } from 'biz_common'
|
||||
import { SubscribedPushManager } from "biz_trade";
|
||||
|
||||
export class RealDataStructObserver implements IStructReceiver {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
onIntercept(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
if (stuffBaseStruct == null) {
|
||||
return false;
|
||||
}
|
||||
const headId = stuffBaseStruct.packageId;
|
||||
if (headId == ProtocolDef.DATAID_NEW_REALTIME) {
|
||||
const dataType = stuffBaseStruct.headType & ProtocolDef.DATA_TYPE_MASK;
|
||||
if (dataType === ProtocolDef.DATA_TYPE_JSON) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
handleStruct(stuffBaseStruct: StuffBaseStruct): boolean {
|
||||
if (!(stuffBaseStruct instanceof StuffResourceStruct)) {
|
||||
return false;
|
||||
}
|
||||
const resStruct = stuffBaseStruct as StuffResourceStruct;
|
||||
const tmpBuffer = resStruct.buffer;
|
||||
const decodedStr = StringUtils.decode(tmpBuffer, "GBK")
|
||||
try {
|
||||
const jsonObject: ESObject = JSON.parse(decodedStr);
|
||||
//解析到分组订阅数据
|
||||
const parsedObj: ESObject = jsonObject["groupdata"];
|
||||
if (parsedObj) {
|
||||
GroupSubManager.getInstance().onReceiveGroupData(JSON.stringify(parsedObj), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 解析到Push订阅数据
|
||||
if (jsonObject[SubscribedPushManager.PARAM_CHANNEL]) {
|
||||
SubscribedPushManager.getInstance().onReceivePushData(decodedStr)
|
||||
}
|
||||
|
||||
const parsedEvents: HashMap<string, object>[] = jsonObject["events"]
|
||||
if (parsedEvents && parsedEvents.length > 0) {
|
||||
for (let event of parsedEvents) {
|
||||
if (event["eventID"] == "00000011") {
|
||||
L2DowngradeManager.getInstance().onReceiveGroupData(JSON.stringify(event));
|
||||
emitter.emit({ eventId: EmitterConstants.EVENT_ID_GET_PERMISSIONSID })
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getReceiverId(): string {
|
||||
return "RealDataStructObserver";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ISubDataHandler } from 'base_mobilepush'
|
||||
import { AIGroupClient, AI_GROUP_BIZ_NAME } from 'service_aigroup'
|
||||
|
||||
/**
|
||||
* AI分组的数据分组订阅处理器
|
||||
* 参考 iOS LJAIGroupManager 中的 MobiPush 注册机制
|
||||
* ctrlid = future_aigroupconfig
|
||||
*/
|
||||
export class AIGroupPushHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = AI_GROUP_BIZ_NAME
|
||||
|
||||
onReceiveGroupData(data: string): void {
|
||||
try {
|
||||
const pushData: AIGroupPushData = JSON.parse(data) as AIGroupPushData
|
||||
AIGroupClient.getInstance().onAIGroupPushNotification(pushData.ver)
|
||||
} catch (e) {
|
||||
// 解析失败时也触发更新,不传版本号
|
||||
AIGroupClient.getInstance().onAIGroupPushNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AIGroupPushData {
|
||||
ver?: number
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { ISubDataHandler } from 'base_mobilepush'
|
||||
import { GlobalContext, HXLog } from 'biz_common'
|
||||
import { ScriptUpdateService } from '@b2c-f/billparsemodule'
|
||||
|
||||
/**
|
||||
* 账户分析 Lua 脚本更新推送处理器
|
||||
*
|
||||
* 处理来自服务端的账户分析 Lua 脚本版本更新通知
|
||||
*/
|
||||
export class AccountAnalysisLuaPushHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = "qhtCapitalLuaUpdate"
|
||||
private static readonly TAG = "AccountAnalysisLuaPush"
|
||||
|
||||
onReceiveGroupData(data: string): void {
|
||||
HXLog.i(AccountAnalysisLuaPushHandler.TAG, `receive: ${data}`)
|
||||
|
||||
try {
|
||||
const pushData: AccountAnalysisLuaPushData = JSON.parse(data) as AccountAnalysisLuaPushData
|
||||
|
||||
if (pushData && pushData.AccountAnalysisLua) {
|
||||
const version = pushData.AccountAnalysisLua.version
|
||||
if (version) {
|
||||
HXLog.i(AccountAnalysisLuaPushHandler.TAG, `收到 Lua 脚本版本更新通知: version=${version}`)
|
||||
this.handleScriptUpdate(version)
|
||||
} else {
|
||||
HXLog.w(AccountAnalysisLuaPushHandler.TAG, '版本号为空')
|
||||
}
|
||||
} else {
|
||||
HXLog.w(AccountAnalysisLuaPushHandler.TAG, '推送数据格式错误')
|
||||
}
|
||||
} catch (error) {
|
||||
HXLog.e(AccountAnalysisLuaPushHandler.TAG, `解析推送数据失败: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理脚本更新
|
||||
*/
|
||||
private handleScriptUpdate(version: string): void {
|
||||
try {
|
||||
const context = GlobalContext.get()
|
||||
if (!context) {
|
||||
HXLog.e(AccountAnalysisLuaPushHandler.TAG, 'Context 为空,无法更新脚本')
|
||||
return
|
||||
}
|
||||
|
||||
const updateService = new ScriptUpdateService(context)
|
||||
|
||||
// 异步执行更新操作
|
||||
updateService.downloadAndInstall(version)
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
if (result.updated) {
|
||||
HXLog.i(AccountAnalysisLuaPushHandler.TAG,
|
||||
`Lua 脚本更新成功: version=${result.version}, scriptPath=${result.scriptPath}`)
|
||||
} else {
|
||||
HXLog.i(AccountAnalysisLuaPushHandler.TAG,
|
||||
`Lua 脚本已是最新版本: version=${result.version}`)
|
||||
}
|
||||
} else {
|
||||
HXLog.e(AccountAnalysisLuaPushHandler.TAG,
|
||||
`Lua 脚本更新失败: ${result.errorMessage}`)
|
||||
}
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
HXLog.e(AccountAnalysisLuaPushHandler.TAG, `Lua 脚本更新异常: ${error.message}`)
|
||||
})
|
||||
} catch (error) {
|
||||
HXLog.e(AccountAnalysisLuaPushHandler.TAG, `处理脚本更新失败: ${error}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户分析 Lua 脚本版本信息
|
||||
*/
|
||||
export interface AccountAnalysisLuaVersion {
|
||||
version: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户分析 Lua 脚本推送数据格式
|
||||
*/
|
||||
export interface AccountAnalysisLuaPushData {
|
||||
AccountAnalysisLua: AccountAnalysisLuaVersion
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { RandomUtil } from "@pura/harmony-utils";
|
||||
import { ISubDataHandler } from "base_mobilepush";
|
||||
import { HXLog } from "biz_common";
|
||||
import { StringUtil } from "biz_common/src/main/ets/utils/StringUtil";
|
||||
import { HexinVersionControl } from "biz_trade/src/main/ets/utils/HexinVersionControl";
|
||||
import { CodeTableManager } from "lib_codetablemanager";
|
||||
|
||||
export abstract class BaseStorageHandler implements ISubDataHandler {
|
||||
/**
|
||||
* 获取日志tag
|
||||
*/
|
||||
abstract getTag(): string
|
||||
|
||||
/**
|
||||
* 获取推送中的数据更新时间key
|
||||
*/
|
||||
abstract getTimeKey(): string
|
||||
|
||||
/**
|
||||
* 处理升级
|
||||
*/
|
||||
abstract update()
|
||||
|
||||
/**
|
||||
* 获取数据库版本信息
|
||||
*/
|
||||
abstract getDbVersion(): Promise<number>
|
||||
|
||||
private readonly RANDOM_REQUEST_TIME = 10 * 1000
|
||||
|
||||
private mTimeoutId: number | null = null
|
||||
|
||||
async onReceiveGroupData(data: string) {
|
||||
if (HexinVersionControl.disableCodeTableIncrementUpdate()) {
|
||||
return
|
||||
}
|
||||
HXLog.d(CodeTableManager.TAG, `${this.getTag()} onReveiveGroupData: ${data}`)
|
||||
const obj = JSON.parse(data) as Record<string, string>
|
||||
if (!obj) {
|
||||
HXLog.e(CodeTableManager.TAG, `${this.getTag()} onReceiveGroupData obj parse invalid`)
|
||||
return
|
||||
}
|
||||
const time: string = obj[this.getTimeKey()]
|
||||
const dbVersion = await this.getDbVersion()
|
||||
const remoteVersion = StringUtil.toSafeNumber(time, 0)
|
||||
HXLog.d(CodeTableManager.TAG, `${this.getTag()} remoteVersion: ${remoteVersion}, dbVersion: ${dbVersion}`)
|
||||
|
||||
if (StringUtil.isNumber(time) && remoteVersion > dbVersion) {
|
||||
this.randomDelayRequest(remoteVersion)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机延时请求
|
||||
*/
|
||||
private randomDelayRequest(remoteVersion: number) {
|
||||
// 取消之前的延迟任务
|
||||
if (this.mTimeoutId !== null) {
|
||||
clearTimeout(this.mTimeoutId)
|
||||
this.mTimeoutId = null
|
||||
}
|
||||
|
||||
const requestDelay: number = RandomUtil.getRandomInt(0, this.RANDOM_REQUEST_TIME)
|
||||
console.debug(CodeTableManager.TAG, `${this.getTag()} requestDelay ${requestDelay}`)
|
||||
|
||||
// 设置延迟任务
|
||||
this.mTimeoutId = setTimeout(() => {
|
||||
this.mTimeoutId = null
|
||||
// 延迟的这段时间,数据库可能被更新了,因此执行前再判断一次,减少不必要的请求
|
||||
this.getDbVersion().then((dbVersion: number) => {
|
||||
HXLog.d(CodeTableManager.TAG, `before update, remoteVersion: ${remoteVersion}, dbVersion: ${dbVersion}`)
|
||||
if (remoteVersion > dbVersion) {
|
||||
this.update()
|
||||
}
|
||||
})
|
||||
}, requestDelay)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ISubDataHandler } from 'base_mobilepush'
|
||||
import { FunctionAuthorityRequestManager } from '../../functionauthority/FunctionAuthorityRequester'
|
||||
|
||||
interface FunctionAuthorityGroupData {
|
||||
timestamp?: string | number
|
||||
timeStamp?: string | number
|
||||
}
|
||||
|
||||
export class FunctionAuthorityGroupHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = 'futures_custom_authority'
|
||||
|
||||
onReceiveGroupData(data: string, key?: string, isRealPush?: boolean): void {
|
||||
const timestamp = this.parseTimestamp(data)
|
||||
if (timestamp <= 0) {
|
||||
return
|
||||
}
|
||||
FunctionAuthorityRequestManager.requestAuthorityDataForPush(timestamp)
|
||||
}
|
||||
|
||||
private parseTimestamp(data: string | object): number {
|
||||
try {
|
||||
const payload = typeof data === 'string'
|
||||
? JSON.parse(data) as FunctionAuthorityGroupData
|
||||
: data as FunctionAuthorityGroupData
|
||||
return Number(payload.timestamp ?? payload.timeStamp ?? 0)
|
||||
} catch (error) {
|
||||
console.error(`[FunctionAuthorityGroupHandler] 解析权限推送失败: ${JSON.stringify(error)}`)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ISubDataHandler } from 'base_mobilepush'
|
||||
import { HXLog } from 'biz_common'
|
||||
import { GrayTestManager } from 'lib_graytestmanager'
|
||||
|
||||
export class GrayTestPushHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = "futures_new_gray_test_config_upgrade_notice"
|
||||
|
||||
onReceiveGroupData(data: string): void {
|
||||
HXLog.i("GrayTest", `GrayTestPushHandler receive: ${data}`)
|
||||
let verTimestamp: PushTimesTamp = JSON.parse(data) as PushTimesTamp
|
||||
if (verTimestamp && verTimestamp.timestamp > 0) {
|
||||
GrayTestManager.getInstance().dealReceiveMobilePush(verTimestamp.timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface PushTimesTamp {
|
||||
timestamp: number
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { AdsPopupManager } from "@b2c-f/fuhm-ads"
|
||||
import { AdsPopupHost } from "@b2c/first_page"
|
||||
import { ISubDataHandler } from "base_mobilepush"
|
||||
import { HXLog } from "biz_common"
|
||||
|
||||
/**
|
||||
* author : liuqingliang@myhexin.com
|
||||
* time : created on 2026/6/9
|
||||
* desc : 全域运营位-只会推送一次,不会实时推送
|
||||
*/
|
||||
export class QhStrategyAdPagePushHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = 'app_page_info'
|
||||
private static readonly KEY_TIME = "app_6"
|
||||
|
||||
onReceiveGroupData(data: string) {
|
||||
try {
|
||||
const pushData = JSON.parse(data) as Record<string, string>
|
||||
const timeStamp = pushData[QhStrategyAdPagePushHandler.KEY_TIME] ?? ''
|
||||
|
||||
if (timeStamp === AdsPopupManager.pushTimestamp) {
|
||||
return
|
||||
}
|
||||
AdsPopupHost.getInstance().requestPageInfo(timeStamp)
|
||||
} catch (error) {
|
||||
HXLog.e('QhStrategyAdPushHandler', `Failed to process push: ${error.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { AdsManager, ADS_TYPE_INDEX_POPUP, ADS_TYPE_QHTMKTPOP } from "@b2c-f/fuhm-ads"
|
||||
import { AdsPopupHost, FirstPagePopupAdHelper } from "@b2c/first_page"
|
||||
import { ISubDataHandler } from "base_mobilepush"
|
||||
import { HXLog, PreferenceService } from "biz_common"
|
||||
import { HXUserService } from "biz_hxservice"
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* author: ME
|
||||
* desc: 期货策略广告推送处理器,对齐 Android QhStragyAdPushHandler
|
||||
* createTime: 2026/06/01
|
||||
* </pre>
|
||||
*/
|
||||
export class QhStrategyAdPushHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = 'qh_strategy_ad_push'
|
||||
private static readonly PUSH_STRATEGY_AD_KEY = 'push_stragy_ad'
|
||||
private static readonly SP_STRATEGY_AD = "sp_stragy_ad"
|
||||
private static readonly KEY_AD_TYPE = "adtype"
|
||||
private static readonly KEY_AD_IDS = "id"
|
||||
|
||||
onReceiveGroupData(data: string) {
|
||||
try {
|
||||
const pushData = JSON.parse(data) as Record<string, string>
|
||||
const adType = pushData[QhStrategyAdPushHandler.KEY_AD_TYPE] ?? ''
|
||||
const adId = pushData[QhStrategyAdPushHandler.KEY_AD_IDS] ?? ''
|
||||
|
||||
if (!adType || !adId) {
|
||||
HXLog.w('QhStrategyAdPushHandler', 'Invalid push data, missing adType or adId')
|
||||
return
|
||||
}
|
||||
|
||||
const pushKey = `${adType}_${adId}`
|
||||
const userId = HXUserService.getInstance().getUserIdFromLocal()
|
||||
|
||||
const preferenceKey = `${QhStrategyAdPushHandler.PUSH_STRATEGY_AD_KEY}_${userId}`
|
||||
const localPushKey = PreferenceService.getStringValueSync(QhStrategyAdPushHandler.SP_STRATEGY_AD,preferenceKey, '')
|
||||
if (pushKey === localPushKey) {
|
||||
HXLog.d('QhStrategyAdPushHandler', `Already pushed: ${pushKey}`)
|
||||
return
|
||||
}
|
||||
PreferenceService.putVal(QhStrategyAdPushHandler.SP_STRATEGY_AD,preferenceKey, pushKey, true)
|
||||
|
||||
if (adType === ADS_TYPE_INDEX_POPUP) {
|
||||
// 收到策略广告后重置启动弹窗过的标志
|
||||
FirstPagePopupAdHelper.getInstance().isShowedPopupAds = false
|
||||
}
|
||||
|
||||
if (adType === ADS_TYPE_QHTMKTPOP) {
|
||||
// 期货营销悬浮球-处理策略广告推送
|
||||
AdsPopupHost.getInstance().handlePush()
|
||||
}
|
||||
|
||||
AdsManager.refreshAdsAfterPushByAdType(adType, adId)
|
||||
} catch (error) {
|
||||
HXLog.e('QhStrategyAdPushHandler', `Failed to process push: ${error.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ISubDataHandler } from 'base_mobilepush';
|
||||
import { UgcClient } from '../../register/ugcregister/UgcClient';
|
||||
|
||||
/**
|
||||
* 自选分组的数据分组订阅
|
||||
* @author menghuiyuan@myhexin.com
|
||||
*/
|
||||
export class SelfCodeGroupHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = "blockstock"
|
||||
onReceiveGroupData(data: string): void {
|
||||
let version:groupUpdate = JSON.parse(data) as groupUpdate
|
||||
UgcClient.getInstance().onSelfGroupRemoteVersion(version.ver)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface groupUpdate{
|
||||
ver:number
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ISubDataHandler } from 'base_mobilepush';
|
||||
import { UgcClient } from '../../register/ugcregister/UgcClient';
|
||||
import { groupUpdate } from './SelfCodeGroupHandler';
|
||||
/**
|
||||
* 默认自选数据分组订阅
|
||||
* @author zhangshan2@myhexin.com
|
||||
*/
|
||||
export class SelfCodeMainListHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = "futuresselfcode"
|
||||
onReceiveGroupData(data: string): void {
|
||||
let version:groupUpdate = JSON.parse(data) as groupUpdate
|
||||
UgcClient.getInstance().onSelfCodeRemoteVersion(version.ver)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { ISubDataHandler } from 'base_mobilepush'
|
||||
import { WxPaySidPermissionMarker } from 'biz_auth'
|
||||
import { HXLog, PreferenceService } from 'biz_common'
|
||||
import { SidPermissionRefreshClient } from 'biz_auth/src/main/ets/auth/sid/SidPermissionRefreshClient'
|
||||
import { HXUserService } from 'biz_hxservice'
|
||||
|
||||
const TAG = 'SidGroupHandler'
|
||||
|
||||
/**
|
||||
* SID权限更新分组推送数据。
|
||||
* 对应分组key: futures_app_sid_update_notice
|
||||
* timeStamp为服务端下发的更新标记,用于和本地已处理标记比较,变化时主动刷新SID权限。
|
||||
*/
|
||||
interface SidGroupData {
|
||||
timeStamp?: string | number
|
||||
}
|
||||
|
||||
export class SidGroupHandler implements ISubDataHandler {
|
||||
static readonly GROUP_HANDLE_NAME = 'futures_app_sid_update_notice'
|
||||
private static readonly PREFERENCES_NAME = 'sid_update_notice'
|
||||
private static readonly KEY_PREFIX = 'sid_update_'
|
||||
|
||||
onReceiveGroupData(data: string): void {
|
||||
try {
|
||||
// 主站只用timeStamp表达“SID权限有新版本”,客户端不解析具体权限内容。
|
||||
const flag = this.parsePushFlag(data)
|
||||
if (!flag || flag.length === 0) {
|
||||
return
|
||||
}
|
||||
const userId = HXUserService.getInstance().getUserId()
|
||||
// 本地仅记录已成功处理的timeStamp,避免同一条分组推送重复触发主动请求。
|
||||
const savedFlag = PreferenceService.getStringValueSync(
|
||||
SidGroupHandler.PREFERENCES_NAME,
|
||||
SidGroupHandler.KEY_PREFIX + userId,
|
||||
''
|
||||
)
|
||||
if (flag === savedFlag) {
|
||||
return
|
||||
}
|
||||
// 主动请求成功且SID发生变化后,SidPermissionRefreshClient会写入本次flag。
|
||||
WxPaySidPermissionMarker.clear()
|
||||
SidPermissionRefreshClient.requestPermission(true, flag)
|
||||
} catch (error) {
|
||||
HXLog.e(TAG, `handle sid group push failed: ${JSON.stringify(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
private parsePushFlag(data: string | object): string {
|
||||
if (!data) {
|
||||
return ''
|
||||
}
|
||||
let payload: SidGroupData | null = null
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
payload = JSON.parse(data) as SidGroupData
|
||||
} catch (error) {
|
||||
HXLog.e(TAG, `parse sid group push failed: ${JSON.stringify(error)}`)
|
||||
return ''
|
||||
}
|
||||
} else {
|
||||
// 分组分发链路可能已把payload解析为对象,这里兼容两种输入形态。
|
||||
payload = data as SidGroupData
|
||||
}
|
||||
const flag = payload?.timeStamp
|
||||
if (flag === undefined || flag === null) {
|
||||
return ''
|
||||
}
|
||||
return flag.toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { AppUtil } from "@pura/harmony-utils";
|
||||
import { HXLog } from "biz_common";
|
||||
import { CodeTableManager } from "lib_codetablemanager";
|
||||
import { BaseStorageHandler } from "./BaseStorageHandler";
|
||||
|
||||
export class StockGroupHandler extends BaseStorageHandler {
|
||||
getTag(): string {
|
||||
return 'StockGroupHandler'
|
||||
}
|
||||
|
||||
getTimeKey(): string {
|
||||
return 'time'
|
||||
}
|
||||
|
||||
update(): void {
|
||||
HXLog.d(CodeTableManager.TAG, `${this.getTag()} update`)
|
||||
CodeTableManager.get().requestStock(AppUtil.getContext(), this.getTag())
|
||||
}
|
||||
|
||||
async getDbVersion(): Promise<number> {
|
||||
const version = await CodeTableManager.get().getStockInfoDbVersion()
|
||||
HXLog.d(CodeTableManager.TAG, `${this.getTag()} getDbVersion ${version}`)
|
||||
return version
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { AppUtil } from "@pura/harmony-utils";
|
||||
import { HXLog } from "biz_common";
|
||||
import { CodeTableManager } from "lib_codetablemanager";
|
||||
import { BaseStorageHandler } from "./BaseStorageHandler";
|
||||
|
||||
export class VarietyGroupHandler extends BaseStorageHandler {
|
||||
getTag(): string {
|
||||
return 'VarietyGroupHandler'
|
||||
}
|
||||
|
||||
getTimeKey(): string {
|
||||
return 'timestamp'
|
||||
}
|
||||
|
||||
update(): void {
|
||||
HXLog.d(CodeTableManager.TAG, `${this.getTag()} update`)
|
||||
CodeTableManager.get().requestVariety(AppUtil.getContext(), this.getTag())
|
||||
}
|
||||
|
||||
async getDbVersion(): Promise<number> {
|
||||
const version = await CodeTableManager.get().getVarietyInfoDbVersion()
|
||||
HXLog.d(CodeTableManager.TAG, `${this.getTag()} getDbVersion ${version}`)
|
||||
return version
|
||||
}
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 197 B |
|
After Width: | Height: | Size: 513 B |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 316 B |
|
After Width: | Height: | Size: 1011 B |
|
After Width: | Height: | Size: 948 B |
|
After Width: | Height: | Size: 930 B |
|
After Width: | Height: | Size: 950 B |
|
After Width: | Height: | Size: 930 B |
|
After Width: | Height: | Size: 919 B |
|
After Width: | Height: | Size: 698 B |
|
After Width: | Height: | Size: 693 B |
|
After Width: | Height: | Size: 435 B |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 439 B |
|
After Width: | Height: | Size: 389 B |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,103 @@
|
||||
|
||||
import {
|
||||
AdsManager,
|
||||
AdRuntimeConfig
|
||||
} from '@b2c-f/fuhm-ads'
|
||||
import { AppConfigManager, HXLog, ScreenUtil } from 'biz_common'
|
||||
import { CookieManager, HXUserService, KaihuFlagManager, NewUserManager, ServerTimeProvider,
|
||||
UserSourceManager } from 'biz_hxservice'
|
||||
import { AppUtil } from '@pura/harmony-utils'
|
||||
|
||||
const TAG = 'AdsInitializer'
|
||||
|
||||
/**
|
||||
* FuHmAds 运行时配置
|
||||
* 提供广告组件所需的上下文、用户信息和业务能力
|
||||
*/
|
||||
class AdsRuntimeConfig implements AdRuntimeConfig {
|
||||
|
||||
|
||||
getContext(): Object | null {
|
||||
return AppUtil.getContext()
|
||||
}
|
||||
|
||||
getScreenWidth(): string {
|
||||
return ScreenUtil.get().getScreenWidth().toString()
|
||||
}
|
||||
|
||||
getScreenHeight(): string {
|
||||
return ScreenUtil.get().getScreenHeight().toString()
|
||||
}
|
||||
|
||||
getInnerVersion(): string {
|
||||
return AppConfigManager.getInstance().getConfigProvider()?.getInnerVersionMiddleNumberStr()??''
|
||||
}
|
||||
|
||||
getComeFrom(): string {
|
||||
return KaihuFlagManager.getInstance().getKaihuFlag()
|
||||
}
|
||||
|
||||
getTrade(): string {
|
||||
let userSource = UserSourceManager.getInstance().getUserSource()
|
||||
if (UserSourceManager.NO_SOURCE != userSource) {
|
||||
return userSource
|
||||
}else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
getDeviceCode(): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
getUserId(): string {
|
||||
const userId = HXUserService.getInstance().getUserId()
|
||||
return userId || ''
|
||||
}
|
||||
|
||||
getUserIdFromPassport(): string {
|
||||
const userId = HXUserService.getInstance().getUserIdFromLocal()
|
||||
return userId || ''
|
||||
}
|
||||
|
||||
isNewUser(): boolean {
|
||||
return NewUserManager.getInstance().isNewUser()
|
||||
}
|
||||
|
||||
getChannelId(): string {
|
||||
return NewUserManager.getChannelId()
|
||||
}
|
||||
|
||||
getCurrentTimeMillis(): number {
|
||||
return ServerTimeProvider.getInstance().getServerTimeMillis()
|
||||
}
|
||||
|
||||
|
||||
getUserAgent(): string {
|
||||
return AppConfigManager.getInstance().getUA()
|
||||
}
|
||||
|
||||
getCookie(): string {
|
||||
return CookieManager.getInstance().getCookieSync()
|
||||
}
|
||||
|
||||
|
||||
uploadElk(message: string): void {
|
||||
HXLog.w(TAG, `Ads elk ${message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告模块初始化器
|
||||
*/
|
||||
export class AdsInitializer {
|
||||
/**
|
||||
* 初始化广告模块
|
||||
*/
|
||||
static initAds(): void {
|
||||
HXLog.i(TAG, 'initAds start')
|
||||
const adsConfig = new AdsRuntimeConfig()
|
||||
AdsManager.initAdContext(adsConfig)
|
||||
HXLog.i(TAG, 'initAds done')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { HXLog } from "biz_common"
|
||||
import { CloudStoreLogManager, ICloudStoreLogger } from "lib_cloud_store"
|
||||
import { ElkService, ElkUploadMessage, ElkUploadMessageBuilder } from '@b2c-m/ohos_elk_logger_upload'
|
||||
import { HXUserService } from 'biz_hxservice'
|
||||
|
||||
const CLOUD_STORE = "cloud_store"
|
||||
|
||||
/**
|
||||
* 云存储错误消息构建类
|
||||
*/
|
||||
class CloudStoreErrMessageBuilder extends ElkUploadMessageBuilder {
|
||||
constructor(bizLevel: string) {
|
||||
super(Date.now(), CLOUD_STORE, bizLevel)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传云存储错误信息到 ELK
|
||||
* @param errorMessage 错误信息
|
||||
*/
|
||||
function uploadCloudStoreError(error: string) {
|
||||
const errorMessageBuilder = new CloudStoreErrMessageBuilder('e')
|
||||
errorMessageBuilder.userid(HXUserService.getInstance().getUserId())
|
||||
errorMessageBuilder.messageKey('CloudStore')
|
||||
errorMessageBuilder.messageValue(error)
|
||||
const message = new ElkUploadMessage(errorMessageBuilder)
|
||||
ElkService.getInstance().pushBusinessLog(message, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* HXLog 适配器
|
||||
* 将 lib_cloud_store 的日志桥接到 HXLog
|
||||
*/
|
||||
class HXLogAdapter implements ICloudStoreLogger {
|
||||
debug(module: string, message: string): void {
|
||||
HXLog.d(module, message)
|
||||
}
|
||||
|
||||
info(module: string, message: string): void {
|
||||
HXLog.i(module, message)
|
||||
}
|
||||
|
||||
warn(module: string, message: string): void {
|
||||
HXLog.w(module, message)
|
||||
}
|
||||
|
||||
error(module: string, message: string): void {
|
||||
HXLog.e(module, message)
|
||||
uploadCloudStoreError(`[${module}] ${message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 云存储日志初始化器
|
||||
*/
|
||||
export class CloudStoreLogInitializor {
|
||||
/**
|
||||
* 初始化云存储日志
|
||||
*/
|
||||
static initCloudStoreLog() {
|
||||
const hxLogAdapter = new HXLogAdapter()
|
||||
CloudStoreLogManager.getInstance().setLogger(hxLogAdapter)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import {AppInfoConfig, IAppConfig, IDeviceConfig } from 'biz_common';
|
||||
export class AppConfigProvider implements IAppConfig {
|
||||
|
||||
private static readonly BEGIN_INDEX = 5
|
||||
getBundleName(): string {
|
||||
return AppInfoConfig.getBundleName();
|
||||
}
|
||||
|
||||
|
||||
getInnerVersionFull(): string {
|
||||
return AppInfoConfig.APPLET_INNER_VERSION_FULL;
|
||||
}
|
||||
|
||||
getInnerVersionMiddle(): string {
|
||||
return AppInfoConfig.APPLET_INNER_VERSION.substring(AppConfigProvider.BEGIN_INDEX -1)
|
||||
}
|
||||
|
||||
getInnerVersionMiddleNumberStr(): string {
|
||||
return AppInfoConfig.APPLET_INNER_VERSION.substring(AppConfigProvider.BEGIN_INDEX).replaceAll(".","")
|
||||
}
|
||||
|
||||
getOuterVersion(): string {
|
||||
|
||||
return AppInfoConfig.getVersionName();
|
||||
}
|
||||
|
||||
getVersionCode(): string {
|
||||
return AppInfoConfig.getVersionCode();
|
||||
}
|
||||
|
||||
getAppPlatform(): string {
|
||||
return AppInfoConfig.APP_PLATFORM;
|
||||
}
|
||||
|
||||
getAppSign(): string {
|
||||
return AppInfoConfig.APP_FOR;
|
||||
}
|
||||
|
||||
getProgramId(): string {
|
||||
return AppInfoConfig.APP_PROGID;
|
||||
}
|
||||
|
||||
getChannelId(): string {
|
||||
return AppInfoConfig.APP_CHANNEL_ID;
|
||||
}
|
||||
|
||||
getSourceId(): string {
|
||||
return AppInfoConfig.APP_CHANNEL_ID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class DeviceConfigProvider implements IDeviceConfig {
|
||||
getDeviceWidth(): number {
|
||||
return 720;
|
||||
}
|
||||
|
||||
getDeviceHeight(): number {
|
||||
return 1440;
|
||||
}
|
||||
|
||||
getDensity(): number {
|
||||
return 3.0;
|
||||
}
|
||||
|
||||
getDeviceType(): string {
|
||||
return "android";
|
||||
}
|
||||
|
||||
getIMEI(): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getIMSI(): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getMACAddress(): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { AppConfigManager } from 'biz_common'
|
||||
import { AppConfigProvider, DeviceConfigProvider } from './ConfigImpl';
|
||||
|
||||
/**
|
||||
* 宿主App初始化配置信息
|
||||
*/
|
||||
export class ConfigInitializer {
|
||||
|
||||
static init() {
|
||||
AppConfigManager.getInstance().registerConfigProvider(new AppConfigProvider());
|
||||
AppConfigManager.getInstance().registerDeviceConfigProvider(new DeviceConfigProvider());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { AppConfigManager, AppInfoConfig, } from "biz_common"
|
||||
import { DebugToolUtil } from "biz_debug"
|
||||
import { CookieManager, HXUserService, ServerTimeProvider } from "biz_hxservice"
|
||||
import { GrayTestManager, GrayTestInjectInterface } from "lib_graytestmanager"
|
||||
|
||||
export class GrayTestInitializor {
|
||||
/**
|
||||
* 初始化灰度配置
|
||||
*/
|
||||
static initGrayTest() {
|
||||
const injectParams = new GrayTestInjectInterface()
|
||||
injectParams.getUserId = () => {
|
||||
return HXUserService.getInstance().getUserIdFromLocal()
|
||||
}
|
||||
injectParams.getCookie = () => {
|
||||
return CookieManager.getInstance().getCookieSync()
|
||||
}
|
||||
injectParams.getUserAgent = () => {
|
||||
return AppConfigManager.getInstance().getUA()
|
||||
}
|
||||
injectParams.getAppVersion = () => {
|
||||
return AppInfoConfig.getCurrentInnerVersionNumber().toString()
|
||||
}
|
||||
injectParams.getServerTimestamp = () => {
|
||||
return ServerTimeProvider.getInstance().getServerTimeMillis()
|
||||
}
|
||||
injectParams.getDebugMode = () => {
|
||||
return DebugToolUtil.isDebugMode()
|
||||
}
|
||||
|
||||
GrayTestManager.getInstance().config(injectParams)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import { ContractChooseManager, ContractDirectionType, ContractInfo, ContractMainType,
|
||||
FavoriteGroup } from "@b2c-f/contractchoose";
|
||||
import { ContractBeanFactory, ContractCode, MarketId, PositionCenter } from "@b2c-f/futures_ohos_trade_sdk";
|
||||
import { Context } from "@kit.AbilityKit";
|
||||
import { Markets } from "biz_common";
|
||||
import { InactiveContractManager } from "biz_market";
|
||||
import { AccountAnalysisTradeLoginListener,
|
||||
AccountProvider,
|
||||
FuturesAccountAnalysisHttpProvider, FuturesBillParseModuleHttpProvider,
|
||||
TradeUtils, TradeReportDateManager } from "biz_trade";
|
||||
import { TradeDailyDataManager, TradeDailyManager, TradeHighFrequentManager, TradeLoginOnlineManager,
|
||||
TradeMsgUniqueIdManager, TradeSettlementBillManager } from "biz_trade";
|
||||
import { TradeType } from "biz_trade/src/main/ets/trade/manager/TradeCustomTabManager";
|
||||
import { CodeTableManager, StockRegularUtils } from "lib_codetablemanager";
|
||||
import { loadCustomGroups } from "service_watchlist";
|
||||
import { TradeDailyConvertManager } from "../NotificationExtension/TradeDaily/TradeDailyConvertManager";
|
||||
import { TradeDailyPushDataManager } from "../NotificationExtension/TradeDaily/TradeDailyPushDataManager";
|
||||
|
||||
export class TradeInitializor {
|
||||
/**
|
||||
* 初始化高频交易管理器
|
||||
*/
|
||||
static async init(context: Context) {
|
||||
TradeInitializor.initContractChoose()
|
||||
|
||||
TradeHighFrequentManager.getInstance().initManager(context)
|
||||
TradeLoginOnlineManager.getInstance().initManager(context)
|
||||
AccountAnalysisTradeLoginListener.getInstance().start()
|
||||
TradeSettlementBillManager.getInstance()
|
||||
FuturesAccountAnalysisHttpProvider.initializeHttpProvider()
|
||||
FuturesBillParseModuleHttpProvider.initializeHttpProvider()
|
||||
TradeSettlementBillManager.getInstance()
|
||||
|
||||
// 交易日志
|
||||
TradeDailyManager.getInstance()
|
||||
await TradeMsgUniqueIdManager.getInstance().initDB(context)
|
||||
await TradeDailyDataManager.getInstance().init(context)
|
||||
await TradeDailyPushDataManager.getInstance().initDB(context)
|
||||
await TradeDailyConvertManager.getInstance().registerForeground(context)
|
||||
TradeReportDateManager.getInstance().updateTradingDate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化合约选择方法注入
|
||||
*/
|
||||
static initContractChoose() {
|
||||
ContractChooseManager.getInstance().setIsOptionContractProvider((showCode, marketId) => {
|
||||
return StockRegularUtils.isOption(showCode)
|
||||
})
|
||||
|
||||
ContractChooseManager.getInstance().setIsMainLinkContractProvider(async (showCode, marketId) => {
|
||||
let contractBean = await ContractBeanFactory.getInstance()
|
||||
.buildContract(new ContractCode(showCode), new MarketId(marketId))
|
||||
return TradeUtils.isCanTradeMainLink(contractBean)
|
||||
})
|
||||
|
||||
ContractChooseManager.getInstance().setIsMainContractProvider(async (showCode, marketId) => {
|
||||
return TradeUtils.isZhuliCode(showCode)
|
||||
})
|
||||
|
||||
ContractChooseManager.getInstance().setPositionProvider(async () => {
|
||||
const accountIdentify = AccountProvider.getInstance().getCurrentAccount()?.accountIdentify()
|
||||
if (!accountIdentify) {
|
||||
return []
|
||||
}
|
||||
const positions = PositionCenter.getInstance().getAllPositionSummary(accountIdentify)?.getPositionInfos()
|
||||
return positions?.flatMap((position) => {
|
||||
const stockCode = CodeTableManager.get().getStockInfoByShowCodeSync(position.contractCode.code, position.marketId.code)?.stockCode ?? ''
|
||||
return new ContractInfo(
|
||||
position.contractCode.code,
|
||||
position.marketId.code,
|
||||
stockCode,
|
||||
position.contractName,
|
||||
position.isOptionPosition,
|
||||
ContractMainType.NONE,
|
||||
position.isLongPosition() ? ContractDirectionType.BUY : ContractDirectionType.SELL
|
||||
)
|
||||
}) ?? []
|
||||
})
|
||||
|
||||
ContractChooseManager.getInstance().setFavoritesProvider(async () => {
|
||||
const favorites = await TradeUtils.getSelfContractForTrade(TradeType.NONE)
|
||||
return favorites.flatMap((favorite) => {
|
||||
if (favorite.contractCode && favorite.marketId) {
|
||||
const stockCode = CodeTableManager.get().getStockInfoByShowCodeSync(favorite.contractCode.code, favorite.marketId.code)?.stockCode ?? ''
|
||||
return new ContractInfo(
|
||||
favorite.contractCode.code,
|
||||
favorite.marketId.code,
|
||||
stockCode,
|
||||
favorite.contractName ?? '',
|
||||
favorite.isOptionContract(),
|
||||
TradeUtils.isZhuliCode(stockCode) ? ContractMainType.MAIN : ContractMainType.NOT_MAIN,
|
||||
ContractDirectionType.NONE,
|
||||
InactiveContractManager.get().isInactiveContract(stockCode) && !Markets.isArbitrageMarket(favorite.marketId.code)
|
||||
)
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
})
|
||||
|
||||
ContractChooseManager.getInstance().setFavoritesGroupProvider(async () => {
|
||||
const groups = loadCustomGroups(true)
|
||||
const favoriteGroup = new FavoriteGroup()
|
||||
for (const group of groups) {
|
||||
const contracts = group.stocks
|
||||
if (contracts.length > 0) {
|
||||
favoriteGroup.addGroup(group.name, contracts.flatMap((contract) => {
|
||||
const stockInfo = CodeTableManager.get().getStockInfoSync(contract.code, contract.market)
|
||||
const showCode = stockInfo?.showCode
|
||||
const stockName = stockInfo?.stockName
|
||||
return new ContractInfo(
|
||||
showCode,
|
||||
contract.market,
|
||||
contract.code,
|
||||
contract.name ?? stockName,
|
||||
StockRegularUtils.isOption(showCode),
|
||||
TradeUtils.isZhuliCode(contract.code) ? ContractMainType.MAIN : ContractMainType.NOT_MAIN,
|
||||
ContractDirectionType.NONE,
|
||||
InactiveContractManager.get().isInactiveContract(contract.code) && !Markets.isArbitrageMarket(contract.market)
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
return favoriteGroup
|
||||
})
|
||||
|
||||
ContractChooseManager.getInstance().setMainContractProvider(async (showCode, marketId) => {
|
||||
const stockCode = CodeTableManager.get().getStockInfoByShowCodeSync(showCode, marketId)?.stockCode ?? ''
|
||||
const contract = await TradeUtils.changeMainLinkToMainContract2(stockCode, marketId)
|
||||
if (contract) {
|
||||
return new ContractInfo(
|
||||
contract.contractCode?.code,
|
||||
contract.marketId?.code,
|
||||
stockCode,
|
||||
contract.contractName ?? '',
|
||||
StockRegularUtils.isOption(showCode),
|
||||
ContractMainType.NONE,
|
||||
ContractDirectionType.NONE
|
||||
)
|
||||
}
|
||||
return new ContractInfo()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
import { HXEventMonitorPlugin } from "@kernel/app_monitor_event";
|
||||
import {
|
||||
HXMonitor,
|
||||
HXMonitorLogger,
|
||||
IElkService,
|
||||
IMonitorAppConfig,
|
||||
IMonitorOnStartCallback
|
||||
} from "@kernel/app_monitor_lib";
|
||||
import { HXPerformancePlugin } from "@kernel/app_monitor_performance";
|
||||
import { HXWebMonitorPlugin } from '@kernel/app_monitor_web';
|
||||
import { HXUserService } from "biz_hxservice";
|
||||
import { ElkService, ElkUploadMessage, ElkUploadMessageBuilder } from "service_monitor";
|
||||
import {
|
||||
AuthLogMonitor,
|
||||
DefaultLogParamConfig,
|
||||
HXTcpMonitorPlugin,
|
||||
LogParamsConfig,
|
||||
TcpMonitorConfig
|
||||
} from "@kernel/app_monitor_tcp";
|
||||
import { HXAuthLogMonitor } from "biz_auth";
|
||||
import { HXHttpMonitorPlugin } from "@kernel/app_monitor_http";
|
||||
import { HXCrashMonitorPlugin, ICrashMonitorConfig } from "@kernel/app_monitor_crash";
|
||||
import { router } from "@kit.ArkUI";
|
||||
import { GIT_COMMIT } from "biz_common/src/main/ets/utils/debug/GitBuildConfig";
|
||||
import { AppConfigManager, getConnectionManager } from "biz_common";
|
||||
import { HxCBASAgentProvider } from "@b2c-spi/hx_cbas_api";
|
||||
import {
|
||||
AppContextLike,
|
||||
type LeakWatcherConfig,
|
||||
initializeWatcherFromPreference
|
||||
} from '@kernel/ohos_leak_watcher';
|
||||
import BuildProfile from "BuildProfile";
|
||||
|
||||
const APP_ID = "mobile-archive-am-qihuo-harmony"
|
||||
const DEBUG_VERSION = "FUHM037.08.999.10.32"
|
||||
|
||||
export class APMHelper {
|
||||
static clientIp: string = "";
|
||||
|
||||
/**
|
||||
* 判断是否为测试版本(debug 或 forTest)
|
||||
*/
|
||||
static isDebugOrTestBuild(): boolean {
|
||||
const modeName = BuildProfile.BUILD_MODE_NAME.toString()
|
||||
const targetName = BuildProfile.TARGET_NAME.toString()
|
||||
return modeName === 'debug' || targetName === 'forTest'
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化APM
|
||||
* @param context
|
||||
*/
|
||||
static init(context: Context) {
|
||||
//目前先只在debug包下打印日志
|
||||
const modeName: string = BuildProfile.BUILD_MODE_NAME.toString()
|
||||
HXMonitorLogger._logOpen = modeName === 'debug'
|
||||
|
||||
HXMonitor.builder(context)
|
||||
.setAppId(APP_ID)
|
||||
.hostParameter(new HXMonitorAppConfig())
|
||||
.setElkService(new APMElkService())
|
||||
.plugin(new HXPerformancePlugin())
|
||||
.plugin(new HXEventMonitorPlugin())
|
||||
.plugin(new HXTcpMonitorPlugin(new HXTcpMonitorConfig()))
|
||||
.plugin(new HXWebMonitorPlugin())
|
||||
.plugin(new HXHttpMonitorPlugin())
|
||||
.plugin(new HXCrashMonitorPlugin(new HXCrashMonitorConfig()))
|
||||
.build()
|
||||
.startAllPlugins(new APMMonitorOnStartCallback())
|
||||
APMHelper.initLeakWatcher(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化内存泄漏监控
|
||||
* @param context 应用上下文
|
||||
*/
|
||||
private static initLeakWatcher(context: Context) {
|
||||
let appContext: AppContextLike = {
|
||||
filesDir: context.getApplicationContext().filesDir || ''
|
||||
}
|
||||
let config: LeakWatcherConfig = {
|
||||
enabled: true,
|
||||
appContext: appContext,
|
||||
baseDir: `${appContext.filesDir}/jsleak_watcher`,
|
||||
leakObjectTypes: [],
|
||||
resourceWatcher: {
|
||||
enabled: true,
|
||||
row: 1,
|
||||
size: 10240,
|
||||
timeOut: 1
|
||||
},
|
||||
retention: { maxArtifacts: 50, maxDays: 7, maxTotalBytes: 536870912 },
|
||||
upload: {
|
||||
enabled: false,
|
||||
url: '',
|
||||
method: 'POST',
|
||||
headers: []
|
||||
}
|
||||
};
|
||||
initializeWatcherFromPreference(config);
|
||||
}
|
||||
}
|
||||
|
||||
class APMMonitorOnStartCallback implements IMonitorOnStartCallback {
|
||||
onStarted(code: number): void {
|
||||
//暂时不做操作
|
||||
HXMonitorLogger.d("APMHelper", `APM started:${code}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* apm异常日志回调
|
||||
*/
|
||||
class APMElkService implements IElkService {
|
||||
pushBusinessLog(logType: string, bt: string, message: string | null): void;
|
||||
pushBusinessLog(logType: string, bt: string, message: string | null, extraData: object | null): void;
|
||||
pushBusinessLog(logType: string, bt: string, message: string | null, extraData?: object | null): void {
|
||||
try {
|
||||
const builder = new APMElkBuilder(logType)
|
||||
builder.userid(HXUserService.getInstance().getUserId())
|
||||
builder.messageKey(bt)
|
||||
builder.messageValue(message || "")
|
||||
if (extraData) {
|
||||
builder.messageExt(JSON.stringify(extraData))
|
||||
}
|
||||
const elkMessage = new ElkUploadMessage(builder)
|
||||
ElkService.getInstance().pushBusinessLog(elkMessage)
|
||||
} catch (e) {
|
||||
console.error("APMElkService upload message fail", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class APMElkBuilder extends ElkUploadMessageBuilder {
|
||||
constructor(biz_level: string) {
|
||||
super(Date.now(), "apm", biz_level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 崩溃采集配置
|
||||
*/
|
||||
class HXCrashMonitorConfig implements ICrashMonitorConfig {
|
||||
provideUserName(): string {
|
||||
return HXUserService.getInstance().getUserName();
|
||||
}
|
||||
|
||||
provideBuildType(): string {
|
||||
return BuildProfile.BUILD_MODE_NAME.toString()
|
||||
}
|
||||
|
||||
provideAppName(): string {
|
||||
return getContext().applicationInfo.name
|
||||
}
|
||||
|
||||
async provideCBASInfo(): Promise<string> {
|
||||
const result = await HxCBASAgentProvider.getInstance().getRecentCBASData()
|
||||
return result || "none"
|
||||
}
|
||||
|
||||
provideCurrentPageId(): string {
|
||||
return router.getState().name
|
||||
}
|
||||
|
||||
provideGitVersion(): string {
|
||||
return GIT_COMMIT
|
||||
}
|
||||
|
||||
providePackageName(): string {
|
||||
return getContext().applicationInfo.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* APM配置
|
||||
*/
|
||||
class HXMonitorAppConfig extends IMonitorAppConfig {
|
||||
getUserId(): string | null {
|
||||
return HXUserService.getInstance().getUserIdFromLocal()
|
||||
}
|
||||
|
||||
getDeviceCode(): string | null {
|
||||
//deviceInfo.ODID 目前华为获取设备id的api已经不可靠,现改为userid。
|
||||
return this.getUserId()
|
||||
}
|
||||
|
||||
getVersion(): string {
|
||||
if (APMHelper.isDebugOrTestBuild()) {
|
||||
return DEBUG_VERSION
|
||||
}
|
||||
return AppConfigManager.getInstance().getConfigProvider()?.getInnerVersionFull()??DEBUG_VERSION
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tcp监控配置
|
||||
*/
|
||||
class HXTcpMonitorConfig extends TcpMonitorConfig {
|
||||
getLogParamConfig(): LogParamsConfig {
|
||||
return new HXTcpLogParamsConfig()
|
||||
}
|
||||
|
||||
getAuthLogMonitor(): AuthLogMonitor {
|
||||
return HXAuthLogMonitor.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
class HXTcpLogParamsConfig extends DefaultLogParamConfig {
|
||||
getAppId(): string | null {
|
||||
return APP_ID
|
||||
}
|
||||
|
||||
getAppVersion(): string | null {
|
||||
if (APMHelper.isDebugOrTestBuild()) {
|
||||
return DEBUG_VERSION
|
||||
}
|
||||
return AppConfigManager.getInstance().getConfigProvider()?.getInnerVersionFull()??DEBUG_VERSION;
|
||||
}
|
||||
|
||||
getPlatform(): string | null {
|
||||
return AppConfigManager.getInstance().getConfigProvider()?.getAppPlatform()??APP_ID
|
||||
}
|
||||
|
||||
getServerIp(): string | null {
|
||||
return getConnectionManager()?.getConnectedIp() || ""
|
||||
}
|
||||
|
||||
getUid(): string | null {
|
||||
return HXUserService.getInstance().getUserIdFromLocal()
|
||||
}
|
||||
|
||||
getCip(): string | null {
|
||||
return APMHelper.clientIp
|
||||
}
|
||||
|
||||
isUserVIP(): boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import hiAppEvent from '@ohos.hiviewdfx.hiAppEvent';
|
||||
import { HXLog } from 'biz_common/src/main/ets/logger/HXLog';
|
||||
import { HXUserService } from 'biz_hxservice';
|
||||
import { ElkService, ElkUploadMessage, ElkUploadMessageBuilder } from 'service_monitor';
|
||||
|
||||
const TAG: string = 'eventSubscription';
|
||||
|
||||
export function eventSubscription() {
|
||||
hiAppEvent.addWatcher({
|
||||
name: "futures",
|
||||
appEventFilters: [//APP_CRASH, APP_FREEZE事件将在下次启动时获取
|
||||
{
|
||||
domain: hiAppEvent.domain.OS,
|
||||
names: [hiAppEvent.event.APP_CRASH, hiAppEvent.event.APP_FREEZE, hiAppEvent.event.MAIN_THREAD_JANK,
|
||||
hiAppEvent.event.CPU_USAGE_HIGH, hiAppEvent.event.RESOURCE_OVERLIMIT, hiAppEvent.event.ADDRESS_SANITIZER]
|
||||
},
|
||||
],
|
||||
onReceive: async (domain: string, appEventGroups: Array<hiAppEvent.AppEventGroup>) => {
|
||||
HXLog.i(TAG, `HiAppEvent onReceive: domain=${domain}`)
|
||||
setTimeout(() => {
|
||||
scheduleHiAppEventUpload(appEventGroups)
|
||||
}, 1000)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function scheduleHiAppEventUpload(appEventGroups: Array<hiAppEvent.AppEventGroup>) {
|
||||
try {
|
||||
let uploadMessage: Array<HiAppEventUploadInfo> = getFaultMessage(appEventGroups)
|
||||
if (uploadMessage.length > 0) {
|
||||
uploadMessage.forEach((message: HiAppEventUploadInfo, index: number) => {
|
||||
uploadAppEventElkMessage("e", message, true)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
const error : string = ' Error while scheduleHiAppEventUpload, the appEventGroups is ' + JSON.stringify(appEventGroups)
|
||||
HXLog.e(TAG, error)
|
||||
uploadAppEventElkMessage('e',{message_key: 'scheduleHiAppEventUpload', message_value:error},true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getFaultMessage(appEventGroups: Array<hiAppEvent.AppEventGroup>) {
|
||||
const messageArr: Array<HiAppEventUploadInfo> = []
|
||||
if (appEventGroups && appEventGroups.length > 0) {
|
||||
// 遍历事件组
|
||||
appEventGroups.forEach((eventGroup: hiAppEvent.AppEventGroup) => {
|
||||
// 遍历事件对象集合
|
||||
eventGroup.appEventInfos.forEach((eventInfo: hiAppEvent.AppEventInfo) => {
|
||||
let message: string = '';
|
||||
message += `HiAppEvent eventInfo.domain=${eventInfo.domain}\n` // 事件领域
|
||||
+ `HiAppEvent eventInfo.name=${eventInfo.name}\n` // 事件名称
|
||||
+ `HiAppEvent eventInfo.eventType=${eventInfo.eventType}\n` // 事件名称
|
||||
+ `HiAppEvent eventInfo.params.time=${eventInfo.params['time']}\n` // 事件发生的时间
|
||||
+ `HiAppEvent eventInfo.params.crash_type=${eventInfo.params['crash_type']}\n`
|
||||
+ `HiAppEvent eventInfo.params.foreground=${eventInfo.params['foreground']}\n`
|
||||
+ `HiAppEvent eventInfo.params.bundle_version=${eventInfo.params['bundle_version']}\n`
|
||||
+ `HiAppEvent eventInfo.params.bundle_name=${eventInfo.params['bundle_name']}\n`
|
||||
+ `HiAppEvent eventInfo.params.exception=${JSON.stringify(eventInfo.params['exception'])}\n`
|
||||
+ `HiAppEvent eventInfo.params.hilog.size=${eventInfo.params['hilog'].length}\n`;
|
||||
const uploadInfo: HiAppEventUploadInfo = {
|
||||
message_key: eventInfo.domain,
|
||||
message_value: eventInfo.name,
|
||||
message_ext: message
|
||||
}
|
||||
messageArr.push(uploadInfo)
|
||||
})
|
||||
})
|
||||
}
|
||||
return messageArr
|
||||
}
|
||||
|
||||
function uploadAppEventElkMessage(biz_level: string, info: HiAppEventUploadInfo, isInstant: boolean) {
|
||||
const messageBuilder = new HiAppEventElkBuilder(biz_level)
|
||||
messageBuilder.userid(HXUserService.getInstance().getUserId())
|
||||
if (info) {
|
||||
messageBuilder.messageKey(info?.message_key ? info.message_key : '')
|
||||
messageBuilder.messageValue(info?.message_value ? info.message_value : '')
|
||||
messageBuilder.messageExt(info?.message_ext ? info.message_ext : '')
|
||||
}
|
||||
const message = new ElkUploadMessage(messageBuilder)
|
||||
ElkService.getInstance().pushBusinessLog(message, isInstant)
|
||||
}
|
||||
|
||||
|
||||
interface HiAppEventUploadInfo {
|
||||
message_key?: string,
|
||||
message_value?: string,
|
||||
message_ext?: string
|
||||
}
|
||||
|
||||
class HiAppEventElkBuilder extends ElkUploadMessageBuilder {
|
||||
constructor(biz_level: string = 'e') {
|
||||
super(Date.now(), "HiAppEvent", biz_level);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { HXUserService } from 'biz_hxservice';
|
||||
import { ElkService, ElkUploadMessage, ElkUploadMessageBuilder } from 'service_monitor';
|
||||
|
||||
export class EntryMonitor {
|
||||
private static instance: EntryMonitor;
|
||||
public static getInstance() : EntryMonitor{
|
||||
if(!EntryMonitor.instance) {
|
||||
EntryMonitor.instance = new EntryMonitor()
|
||||
}
|
||||
return EntryMonitor.instance
|
||||
}
|
||||
public UploadElkMessage(biz_level:string,info: EntryUploadInfo, isInstant:boolean) {
|
||||
const messageBuilder = new EntryElkBuilder(biz_level)
|
||||
messageBuilder.userid(HXUserService.getInstance().getUserId())
|
||||
if(info) {
|
||||
messageBuilder.messageKey(info?.message_key ? info.message_key : '' )
|
||||
messageBuilder.messageValue(info?.message_value ? info.message_value : '')
|
||||
messageBuilder.messageExt(info?.message_ext ? info.message_ext : '')
|
||||
}
|
||||
const message = new ElkUploadMessage(messageBuilder)
|
||||
|
||||
ElkService.getInstance().pushBusinessLog(message,isInstant)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function buildEntryUploadBasicInfo(message:string,caller:string) {
|
||||
let info : EntryUploadInfo = {
|
||||
message_key: caller,
|
||||
message_value: `${caller} upload message ${message} `,
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
export interface EntryUploadInfo {
|
||||
message_key?:string,
|
||||
message_value?:string,
|
||||
message_ext?:string
|
||||
}
|
||||
|
||||
|
||||
class EntryElkBuilder extends ElkUploadMessageBuilder {
|
||||
constructor(biz_level: string) {
|
||||
super(Date.now(), "entry", biz_level);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { PassportManager } from "biz_auth/src/main/ets/auth/PassportManager";
|
||||
import { CookieManager, HXUserService } from "biz_hxservice";
|
||||
import { SecurityMonitorProxy } from "lib_apm_security";
|
||||
import { bundleManager } from "@kit.AbilityKit";
|
||||
import { AppConfigManager, AppInfoConfig } from "biz_common";
|
||||
import { StringBuilder } from "hxutil";
|
||||
import { StrUtil } from "@pura/harmony-utils";
|
||||
|
||||
/**
|
||||
* author:zhangshan2
|
||||
* created on 2025/1/3
|
||||
* desc:
|
||||
*/
|
||||
export class SecurityMonitorInit{
|
||||
private static instance:SecurityMonitorInit
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
static getInstance(){
|
||||
if (!SecurityMonitorInit.instance) {
|
||||
SecurityMonitorInit.instance = new SecurityMonitorInit()
|
||||
}
|
||||
return SecurityMonitorInit.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* 崩溃保护初始化
|
||||
*/
|
||||
init(){
|
||||
if (!AppInfoConfig.Official){
|
||||
return;
|
||||
}
|
||||
|
||||
SecurityMonitorProxy.getInstance().commonInfInfo=()=>{
|
||||
let userId = HXUserService.getInstance().getUserIdFromLocal();
|
||||
let info = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT);
|
||||
let sb = new StringBuilder();
|
||||
|
||||
sb.append("userId:").append(userId).append("\r\n")
|
||||
.append("bundleName:").append(info.name).append("\r\n")
|
||||
.append("versionName:").append(info.versionName).append("\r\n")
|
||||
.append("innerVersion:").append(AppInfoConfig.APPLET_INNER_VERSION)
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
SecurityMonitorProxy.getInstance().agent=()=>{
|
||||
return AppConfigManager.getInstance().getUA();
|
||||
}
|
||||
SecurityMonitorProxy.getInstance().innerVersion = ()=>{
|
||||
return AppInfoConfig.APPLET_INNER_VERSION_FULL;
|
||||
}
|
||||
|
||||
SecurityMonitorProxy.getInstance().isUserTemp = ()=>{return HXUserService.getInstance().isTemporaryUser()} ;
|
||||
SecurityMonitorProxy.getInstance().cooke = ()=>{
|
||||
let cookie = CookieManager.getInstance().getCookieSync();
|
||||
if (!cookie) {
|
||||
return "";
|
||||
}
|
||||
return cookie;
|
||||
};
|
||||
|
||||
//初始化启动崩溃保护
|
||||
SecurityMonitorProxy.getInstance().initSecurityMonitor();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { connection } from "@kit.NetworkKit";
|
||||
import { Want } from "@kit.AbilityKit";
|
||||
import { AppUtil } from "@pura/harmony-utils";
|
||||
import { HXLog } from "biz_common";
|
||||
|
||||
export class HXNetwork {
|
||||
private static instance: HXNetwork;
|
||||
private TAG: string = "HXNetwork";
|
||||
|
||||
public static getInstance(): HXNetwork {
|
||||
if (!HXNetwork.instance) {
|
||||
HXNetwork.instance = new HXNetwork()
|
||||
}
|
||||
return HXNetwork.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测网络是否可用
|
||||
*/
|
||||
detectNet(): boolean {
|
||||
try {
|
||||
let handle = connection.getDefaultNetSync();
|
||||
if (!handle || handle.netId === 0) {
|
||||
// 是否获取到有效网络连接
|
||||
HXLog.e(this.TAG, "Can\'t get available connection!");
|
||||
return false;
|
||||
}
|
||||
|
||||
let capabilities = connection.getNetCapabilitiesSync(handle);
|
||||
let cap = capabilities.networkCap || [];
|
||||
|
||||
// 网络是否可用
|
||||
if (!cap.includes(connection.NetCap.NET_CAPABILITY_VALIDATED) && !cap.includes(connection.NetCap.NET_CAPABILITY_INTERNET)) {
|
||||
HXLog.e(this.TAG, "The network is not available.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
HXLog.e(this.TAG, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 网络异常(不可用)弹窗
|
||||
*/
|
||||
@Preview
|
||||
@Component
|
||||
export struct NetworkAnomalyDialog {
|
||||
exitApp?: () => void;
|
||||
setup?: () => void;
|
||||
retry?: () => void;
|
||||
exitBtnText: string | ResourceStr = "退出";
|
||||
setupBtnText: string | ResourceStr = "设置网络";
|
||||
retryBtnText: string | ResourceStr = "重试";
|
||||
//弹窗标题
|
||||
title?: string | ResourceStr = "提示";
|
||||
//弹窗正文
|
||||
content?: string | ResourceStr = "联网可能异常,请检查您的网络!";
|
||||
borderW: number = 0.7;
|
||||
borderR: number = 10;
|
||||
marginTop: number = 20;
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text(this.title).fontSize(18).margin({ top: this.marginTop }).fontWeight(FontWeight.Bold)
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
Text(this.content).fontSize(14).margin({ top: this.marginTop })
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
|
||||
Row() {
|
||||
Button(this.exitBtnText, { type: ButtonType.Normal, stateEffect: true })
|
||||
.onClick(() => {
|
||||
if (this.exitApp) {
|
||||
this.exitApp();
|
||||
}
|
||||
})
|
||||
.fontColor($r('app.color.elements_text_secondary'))
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.layoutWeight(1)
|
||||
.border({
|
||||
width: { top: this.borderW, right: this.borderW },
|
||||
color: $r('app.color.elements_others_divider_primary'),
|
||||
radius: { bottomLeft: this.borderR }
|
||||
})
|
||||
Button(this.setupBtnText, { type: ButtonType.Normal, stateEffect: true })
|
||||
.onClick(() => {
|
||||
if (this.setup) {
|
||||
this.setup();
|
||||
}
|
||||
})
|
||||
.fontColor($r('app.color.elements_text_primary_01'))
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.layoutWeight(1)
|
||||
.border({
|
||||
width: { top: this.borderW },
|
||||
color: $r('app.color.elements_others_divider_primary'),
|
||||
})
|
||||
Button(this.retryBtnText, { type: ButtonType.Normal, stateEffect: true })
|
||||
.onClick(() => {
|
||||
if (this.retry) {
|
||||
this.retry();
|
||||
}
|
||||
})
|
||||
.fontColor($r('app.color.elements_text_primary_01'))
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.layoutWeight(1)
|
||||
.border({
|
||||
width: { top: this.borderW, left: this.borderW },
|
||||
color: $r('app.color.elements_others_divider_primary'),
|
||||
radius: { bottomRight: this.borderR }
|
||||
})
|
||||
}
|
||||
.margin({ top: this.marginTop })
|
||||
}
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.width('75%')
|
||||
.borderRadius(this.borderR)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { AppConfigManager, HXLog, PreferenceService } from 'biz_common'
|
||||
import { common, Want } from '@kit.AbilityKit'
|
||||
import router from '@ohos.router'
|
||||
import { window } from '@kit.ArkUI'
|
||||
import { deviceInfo } from '@kit.BasicServicesKit'
|
||||
import { NetworkAnomalyDialog } from './NetworkAnomalyDialog'
|
||||
import { HXNetwork } from './HXNetwork'
|
||||
import { ConnectionSeq } from '@kernel/lib_communication'
|
||||
import { AuthProviderService } from 'biz_auth'
|
||||
import { AdvertisingMonitorService } from 'biz_hxservice'
|
||||
|
||||
const SettingWant: Want = {
|
||||
bundleName: 'com.huawei.hmos.settings',
|
||||
abilityName: 'com.huawei.hmos.settings.MainAbility',
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络异常页面
|
||||
* 用于承接空白Logo页面,网络异常弹窗
|
||||
*/
|
||||
@Entry
|
||||
@Component
|
||||
struct NetworkAnomalyPage {
|
||||
private TAG: string = "HXNetwork";
|
||||
private systemBarColor = "#00000000";
|
||||
private systemBarContentColor = "#FFFFFFFF";
|
||||
|
||||
async setSystemBar() {
|
||||
let context = (this.getUIContext().getHostContext() as common.UIAbilityContext);
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(context);
|
||||
// 前提:设置窗口为全屏窗口
|
||||
if ((deviceInfo.deviceType !== '2in1')) {
|
||||
windowClass.setWindowLayoutFullScreen(true);
|
||||
} else {
|
||||
windowClass.setWindowLayoutFullScreen(false);
|
||||
}
|
||||
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
// 颜色属性为ARGB,将蒙层设置为0%使其透明
|
||||
navigationBarColor: this.systemBarColor,
|
||||
statusBarColor: this.systemBarColor,
|
||||
navigationBarContentColor: this.systemBarContentColor,
|
||||
statusBarContentColor: this.systemBarContentColor
|
||||
})
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.setSystemBar()
|
||||
}
|
||||
|
||||
onExit() {
|
||||
HXLog.i(this.TAG, "EXIT APP!!");
|
||||
// 用户退出应用
|
||||
(this.getUIContext().getHostContext() as common.UIAbilityContext).terminateSelf(() => {
|
||||
});
|
||||
}
|
||||
|
||||
onJumpToSetup() {
|
||||
HXLog.i(this.TAG, "JumpSetup!!");
|
||||
let context = (this.getUIContext().getHostContext() as common.UIAbilityContext);
|
||||
context.startAbility(SettingWant, (err, data) => {
|
||||
HXLog.i(this.TAG, `startAbility : err : ${JSON.stringify(err) ?? ''}`);
|
||||
HXLog.i(this.TAG, `startAbility : data : ${JSON.stringify(data) ?? ''}`);
|
||||
})
|
||||
}
|
||||
|
||||
onRetry() {
|
||||
const netAvailable = HXNetwork.getInstance().detectNet();
|
||||
if (netAvailable) {
|
||||
// 用户是否同意隐私政策
|
||||
const userAgree = PreferenceService.getBooleanValueSync('Privacy', 'isUserAgree', false);
|
||||
const jumpUrl = userAgree ? 'pages/Index' : 'pages/LauncherPage';
|
||||
router.replaceUrl({ url: jumpUrl }, router.RouterMode.Single, (err) => {
|
||||
if (err) {
|
||||
console.error("Failed to start Index Page: " + err)
|
||||
return;
|
||||
}
|
||||
if (userAgree) {
|
||||
AdvertisingMonitorService.getInstance().init(this.getUIContext().getHostContext() as common.UIAbilityContext)
|
||||
AppConfigManager.getInstance().fetchIMEI().then((isSuccess: boolean) => {
|
||||
if (isSuccess) {
|
||||
AuthProviderService.getInstance().sendAuthRequest(ConnectionSeq.HQ);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
NetworkAnomalyDialog({
|
||||
exitApp: this.onExit,
|
||||
setup: this.onJumpToSetup,
|
||||
retry: this.onRetry,
|
||||
title: '提示',
|
||||
content: '联网可能异常,请检查您的网络!'
|
||||
})
|
||||
.zIndex(4)
|
||||
.align(Alignment.Center)
|
||||
.alignRules(
|
||||
{
|
||||
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
|
||||
'bottom': { 'anchor': '__container__', 'align': VerticalAlign.Bottom },
|
||||
'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start },
|
||||
'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
|
||||
})
|
||||
.backgroundColor($r('app.color.dialog_bg_99666666'))
|
||||
Image($r('app.media.launch_logo'))
|
||||
.objectFit(ImageFit.Auto)
|
||||
.width('80%')
|
||||
.height(205)
|
||||
.alignRules({
|
||||
'bottom': {
|
||||
'anchor': "__container__",
|
||||
'align': VerticalAlign.Bottom
|
||||
},
|
||||
'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center },
|
||||
})
|
||||
}.width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { SectorList } from 'biz_market';
|
||||
import router from '@ohos.router';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import window from '@ohos.window';
|
||||
import { StatusBar } from '../widget/StatusBar';
|
||||
import { SubPageBar } from 'biz_selfcode';
|
||||
import { TypeUtil } from 'biz_common';
|
||||
import { StockListPageConfig } from 'biz_market/src/main/ets/components/stockListPage/Config';
|
||||
import { sectorListPageConfig } from 'biz_market/src/main/ets/features/sectorList/Config';
|
||||
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct SectorListPage {
|
||||
@State barHeight: number = 0;
|
||||
private pageConfig: StockListPageConfig = sectorListPageConfig;
|
||||
private selectedTabIndex: number = 0;
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
|
||||
async setSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 获取状态栏高度
|
||||
this.barHeight = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
async resetSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 从路由中获取参数并初始化页面数据
|
||||
*/
|
||||
aboutToAppear() {
|
||||
let routerParam = router.getParams() as Record<string, Object>;
|
||||
if (routerParam.selectedTabIndex && TypeUtil.isNumber(routerParam.selectedTabIndex)) {
|
||||
this.selectedTabIndex = routerParam.selectedTabIndex as number;
|
||||
}
|
||||
if (routerParam.pageConfig) {
|
||||
this.pageConfig = routerParam.pageConfig as StockListPageConfig;
|
||||
}
|
||||
this.setSystemBar()
|
||||
}
|
||||
|
||||
aboutToDisappear() {
|
||||
this.resetSystemBar()
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
StatusBar();
|
||||
SubPageBar({
|
||||
title: this.pageConfig.pageTitle,
|
||||
});
|
||||
Flex({ direction: FlexDirection.Column }) {
|
||||
SectorList({
|
||||
defaultTabIndex: this.selectedTabIndex,
|
||||
pageConfig: this.pageConfig
|
||||
})
|
||||
.flexShrink(1)
|
||||
.height('100%')
|
||||
}.layoutWeight(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import router from '@ohos.router'
|
||||
import web_webview from '@ohos.web.webview';
|
||||
import window from '@ohos.window';
|
||||
import common from '@ohos.app.ability.common';
|
||||
|
||||
/**
|
||||
* 暂时使用的页面
|
||||
*/
|
||||
@Entry
|
||||
@Component
|
||||
export default struct CommonWebPage {
|
||||
private url: string = '';
|
||||
private title: string = ''
|
||||
private webController: WebviewController = new web_webview.WebviewController;
|
||||
@State barHeight:number = 0;
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
|
||||
async setSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 获取状态栏高度
|
||||
this.barHeight = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
async resetSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
this.setSystemBar()
|
||||
|
||||
const routerParam = router.getParams() as Record<string, string>;
|
||||
this.url = routerParam.url;
|
||||
this.title = routerParam.title || '';
|
||||
}
|
||||
|
||||
aboutToDisappear(){
|
||||
this.resetSystemBar()
|
||||
try {
|
||||
this.webController.stop()
|
||||
} catch (e) {
|
||||
// WebView may already be detached.
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Blank().height(px2vp(this.barHeight) +2).color($r('app.color.subpage_title_red')).width('100%')
|
||||
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
|
||||
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
|
||||
Column() {
|
||||
Image($r('app.media.toolbar_arrow_left'))
|
||||
.width(19)
|
||||
.height(24)
|
||||
.fillColor('#FFFFFF')
|
||||
}.margin({ right: 16 })
|
||||
.onClick(() => {
|
||||
router.back();
|
||||
})
|
||||
.padding(10)
|
||||
Text(this.title).fontSize(19).fontColor('#FFFFFF')
|
||||
}.margin({ left: 16 })
|
||||
}
|
||||
.height(44).backgroundColor($r('app.color.navbar_bg_red')).width('100%')
|
||||
|
||||
Web({
|
||||
src: this.url,
|
||||
controller: this.webController
|
||||
}).domStorageAccess(true)
|
||||
.mixedMode(MixedMode.All)
|
||||
.flexGrow(1)
|
||||
.flexShrink(1)
|
||||
}
|
||||
.height('100%')
|
||||
.width('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { DebugService, FloatingDebugWindow} from '@b2c-spi/hx_cbas_api';
|
||||
|
||||
/**
|
||||
* 悬浮窗页面
|
||||
*/
|
||||
@Entry
|
||||
@Component
|
||||
struct FloatingWindowPage {
|
||||
build() {
|
||||
Column() {
|
||||
FloatingDebugWindow({
|
||||
onMinimize: (): void => { DebugService.getInstance().minimizeToIcon(); },
|
||||
onClose: (): void => { DebugService.getInstance().hideWindow(); },
|
||||
onMove: (deltaX: number, deltaY: number): void => {
|
||||
DebugService.getInstance().moveWindow(deltaX, deltaY);
|
||||
}
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
import { LoginCommonConstants } from 'biz_login';
|
||||
import { RSAKeyManager, WxPaySidPermissionMarker } from 'biz_auth';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import window from '@ohos.window';
|
||||
import { BusinessError } from '@ohos.base';
|
||||
import { saveSelfcodeSync } from 'service_watchlist';
|
||||
import emitter from '@ohos.events.emitter';
|
||||
import WTInit from 'biz_trade/src/main/ets/utils/WTInit';
|
||||
import {
|
||||
AdsPageConfig,
|
||||
EmitterConstants,
|
||||
GlobalContext,
|
||||
HeadSettingManager,
|
||||
HXLog,
|
||||
HxTags,
|
||||
IndexTabManager,
|
||||
OptionPageConstants,
|
||||
TabManager,
|
||||
TabManagerConstants
|
||||
} from 'biz_common';
|
||||
import hilog from '@ohos.hilog';
|
||||
import { CookieManager, HXUserService, KaihuFlagManager, NewUserManager, UserSourceManager } from 'biz_hxservice';
|
||||
import { StatusBar } from '../widget/StatusBar';
|
||||
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
|
||||
import { deviceInfo } from '@kit.BasicServicesKit';
|
||||
import { AdsPopupHost, FirstPage } from '@b2c/first_page';
|
||||
import { router } from '@kit.ArkUI';
|
||||
import { HQPage } from './future/HQPage';
|
||||
import TradeBizPageContainer from './TradeBizPageContainer';
|
||||
import { GrayTestManager } from 'lib_graytestmanager'
|
||||
import { HexinVersionControl } from 'biz_trade/src/main/ets/utils/HexinVersionControl';
|
||||
import { DiscoverPage } from '@b2c/biz_discover';
|
||||
import { HQPageConstants } from 'biz_common/src/main/ets/constants/HQPageConstants';
|
||||
import { NumberUtil } from '@pura/harmony-utils';
|
||||
import { parseInt } from '@wolfx/lodash';
|
||||
import { cbasService } from '../register/cbasregister/CbasServiceRegister';
|
||||
import BuildProfile from 'biz_common/BuildProfile';
|
||||
import { DebugFloatingBall } from './debug/DebugFloatingBall';
|
||||
import { GroupSubManager } from 'base_mobilepush';
|
||||
import { HXPushService } from 'service_push';
|
||||
import { FIRST_PAGE_ONAPPEAR_EVENT } from '../entryability/WantManager';
|
||||
import { DialogHub } from '@hadss/dialoghub';
|
||||
import { DrawLineInit, QuoteCustomSettingManager, SettingHelper, SettingDataSource, QuoteSettingEvents, CurveBottomToolsDataManager } from 'biz_quote';
|
||||
import { TradeSettingManager } from 'biz_trade/src/main/ets/setting';
|
||||
import { AutoLoginManager } from 'biz_trade/src/main/ets/trade/manager/AutoLoginManager';
|
||||
import { ColourModeStatus } from '@kernel/theme_manager';
|
||||
import { enableDarkMode, ThemeManagerEmitterConstants } from '@kernel/theme_manager';
|
||||
import { CmeTraceForeignManager } from 'biz_quote/src/main/ets/manager/CmeTraceForeignManager';
|
||||
import { HQUGCManager } from 'biz_market';
|
||||
import { AdsManager } from '@b2c-f/fuhm-ads';
|
||||
|
||||
|
||||
const TAG: string = 'Struct Index';
|
||||
const SWIPER_INDEX_0: number = 0;
|
||||
const SWIPER_INDEX_1: number = 1;
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct IndexM {
|
||||
/**************************************/
|
||||
//Tab管理
|
||||
tabManager: TabManager = new IndexTabManager();
|
||||
//主页TabIndex
|
||||
@Provide('mainPageIndex') currentIndex: number = 0;
|
||||
private tabController: TabsController = new TabsController();
|
||||
/**************************************/
|
||||
//交易用的index page show
|
||||
@Provide MainPageShow: boolean = false;
|
||||
@Provide pageShow: boolean = false;
|
||||
/**************************************/
|
||||
//全局userId, userName快速获取
|
||||
@Provide userId: string = '';
|
||||
@Provide userName: string = '';
|
||||
/**************************************/
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
|
||||
/**************************************/
|
||||
//一多断点
|
||||
@StorageLink('displayBreakPoint') curBp: string = 'sm'
|
||||
@StorageLink('windowWidthVp') windowWidthVp: number = 0;
|
||||
/**************************************/
|
||||
//自选业务
|
||||
@Provide('SIDE_BAR_ENABLE') @Watch('sideBarStatusChanged') sideBarEnable: boolean = false;
|
||||
@Provide('PRESSED_ROW') pressedRow: Array<string> = new Array<string>(3).fill('');
|
||||
/**************************************/
|
||||
// 云同步数据更新回调
|
||||
private onSettingUpdateCallback = () => {
|
||||
HXLog.i(TAG, 'QuoteSettingEvents.SETTING_UPDATE received, clearing HeadSettingManager cache')
|
||||
HeadSettingManager.getInstance().clearCache()
|
||||
}
|
||||
@Provide('SELF_CODE_TITLE') selfCodeTitle: string = '同花顺自选';
|
||||
/**************************************/
|
||||
//侧滑栏
|
||||
@State scrollable: boolean = false;
|
||||
@State swiperIndex: number = SWIPER_INDEX_1;
|
||||
private swiperController: SwiperController = new SwiperController()
|
||||
@State swiperOffset: number = 0;
|
||||
@State @Watch('coeChanged') coefficient: number = 0;
|
||||
//蒙层可见性
|
||||
@State stackVis: boolean = false
|
||||
//布局总宽度
|
||||
private totalWidth = 0
|
||||
@State isShowStatusBar: boolean = false
|
||||
@State isHideYaoYi: boolean = false;
|
||||
// 行情二级Tab
|
||||
@Provide hqPageSubTabIndex: number = HQPageConstants.HQ_PAGE_INDEX_SELF_CODE_PAGE
|
||||
// 期权三级Tab
|
||||
@Provide optionPageL3TabIndex: number = OptionPageConstants.HQ_PAGE_INDEX_T_QUOTE_PAGE
|
||||
// 主题监听
|
||||
@StorageProp(enableDarkMode) @Watch('onThemeModeChanged') enableDarkMode: boolean = ColourModeStatus.enableDarkMode()
|
||||
|
||||
// 主题变化回调
|
||||
onThemeModeChanged() {
|
||||
HXLog.d(TAG, 'onThemeModeChanged triggered, enableDarkMode=' + this.enableDarkMode)
|
||||
this.setSystemBar(this.stackVis)
|
||||
}
|
||||
|
||||
parseOffset(curOffset: number): number {
|
||||
if (this.totalWidth > 0) {
|
||||
let coe = this.swiperIndex === 1 ? (Math.abs(curOffset) / (this.totalWidth * 0.85)) :
|
||||
(1 - Math.abs(curOffset) / (this.totalWidth * 0.85))
|
||||
console.log(`lym: coe: ${coe}`)
|
||||
return coe * 0.4
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
coeChanged() {
|
||||
}
|
||||
|
||||
sideBarStatusChanged() {
|
||||
}
|
||||
|
||||
|
||||
//沉浸式设置、状态栏设置
|
||||
async setSystemBar(stackVis: boolean) {
|
||||
// 获取主窗口
|
||||
let windowStageInstance: window.WindowStage = AppStorage.get('EntryAbilityWindowStage') as window.WindowStage
|
||||
if (!windowStageInstance) {
|
||||
return
|
||||
}
|
||||
let windowClass: window.Window = windowStageInstance.getMainWindowSync()
|
||||
|
||||
// 前提:设置窗口为全屏窗口
|
||||
if ((deviceInfo.deviceType !== '2in1')) {
|
||||
windowClass.setWindowLayoutFullScreen(true);
|
||||
} else {
|
||||
console.log("deviceType" + deviceInfo.deviceType)
|
||||
windowClass.setWindowLayoutFullScreen(false);
|
||||
}
|
||||
// 根据主题设置状态栏内容颜色
|
||||
let isDarkMode = ColourModeStatus.enableDarkMode()
|
||||
let contentColor = isDarkMode ? "#FFFFFFFF" : "#D6000000"
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
// 颜色属性为ARGB,将蒙尘设置为0%使其透明
|
||||
navigationBarColor: getContext().resourceManager.getStringByNameSync('entry_transparent_color'),
|
||||
statusBarColor: getContext().resourceManager.getStringByNameSync('entry_transparent_color'),
|
||||
navigationBarContentColor: stackVis
|
||||
? getContext().resourceManager.getStringByNameSync('entry_navigation_bar_content_with_mask_color')
|
||||
: contentColor,
|
||||
statusBarContentColor: stackVis
|
||||
? getContext().resourceManager.getStringByNameSync('entry_navigation_bar_content_with_mask_color')
|
||||
: contentColor
|
||||
})
|
||||
}
|
||||
|
||||
@Builder
|
||||
TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
|
||||
Column() {
|
||||
Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
|
||||
.size({ width: 24, height: 24 })
|
||||
.fillColor(this.currentIndex !== targetIndex ? $r('app.color.elements_text_primary_02') : $r('app.color.elements_text_primary_01'))
|
||||
Text(title)//'#2E58FF'
|
||||
.fontColor(this.currentIndex === targetIndex ? $r('app.color.elements_text_primary_01') : $r('app.color.elements_text_primary_02')).fontSize('11fp').margin({ top: 4 })
|
||||
}
|
||||
.hoverEffect(HoverEffect.Highlight)
|
||||
//.width('100%')
|
||||
.height(50)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
}
|
||||
|
||||
updateUserInfo() {
|
||||
this.userId = HXUserService.getInstance().getUserId();
|
||||
this.userName = HXUserService.getInstance().getUserName();
|
||||
cbasService.setUserId(this.userId)
|
||||
}
|
||||
|
||||
subscribeLoginEvent() {
|
||||
emitter.on({
|
||||
eventId: LoginCommonConstants.USER_LOGIN
|
||||
}, async () => {
|
||||
HXLog.d(TAG, HxTags.entryTags + 'subscribe login index triggered at ' + Date.now())
|
||||
this.updateUserInfo();
|
||||
// 登录后刷新 drawline 用户上下文,确保后续同步按当前账号隔离。
|
||||
DrawLineInit.init()
|
||||
WxPaySidPermissionMarker.requestPermissionIfMarked()
|
||||
WTInit.initAfterAppLogin()
|
||||
GroupSubManager.getInstance().requestGroupData();
|
||||
RSAKeyManager.getInstance().requestRSAInfo();
|
||||
GrayTestManager.getInstance().requestGrayTestWhenUserIdChanged(this.userId)
|
||||
HXPushService.getInstance().shouldUploadNotificationByUseridChanged();
|
||||
// 发起 CME 内外盘联动配置请求(只请求一次)
|
||||
CmeTraceForeignManager.getInstance().request()
|
||||
NewUserManager.getInstance().onAuthSucceed(() => {
|
||||
AdsManager.reset()
|
||||
if (NewUserManager.getInstance().needRequestAdsNow()) {
|
||||
AdsManager.refreshNotifyFirstPageAds(true)
|
||||
}
|
||||
})
|
||||
KaihuFlagManager.getInstance().onConnectToServer()
|
||||
UserSourceManager.getInstance().onConnectToServer()
|
||||
// 更新行情设置管理器的用户ID(登录成功强制重新下载云端数据)
|
||||
QuoteCustomSettingManager.getInstance().updateUserId(
|
||||
HXUserService.getInstance().getUserIdFromLocal(),
|
||||
true // 强制重新下载云端数据
|
||||
)
|
||||
|
||||
// 更新底部操作栏排序管理器的用户ID(登录成功强制重新下载云端数据)
|
||||
CurveBottomToolsDataManager.getInstance().updateUserId(
|
||||
HXUserService.getInstance().getUserIdFromLocal(),
|
||||
true
|
||||
)
|
||||
|
||||
// 更新交易设置管理器的用户ID(登录成功强制重新下载云端数据)
|
||||
TradeSettingManager.getInstance().updateUserId(
|
||||
HXUserService.getInstance().getUserIdFromLocal(),
|
||||
false
|
||||
)
|
||||
});
|
||||
emitter.on({
|
||||
eventId: LoginCommonConstants.USER_LOGOUT
|
||||
}, () => {
|
||||
HXLog.d(TAG, 'LoginEvent LoginCommonConstants.USER_LOGOUT')
|
||||
this.updateUserInfo();
|
||||
// 登出后重新写入临时用户上下文,避免沿用上一个账号的画线身份。
|
||||
DrawLineInit.init()
|
||||
CurveBottomToolsDataManager.getInstance().updateUserId(
|
||||
HXUserService.getInstance().getUserIdFromLocal(),
|
||||
false
|
||||
)
|
||||
|
||||
// 退出账号,重置所有自动登录信息
|
||||
AutoLoginManager.getInstance().forceClearAutoLoginAccountToken()
|
||||
|
||||
// 退出登录时清除表头设置缓存,并发送事件通知页面刷新为默认配置
|
||||
HeadSettingManager.getInstance().clearCache()
|
||||
emitter.emit({ eventId: QuoteSettingEvents.SETTING_UPDATE })
|
||||
});
|
||||
/*emitter.on({
|
||||
eventId: EventConstants.ID_JUMP_TO_WT_FIRST_PAGE
|
||||
}, (data) => {
|
||||
// 用于其它页面 跳到主页面,并显示指定tab
|
||||
let index = data.data!!["tabIndex"] as number;
|
||||
if (this.currentIndex != index) {
|
||||
router.back({
|
||||
url: "pages/Index",
|
||||
params: {
|
||||
tabIndex: index,
|
||||
}
|
||||
})
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
subscribeTabChangeIndex() {
|
||||
emitter.on({ eventId: EmitterConstants.TAB_UI_MANAGER_CHANGE_INDEX }, (eventData) => {
|
||||
this.tabController.changeIndex(2)
|
||||
if (eventData && eventData.data) {
|
||||
let tabUIManagerIndex: string | undefined = eventData.data[EmitterConstants.EVENT_DATA_KEY_TAB_INDEX]
|
||||
if (tabUIManagerIndex && NumberUtil.isNumber(tabUIManagerIndex)) {
|
||||
this.tabController.changeIndex(parseInt(tabUIManagerIndex))
|
||||
}
|
||||
let qhPageSubTabIndex: string | undefined =
|
||||
eventData.data[EmitterConstants.EVENT_DATA_KEY_HQ_PAGE_SUB_TAB_INDEX]
|
||||
if (qhPageSubTabIndex && NumberUtil.isNumber(qhPageSubTabIndex)) {
|
||||
this.hqPageSubTabIndex = parseInt(qhPageSubTabIndex)
|
||||
}
|
||||
let optionPageL3TabIndex: string | undefined =
|
||||
eventData.data[EmitterConstants.EVENT_DATA_KEY_OPTION_PAGE_SUB_TAB_INDEX]
|
||||
if (optionPageL3TabIndex && NumberUtil.isNumber(optionPageL3TabIndex)) {
|
||||
this.optionPageL3TabIndex = parseInt(optionPageL3TabIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
subscribeSwiperChangeIndex() {
|
||||
emitter.on({ eventId: EmitterConstants.SWIPER_CHANGETOINDEX_0 }, () => {
|
||||
this.swiperController.changeIndex(0, true)
|
||||
this.coefficient = 0.4
|
||||
this.stackVis = true
|
||||
this.setSystemBar(this.stackVis)
|
||||
})
|
||||
emitter.on({ eventId: EmitterConstants.SWIPER_CHANGETOINDEX_1_TEMP }, () => {
|
||||
this.swiperController.changeIndex(1, true)
|
||||
this.stackVis = false
|
||||
this.setSystemBar(this.stackVis)
|
||||
})
|
||||
}
|
||||
|
||||
unSubscribeLoginEvent() {
|
||||
HXLog.d(TAG, HxTags.entryTags +
|
||||
'Unsubscribe LoginEvent LoginCommonConstants.USER_LOGOUT ,LoginCommonConstants.USER_LOGOUT triggered at ' +
|
||||
Date.now())
|
||||
emitter.off(LoginCommonConstants.USER_LOGIN);
|
||||
emitter.off(LoginCommonConstants.USER_LOGOUT);
|
||||
}
|
||||
|
||||
aboutToDisappear() {
|
||||
HXLog.d(TAG, 'Index aboutToDisappear triggered at ' + Date.now())
|
||||
emitter.off(EmitterConstants.SWIPER_CHANGETOINDEX_1_TEMP)
|
||||
emitter.off(EmitterConstants.TAB_UI_MANAGER_CHANGE_INDEX)
|
||||
emitter.off(QuoteSettingEvents.SETTING_UPDATE, this.onSettingUpdateCallback)
|
||||
this.unSubscribeLoginEvent();
|
||||
// this.unSubscribeThemeChange();
|
||||
saveSelfcodeSync()
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
hilog.info(0x0000, 'Entry IndexPage', '%{public}s', 'page aboutToAppear');
|
||||
this.subscribeLoginEvent();
|
||||
this.subscribeSwiperChangeIndex()
|
||||
this.subscribeTabChangeIndex()
|
||||
this.setSystemBar(this.stackVis)
|
||||
this.isHideYaoYi = HexinVersionControl.hideTrade();
|
||||
DialogHub.init(this.getUIContext())
|
||||
|
||||
// 初始化行情设置管理器(使用当前用户ID,可能是临时用户)
|
||||
QuoteCustomSettingManager.getInstance().initialize(
|
||||
this.context,
|
||||
HXUserService.getInstance().getUserIdFromLocal()
|
||||
).catch((err: Error) => {
|
||||
HXLog.e(TAG, `QuoteCustomSettingManager early init failed: ${err.message}`)
|
||||
})
|
||||
|
||||
CurveBottomToolsDataManager.getInstance().initialize(
|
||||
this.context,
|
||||
HXUserService.getInstance().getUserIdFromLocal()
|
||||
).catch((err: Error) => {
|
||||
HXLog.e(TAG, `CurveBottomToolsDataManager early init failed: ${err.message}`)
|
||||
})
|
||||
|
||||
HQUGCManager.get().queryData(CookieManager.getInstance().getCookieSync())
|
||||
|
||||
// 配置表头设置管理器的云同步访问器
|
||||
HeadSettingManager.getInstance().setSettingAccessor(
|
||||
(key: string, defaultValue: ESObject): ESObject => SettingHelper.getSetting(key, defaultValue, SettingDataSource.QUOTE_CLOUD),
|
||||
(key: string, value: ESObject): void => { SettingHelper.updateSetting(key, value, SettingDataSource.QUOTE_CLOUD) }
|
||||
)
|
||||
|
||||
// 监听云同步数据更新事件,清除表头设置缓存以便重新从云端读取
|
||||
emitter.on({ eventId: QuoteSettingEvents.SETTING_UPDATE }, this.onSettingUpdateCallback)
|
||||
|
||||
// 初始化交易设置管理器(使用当前用户ID,可能是临时用户)
|
||||
TradeSettingManager.getInstance().initialize(
|
||||
this.context
|
||||
).catch((err: Error) => {
|
||||
HXLog.e(TAG, `TradeSettingManager early init failed: ${err.message}`)
|
||||
})
|
||||
|
||||
// 冷启动先初始化一次 drawline 基础能力,保证首次进入行情页即可绘制同步线。
|
||||
DrawLineInit.init()
|
||||
}
|
||||
|
||||
onPageShow() {
|
||||
this.updateUserInfo();
|
||||
this.MainPageShow = true;
|
||||
this.pageShow = true;
|
||||
this.tabManager.notifyVisibilityChangeWithVisibility(true)
|
||||
this.setSystemBar(this.stackVis)
|
||||
//fixme 这里直接通过生命周期去请求是比较取巧的解决方案,应该在认证成功后就发起,后续修改
|
||||
//index页面在前台且展示自选tab页
|
||||
if (router.getParams()) {
|
||||
let params = router.getParams() as Record<string, string>;
|
||||
let indexStr = params.tabIndex;
|
||||
if (indexStr) {
|
||||
let index = Number.parseInt(indexStr.toString());
|
||||
if (index && index != this.currentIndex) {
|
||||
this.tabController.changeIndex(index);
|
||||
}
|
||||
} else if (params.loginSuccessBackParam && params.loginSuccessBackParam == 'selectjiaoyi') {
|
||||
this.tabController.changeIndex(TabManagerConstants.INDEX_TAB_INDEX_TRADE_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建调试悬浮球(仅非release包)
|
||||
if (BuildProfile.BUILD_MODE_NAME.toString() != "release" && DebugFloatingBall.canShow) {
|
||||
let windowStage = AppStorage.get('EntryAbilityWindowStage') as window.WindowStage
|
||||
DebugFloatingBall.createDebugFloatingBall(windowStage)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据是否隐藏交易获取发现tab的索引
|
||||
* @returns
|
||||
*/
|
||||
private getDiscoverIndex():number{
|
||||
return this.isHideYaoYi?TabManagerConstants.INDEX_TAB_INDEX_DISCOVER_PAGE-1:TabManagerConstants.INDEX_TAB_INDEX_DISCOVER_PAGE
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Tab 索引获取广告悬浮球对应的 pageId
|
||||
*/
|
||||
private getFloatBallPageId(tabIndex: number): AdsPageConfig {
|
||||
if (tabIndex === TabManagerConstants.INDEX_TAB_INDEX_FIRST_PAGE) {
|
||||
return AdsPageConfig.FIRST_PAGE_ID
|
||||
}
|
||||
return AdsPageConfig.INVALID
|
||||
}
|
||||
|
||||
onPageHide() {
|
||||
this.MainPageShow = false;
|
||||
this.pageShow = false;
|
||||
this.tabManager.notifyVisibilityChangeWithVisibility(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当首页节点挂载时触发
|
||||
*/
|
||||
private onFirstPageNodeAppear() {
|
||||
// 发送首页挂载的通知
|
||||
GlobalContext.get().eventHub.emit(FIRST_PAGE_ONAPPEAR_EVENT);
|
||||
// 首页挂载后进行push服务的初始化检查和上报
|
||||
HXPushService.getInstance().onFirstPageAppear(() => {
|
||||
});
|
||||
// 通知广告悬浮球页面切换(首次进入首页时需主动调用)
|
||||
AdsPopupHost.getInstance().onPageJump(AdsPageConfig.FIRST_PAGE_ID);
|
||||
}
|
||||
|
||||
build() {
|
||||
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
|
||||
StatusBar()
|
||||
.visibility(this.isShowStatusBar ? Visibility.Visible : Visibility.None)
|
||||
Tabs({ controller: this.tabController }) {
|
||||
if (this.tabManager.getShowingTabSet().has(TabManagerConstants.FirstPage)) {
|
||||
TabContent() {
|
||||
FirstPage({ tabManager: this.tabManager.getManager(TabManagerConstants.FirstPage) })
|
||||
}
|
||||
.tabBar(this.TabBuilder("首页", TabManagerConstants.INDEX_TAB_INDEX_FIRST_PAGE, $r("app.media.icon_home_fill_24"),
|
||||
$r("app.media.icon_home_24")))
|
||||
.onAppear(()=> {
|
||||
this.onFirstPageNodeAppear();
|
||||
})
|
||||
}
|
||||
|
||||
if (this.tabManager.getShowingTabSet().has(TabManagerConstants.SelfcodePage)) {
|
||||
TabContent() {
|
||||
HQPage({ tabManager: this.tabManager.getManager(TabManagerConstants.SelfcodePage) })
|
||||
}
|
||||
.tabBar(this.TabBuilder("行情", TabManagerConstants.INDEX_TAB_INDEX_QUOTE_PAGE, $r("app.media.icon_line_chart_fill_24"),
|
||||
$r("app.media.icon_line_chart_24")))
|
||||
}
|
||||
|
||||
if (!this.isHideYaoYi && this.tabManager.getShowingTabSet().has(TabManagerConstants.TradePage)) {
|
||||
TabContent() {
|
||||
TradeBizPageContainer({
|
||||
tabManager: this.tabManager.getManager(TabManagerConstants.TradePage)
|
||||
});
|
||||
}
|
||||
.tabBar(this.TabBuilder("交易", TabManagerConstants.INDEX_TAB_INDEX_TRADE_PAGE, $r("app.media.icon_trade_fill_24")
|
||||
, $r("app.media.icon_trade_24")))
|
||||
}
|
||||
|
||||
if (this.tabManager.getShowingTabSet().has(TabManagerConstants.NewsPage)) {
|
||||
TabContent() {
|
||||
DiscoverPage({
|
||||
tabManager: this.tabManager.getManager(TabManagerConstants.NewsPage)
|
||||
})
|
||||
}
|
||||
.tabBar(this.TabBuilder("发现", this.getDiscoverIndex(), $r("app.media.icon_compass_fill_24"),
|
||||
$r("app.media.icon_compass_24")))
|
||||
}
|
||||
}
|
||||
.vertical(this.curBp === 'lg' ? true : false)
|
||||
.barPosition(this.curBp === 'lg' ? BarPosition.Start : BarPosition.End)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.scrollable(false)
|
||||
.flexGrow(1)
|
||||
.flexBasis('0%')
|
||||
.animationDuration(0)
|
||||
.onChange((index: number) => {
|
||||
if (!this.isHideYaoYi && this.currentIndex != 2 && index == 2 &&
|
||||
HXUserService.getInstance().isTemporaryUser()) {
|
||||
this.tabController.changeIndex(this.currentIndex)
|
||||
router.pushUrl({
|
||||
url: 'pages/login/LoginPage',
|
||||
params: {
|
||||
flexSwitchFlag: true,
|
||||
sourcePage: 'Index',
|
||||
backParam: 'selectjiaoyi'
|
||||
},
|
||||
}, router.RouterMode.Single)
|
||||
} else {
|
||||
if (index == TabManagerConstants.INDEX_TAB_INDEX_FIRST_PAGE) {
|
||||
this.isShowStatusBar = false
|
||||
} else {
|
||||
this.isShowStatusBar = true
|
||||
}
|
||||
this.currentIndex = index;
|
||||
this.scrollable = (!this.isHideYaoYi &&
|
||||
this.currentIndex === TabManagerConstants.INDEX_TAB_INDEX_TRADE_PAGE) ? true : false;
|
||||
this.tabManager.notifyIndexChange(index);
|
||||
}
|
||||
})
|
||||
.onAreaChange((oldArea: Area, newArea: Area) => {
|
||||
if (newArea.height) {
|
||||
let height = newArea.height as number;
|
||||
GlobalContext.initPageHeight(height);
|
||||
}
|
||||
})
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.barHeight(this.curBp === 'lg' ? '100%' : 50)
|
||||
.barWidth(this.curBp == 'lg' ? 50 : '100%')
|
||||
.animationDuration(0)
|
||||
.divider({
|
||||
strokeWidth: 1,
|
||||
color: $r('app.color.elements_others_divider_primary')
|
||||
})
|
||||
if (this.curBp !== 'lg') {
|
||||
Blank().height(px2vp(GlobalContext.navigationBarHeight)).flexShrink(0).backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
}
|
||||
}
|
||||
.height('100%')
|
||||
.width('100%')
|
||||
}
|
||||
}
|
||||
|
||||
function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
|
||||
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
|
||||
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
|
||||
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
|
||||
let grantStatus: Array<number> = data.authResults;
|
||||
let length: number = grantStatus.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (grantStatus[i] === 0) {
|
||||
// 用户授权,可以继续访问目标操作
|
||||
} else {
|
||||
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 授权成功
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 自编指标草稿管理页面入口
|
||||
*
|
||||
* 此文件是路由入口,实际页面实现在 biz_indicator 模块中
|
||||
*/
|
||||
|
||||
import { IndicatorDraftManagePage as IndicatorDraftManagePageImpl } from 'biz_indicator'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
export struct IndicatorDraftManagePage {
|
||||
build() {
|
||||
Column() {
|
||||
IndicatorDraftManagePageImpl()
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 指标参数设置页面入口
|
||||
*
|
||||
* 此文件是路由入口,实际页面实现在 biz_indicator 模块中
|
||||
*/
|
||||
|
||||
import { IndicatorParamPage as IndicatorParamPageImpl } from 'biz_indicator'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
export struct IndicatorParamPage {
|
||||
build() {
|
||||
Column() {
|
||||
IndicatorParamPageImpl()
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* indicator_parser HAR 验证测试页面
|
||||
*
|
||||
* Author: LY
|
||||
* Date: 2026-01-07
|
||||
*/
|
||||
|
||||
import { IndicatorParserTask } from 'biz_indicator'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct IndicatorParserTestPage {
|
||||
@State testResult: string = '点击按钮开始测试'
|
||||
@State isLoading: boolean = false
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text('Indicator Parser HAR 测试')
|
||||
.fontSize(24)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
.margin({ top: 50, bottom: 30 })
|
||||
|
||||
Button(this.isLoading ? '测试中...' : '开始测试')
|
||||
.width('80%')
|
||||
.height(50)
|
||||
.fontSize(18)
|
||||
.enabled(!this.isLoading)
|
||||
.onClick(() => {
|
||||
this.runTest()
|
||||
})
|
||||
|
||||
Scroll() {
|
||||
Text(this.testResult)
|
||||
.fontSize(14)
|
||||
.fontColor('#333333')
|
||||
.padding(20)
|
||||
}
|
||||
.width('90%')
|
||||
.height(400)
|
||||
.margin({ top: 30 })
|
||||
.backgroundColor('#F5F5F5')
|
||||
.borderRadius(10)
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#FFFFFF')
|
||||
}
|
||||
|
||||
async runTest() {
|
||||
this.isLoading = true
|
||||
this.testResult = '开始测试...\n'
|
||||
|
||||
try {
|
||||
// 1. 创建实例
|
||||
this.testResult += '\n[1] 创建 IndicatorParserTask 实例...'
|
||||
const parser = new IndicatorParserTask()
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
|
||||
// 2. 测试预解析 - 使用简单的 MA 均线公式(异步版本)
|
||||
this.testResult += '\n[2] 测试预解析 (MA均线公式, 异步版本)...'
|
||||
const script = 'MA5:MA(CLOSE,5);'
|
||||
const preParseResult = await parser.getPreParseResAsync(script, '', '')
|
||||
if (preParseResult) {
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
this.testResult += ` 解析结果: ${preParseResult.substring(0, 200)}...\n`
|
||||
} else {
|
||||
this.testResult += ' ⚠️ 返回空 (可能需要数据)\n'
|
||||
}
|
||||
|
||||
// 3. 测试获取列数
|
||||
this.testResult += '\n[3] 测试 getColumnNum()...'
|
||||
const colNum = parser.getColumnNum()
|
||||
this.testResult += ` ✅ 列数: ${colNum}\n`
|
||||
|
||||
// 4. 测试获取行数
|
||||
this.testResult += '\n[4] 测试 getRowNum()...'
|
||||
const rowNum = parser.getRowNum()
|
||||
this.testResult += ` ✅ 行数: ${rowNum}\n`
|
||||
|
||||
// 5. 测试清空数据
|
||||
this.testResult += '\n[5] 测试 clearData()...'
|
||||
parser.clearData()
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
|
||||
// 6. 销毁实例
|
||||
this.testResult += '\n[6] 销毁实例...'
|
||||
parser.destroy()
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
|
||||
this.testResult += '\n=============================\n'
|
||||
this.testResult += '🎉 HAR 验证通过!所有接口可正常调用\n'
|
||||
this.testResult += '=============================\n'
|
||||
|
||||
} catch (error) {
|
||||
this.testResult += `\n\n❌ 测试失败: ${error}\n`
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import router from '@ohos.router'
|
||||
import { WebViewComponent, CustomWebviewController, NavigationBarViewModel } from 'biz_webview';
|
||||
import display from '@ohos.display';
|
||||
import window from '@ohos.window';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import { WebViewUrlUtil } from 'biz_webview/src/main/ets/Util/WebViewUrlUtil';
|
||||
import { handleBackEvent } from 'biz_common/src/main/ets/pc/PCAdapter';
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
import { emitter } from '@kit.BasicServicesKit';
|
||||
import { EmitterConstants } from 'biz_common';
|
||||
|
||||
const DOMAIN = 0x0000;
|
||||
const TAG = 'LandscapeWebViewPage';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
export default struct LandscapeWebViewPage {
|
||||
private webViewController: CustomWebviewController = new CustomWebviewController()
|
||||
private url: string = ''
|
||||
private refreshOnWidthChange: boolean = false;
|
||||
@State onShow: boolean = false
|
||||
navigationBarHandler: NavigationBarViewModel = new NavigationBarViewModel();
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
private windowClass: window.Window | null = null
|
||||
@StorageLink('displayBreakPoint') @Watch('displayChanged') curBp: string = 'sm'
|
||||
useCookie: boolean = false
|
||||
//登录状态改变后刷新web
|
||||
isLoginRefreshWeb: boolean = false
|
||||
|
||||
registerJSEvent() {
|
||||
display.getDefaultDisplaySync()
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
let routerParam = router.getParams() as Record<string, string>;
|
||||
this.url = routerParam.url;
|
||||
routerParam = WebViewUrlUtil.assign(routerParam, this.url)
|
||||
try {
|
||||
this.refreshOnWidthChange = routerParam.refreshOnWidthChange === "true" ? true : false;
|
||||
} catch (error) {
|
||||
console.error(`getStringSync failed, error code: ${error.code}, message: ${error.message}.`)
|
||||
}
|
||||
this.isLoginRefreshWeb = "" + routerParam['isLoginRefreshWeb'] === "true" ? true : false;
|
||||
if ("" + routerParam['useCookie'] === "true") {
|
||||
this.useCookie = true
|
||||
}
|
||||
this.registerJSEvent()
|
||||
// 提前切换到横屏,减少对其他页面的影响
|
||||
this.switchToLandscape()
|
||||
}
|
||||
|
||||
aboutToDisappear() {
|
||||
}
|
||||
|
||||
// 拦截系统返回上一级
|
||||
onBackPress() {
|
||||
if (this.webViewController && this.webViewController.isAttached && this.webViewController.accessBackward()) {
|
||||
this.webViewController.backward()
|
||||
return true
|
||||
}
|
||||
return !this.navigationBarHandler.canGoBack()
|
||||
}
|
||||
|
||||
onPageShow() {
|
||||
this.onShow = true
|
||||
// 从后台恢复时重新切换到横屏
|
||||
this.switchToLandscape()
|
||||
}
|
||||
|
||||
onPageHide() {
|
||||
if (this.onShow == true) {
|
||||
this.onShow = false
|
||||
}
|
||||
this.switchToPortrait()
|
||||
}
|
||||
|
||||
displayChanged() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换为横屏显示
|
||||
*/
|
||||
private async switchToLandscape(): Promise<void> {
|
||||
try {
|
||||
const mainWindow = await this.context.windowStage.getMainWindow()
|
||||
this.windowClass = mainWindow
|
||||
await this.windowClass.setPreferredOrientation(window.Orientation.LANDSCAPE)
|
||||
} catch (err) {
|
||||
hilog.warn(DOMAIN, TAG, '切换横屏失败: %{public}s', JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换为竖屏显示
|
||||
*/
|
||||
private async switchToPortrait(): Promise<void> {
|
||||
try {
|
||||
const mainWindow = await this.context.windowStage.getMainWindow()
|
||||
this.windowClass = mainWindow
|
||||
await this.windowClass.setPreferredOrientation(window.Orientation.PORTRAIT)
|
||||
} catch (err) {
|
||||
hilog.warn(DOMAIN, TAG, '切换竖屏失败: %{public}s', JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
build() {
|
||||
Stack({ alignContent: Alignment.TopStart }) {
|
||||
Column() {
|
||||
WebViewComponent({
|
||||
urlString: this.url,
|
||||
onShow: this.onShow,
|
||||
webViewController: this.webViewController,
|
||||
navigationBarHandler: this.navigationBarHandler,
|
||||
refreshOnWidthChange: this.refreshOnWidthChange,
|
||||
useCookie: this.useCookie,
|
||||
isLoginRefreshWeb: this.isLoginRefreshWeb,
|
||||
overScrollMode: OverScrollMode.NEVER
|
||||
})
|
||||
.layoutWeight(1)
|
||||
}
|
||||
.onKeyEvent((event: KeyEvent) => {
|
||||
handleBackEvent(event)
|
||||
})
|
||||
.height('100%')
|
||||
.width('100%')
|
||||
.padding({ left: 44, right: 44 })
|
||||
|
||||
// 返回按钮
|
||||
Image($r("app.media.navigate_back_icon"))
|
||||
.width(24)
|
||||
.height(24)
|
||||
.margin({ left: 60, top: 16 })
|
||||
.onClick(() => {
|
||||
emitter.emit({ eventId: EmitterConstants.LANDSCAPE_WEBVIEW_BACK })
|
||||
router.back();
|
||||
})
|
||||
}
|
||||
.height('100%')
|
||||
.width('100%')
|
||||
.id('biz_webview/landscape_container')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { AppConfigManager, PreferenceService } from 'biz_common'
|
||||
import { common } from '@kit.AbilityKit'
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit'
|
||||
import router from '@ohos.router'
|
||||
import { AuthProviderService, HXAuthManager, LoginParamHelper } from 'biz_auth'
|
||||
import { PrivacyUserDialog } from '../widget/PrivacyUserDialog'
|
||||
import { StatusBar } from '../widget/StatusBar'
|
||||
import { window } from '@kit.ArkUI'
|
||||
import { ConnectionSeq } from '@kernel/lib_communication/src/main/ets/data/CommunicationConstants'
|
||||
import { deviceInfo } from '@kit.BasicServicesKit'
|
||||
import { AdvertisingMonitorService } from 'biz_hxservice'
|
||||
|
||||
/**
|
||||
* 启动页面
|
||||
* 用于承接空白Logo页面,隐私弹窗确认页面以及广告
|
||||
* @author menghuiyuan@myhexin.com
|
||||
*/
|
||||
@Entry
|
||||
@Component
|
||||
struct LauncherPage {
|
||||
|
||||
@State userPrivacyDialogVisible : boolean = false;
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
|
||||
|
||||
async setSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context);
|
||||
// 前提:设置窗口为全屏窗口
|
||||
// PC
|
||||
|
||||
if( (deviceInfo.deviceType !== '2in1') ) {
|
||||
windowClass.setWindowLayoutFullScreen(true);
|
||||
} else {
|
||||
windowClass.setWindowLayoutFullScreen(false);
|
||||
}
|
||||
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
// 颜色属性为ARGB,将蒙尘设置为0%使其透明
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.setSystemBar()
|
||||
this.userPrivacyDialogVisible = !PreferenceService.getBooleanValueSync('Privacy', 'isUserAgree', false);
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
hilog.info(0xfff1, "LauncherPage.AgreePolicy onCancel", "clicked","");
|
||||
//如果用户拒绝了授权,关闭应用
|
||||
(getContext(this) as common.UIAbilityContext).terminateSelf(() => {});
|
||||
}
|
||||
|
||||
onAccept() {
|
||||
hilog.info(0xfff1, "LauncherPage.AgreePolicyOnAccept", "clicked","");
|
||||
PreferenceService.putVal('Privacy', 'isUserAgree', true, true);
|
||||
router.replaceUrl( { url : 'pages/Index'}, router.RouterMode.Single, (err) => {
|
||||
if (err) {
|
||||
console.error("Failed to start Index Page: " + err)
|
||||
return;
|
||||
}
|
||||
AdvertisingMonitorService.getInstance().init(getContext(this) as common.UIAbilityContext)
|
||||
AppConfigManager.getInstance().fetchIMEI().then((isSuccess : boolean) => {
|
||||
if (isSuccess) {
|
||||
AuthProviderService.getInstance().sendAuthRequest(ConnectionSeq.HQ);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
if (this.userPrivacyDialogVisible) {
|
||||
PrivacyUserDialog({
|
||||
cancel: this.onCancel,
|
||||
confirm: this.onAccept,
|
||||
title: $r("app.string.privacy_policy_title"),
|
||||
content: $r("app.string.privacy_policy_description"),
|
||||
leftBtnText: $r("app.string.text_disagreed"),
|
||||
rightBtnText: $r("app.string.text_agreed")
|
||||
}).zIndex(4)
|
||||
.align(Alignment.Center)
|
||||
.alignRules(
|
||||
{
|
||||
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
|
||||
'bottom': { 'anchor': '__container__', 'align': VerticalAlign.Bottom },
|
||||
'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start },
|
||||
'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
|
||||
})
|
||||
.id('dialog_privacy_policy')
|
||||
.backgroundColor($r('app.color.dialog_bg_99666666'))
|
||||
}
|
||||
Image($r('app.media.launch_logo'))
|
||||
.objectFit(ImageFit.Auto)
|
||||
.width('80%').height(205).alignRules({
|
||||
'bottom': {
|
||||
'anchor': "__container__",
|
||||
'align': VerticalAlign.Bottom
|
||||
},
|
||||
'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center },
|
||||
}).id("image")
|
||||
}.width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { DebugService, FloatingMinimizedIcon } from '@b2c-spi/hx_cbas_api';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
|
||||
/**
|
||||
* 最小化图标页面
|
||||
*/
|
||||
@Entry
|
||||
@Component
|
||||
struct MinimizedIconPage {
|
||||
build() {
|
||||
Column() {
|
||||
FloatingMinimizedIcon({
|
||||
onExpand: (): void => { DebugService.getInstance().showWindow(getContext(this) as common.UIAbilityContext); }
|
||||
})
|
||||
}
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import { QuotePage, AIAnalysisWebDialogController } from 'biz_quote';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import { StatusBar } from '../widget/StatusBar';
|
||||
import { AbilityConstant } from '@kit.AbilityKit';
|
||||
import emitter from '@ohos.events.emitter';
|
||||
import { AdsPageConfig, emitAdsPage, EmitterConstants, ScreenUtil } from 'biz_common';
|
||||
import { HXLog, HxTags } from 'biz_common';
|
||||
import { PeriodValue } from 'biz_futures_chart';
|
||||
import { CurveRuntimeData } from 'biz_quote/src/main/ets/util/CurveRuntimeData';
|
||||
import { SpotGoodsVarietyManager } from 'biz_chart_data_processor';
|
||||
import { router, window } from '@kit.ArkUI';
|
||||
import { TradeLineAskToChangeState } from 'biz_quote/src/main/ets/draw/overlay/hx_line/tradeline_manager/TradeLineAskToChangeState';
|
||||
|
||||
/**
|
||||
* 分时页承载Page
|
||||
* @author menghuiyuan@myhexin.com
|
||||
*/
|
||||
@Entry
|
||||
@Component
|
||||
struct QuoteDetailPage {
|
||||
private readonly TAG = "QuoteDetailPage"
|
||||
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
|
||||
/**
|
||||
* 这个pageShow 用于闪电下单,别删除
|
||||
*/
|
||||
@Provide pageShow: boolean = false;
|
||||
@Provide quoteDetailPageShow: boolean = false;
|
||||
@Provide showFlashTrade: boolean = false;
|
||||
@Provide showDrawLineStopLoss: boolean = false;
|
||||
@Provide showDrawOrder: boolean = false;
|
||||
@Provide bottomToolsPopupVisible: boolean = false;
|
||||
@StorageLink('isFullScreen') @Watch('onScreenChange') isLandscape: boolean = false; // 是否横屏
|
||||
@Provide isReLandscape: boolean = false; // 是否反向横屏
|
||||
// 横屏合约选择列表是否展开
|
||||
@Provide landStockListExpand: boolean = false;
|
||||
@Provide currentPeriod: PeriodValue = CurveRuntimeData.get().timeLinePeriod;
|
||||
|
||||
private resetPageStatusWhenLogin = () => {
|
||||
this.resetPageStatus();
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
emitter.on({
|
||||
eventId: EmitterConstants.USER_LOGIN
|
||||
}, this.resetPageStatusWhenLogin);
|
||||
// 进入分时/K 线页面时预加载现货类别列表(成功后内存级缓存,失败下次重试)
|
||||
SpotGoodsVarietyManager.getInstance().requestSpotCategoryIfNeeded()
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
HXLog.d("QuoteDetailPage",HxTags.quoteTags + "QuoteDetailPage aboutToDisappear , then unsubscribe (EmitterConstants.USER_LOGIN) ")
|
||||
emitter.off(EmitterConstants.USER_LOGIN,this.resetPageStatusWhenLogin)
|
||||
AIAnalysisWebDialogController.close()
|
||||
}
|
||||
|
||||
// 拦截返回操作
|
||||
onBackPress(): boolean | void {
|
||||
if (ScreenUtil.get().isSmallLand()) {
|
||||
// 类直板机横屏
|
||||
ScreenUtil.get().setPortrait()
|
||||
}
|
||||
|
||||
if (ScreenUtil.get().isFullScreen) {
|
||||
ScreenUtil.get().isFullScreen = false
|
||||
ScreenUtil.get().clickFull = false
|
||||
return true
|
||||
}
|
||||
if (this.showDrawLineStopLoss) {
|
||||
TradeLineAskToChangeState.getInstance().askToDiscard(() => {
|
||||
router.back()
|
||||
})
|
||||
return true
|
||||
}
|
||||
if (this.showDrawOrder) {
|
||||
TradeLineAskToChangeState.getInstance().askToDiscard(() => {
|
||||
router.back()
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
onPageShow(): void {
|
||||
//分时页前台时启用应用接续
|
||||
this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
|
||||
HXLog.i(this.TAG, "setMissionContinueState Active")
|
||||
});
|
||||
this.quoteDetailPageShow = true;
|
||||
this.pageShow = true;
|
||||
HXLog.d('QuoteDetailPage', HxTags.entryTags + 'QuoteDetailPage page show triggered')
|
||||
emitAdsPage(AdsPageConfig.QUOTE_DETAIL_PAGE_ID)
|
||||
}
|
||||
|
||||
onPageHide(): void {
|
||||
this.bottomToolsPopupVisible = false;
|
||||
this.quoteDetailPageShow = false;
|
||||
this.pageShow = false;
|
||||
//当分时页在后台时,关闭应用接续功能
|
||||
this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
|
||||
HXLog.i(this.TAG, "setMissionContinueState InActive")
|
||||
});
|
||||
emitAdsPage(AdsPageConfig.INVALID)
|
||||
if (AppStorage.get('isAbilityForeGround') == true) {
|
||||
this.onSwitchToOtherPage()
|
||||
}
|
||||
}
|
||||
|
||||
private resetPageStatus() {
|
||||
if (this.quoteDetailPageShow) {
|
||||
this.quoteDetailPageShow = false
|
||||
setTimeout(() => {
|
||||
this.quoteDetailPageShow = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private onSwitchToOtherPage() {
|
||||
AIAnalysisWebDialogController.close()
|
||||
}
|
||||
|
||||
private onScreenChange() {
|
||||
if (this.isLandscape) {
|
||||
// 横屏隐藏状态栏
|
||||
ScreenUtil.get().setSystemBarEnabled(false);
|
||||
} else {
|
||||
ScreenUtil.get().setSystemBarEnabled(true);
|
||||
}
|
||||
AIAnalysisWebDialogController.close()
|
||||
}
|
||||
|
||||
build() {
|
||||
Flex({ direction: FlexDirection.Column }) {
|
||||
StatusBar({
|
||||
bkColor: $r('app.color.surface_layer1_foreground'),
|
||||
maskVisible: this.bottomToolsPopupVisible,
|
||||
maskColor: $r('app.color.wt_40_000000')
|
||||
})
|
||||
.visibility(this.isLandscape ? Visibility.None : Visibility.Visible)
|
||||
QuotePage()
|
||||
.flexShrink(1)
|
||||
.height('100%')
|
||||
}
|
||||
.backgroundColor($r('app.color.surface_layer1_background'))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { SearchView} from 'biz_search'
|
||||
import router from '@ohos.router'
|
||||
import common from '@ohos.app.ability.common';
|
||||
import window from '@ohos.window';
|
||||
import { GlobalContext } from 'biz_common/src/main/ets/utils/GlobalContext';
|
||||
import { SearchScope, SearchScene } from 'biz_search/src/main/ets/model/SearchParam';
|
||||
import { TradeSearchScope } from 'biz_search/src/main/ets/model/TradeSearchScope';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct SearchPage {
|
||||
@State barHeight:number = 0;
|
||||
@Provide searchPageShown:boolean = false;
|
||||
@Provide isFromTrade: boolean = false
|
||||
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
// 行情页搜索范围
|
||||
searchScope: SearchScope = SearchScope.COMPREHENSIVE_SCOPE
|
||||
searchScene: string = SearchScene.NORMAL
|
||||
// 交易下单页搜索范围
|
||||
@State tradeSearchScope: TradeSearchScope | null = null
|
||||
|
||||
async setSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 获取状态栏高度
|
||||
let barHeight = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height
|
||||
let storeBarHeight = GlobalContext.statusBarHeight
|
||||
|
||||
if (barHeight !== storeBarHeight && barHeight !== 0) {
|
||||
this.barHeight = barHeight
|
||||
} else {
|
||||
this.barHeight = storeBarHeight
|
||||
}
|
||||
}
|
||||
|
||||
onBackPress(){
|
||||
|
||||
}
|
||||
|
||||
onPageShow(): void {
|
||||
this.searchPageShown = true
|
||||
}
|
||||
|
||||
onPageHide() {
|
||||
this.searchPageShown = false
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
this.setSystemBar()
|
||||
let routerParam = router.getParams() as Record<string, string | number | object>
|
||||
if (routerParam) {
|
||||
this.isFromTrade = routerParam['isFromTrade']?.toString() === '1'
|
||||
if (this.isFromTrade) {
|
||||
// 交易下单页 - 解析 TradeSearchScope
|
||||
let scopeType = routerParam['scopeType'] as number
|
||||
this.tradeSearchScope = TradeSearchScope.getSearchScope(scopeType) ?? TradeSearchScope.FUTURES_ALL
|
||||
} else {
|
||||
// 行情页 - 使用原有 SearchScope 字符串
|
||||
this.searchScope = routerParam['searchScope'] as string || SearchScope.COMPREHENSIVE_SCOPE
|
||||
this.searchScene = routerParam['searchScene']?.toString() ?? SearchScene.NORMAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Blank().height(px2vp(this.barHeight) +2).color($r('app.color.surface_layer1_foreground')).width('100%')
|
||||
SearchView({
|
||||
searchScope: this.searchScope,
|
||||
searchScene: this.searchScene,
|
||||
tradeSearchScope: this.tradeSearchScope,
|
||||
isFromTrade: this.isFromTrade
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import router from '@ohos.router'
|
||||
import window from '@ohos.window';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import { StatusBar } from '../widget/StatusBar';
|
||||
import { SelfCodeSecondPage } from 'biz_selfcode';
|
||||
|
||||
@Entry
|
||||
|
||||
@Component
|
||||
struct SecondPage {
|
||||
@Provide newsPageShow:number = 0; //0 not show 1 shown
|
||||
@State barHeight:number = 0;
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
|
||||
@State titlePrefix:string = '';
|
||||
@State codeListStr:string = '';
|
||||
@State marketListStr:string = '';
|
||||
@State stockListStr:string = ''
|
||||
@State initIndex:number = 0;
|
||||
async setSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 获取状态栏高度
|
||||
this.barHeight = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
async resetSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 从路由中获取参数并初始化页面数据
|
||||
*/
|
||||
aboutToAppear() {
|
||||
let routerParam = router.getParams() as Record<string, string>;
|
||||
this.codeListStr = routerParam.codeList;
|
||||
this.marketListStr = routerParam.marketList;
|
||||
this.stockListStr = routerParam.stockList
|
||||
this.titlePrefix = routerParam.title;
|
||||
|
||||
}
|
||||
aboutToDisappear(){
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
StatusBar()
|
||||
this.buildSecondPage(this.stockListStr,this.titlePrefix,this.initIndex)
|
||||
//this.buildTestPage(this.codeListStr, this.marketListStr, this.titlePrefix)
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
|
||||
@Builder
|
||||
buildSecondPage(stockListStr:string, titlePrefix:string, initIndex:number ) {
|
||||
SelfCodeSecondPage({selfCodeList:stockListStr,titleWord:titlePrefix,initIndex:initIndex})
|
||||
}
|
||||
@Builder
|
||||
buildTestPage(code:string, market:string,title:string) {
|
||||
Text('入参'+code+market+title)
|
||||
.fontSize(100)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import {
|
||||
buildHeaderRowData,
|
||||
CellData,
|
||||
HeaderItem,
|
||||
OriginalData,
|
||||
parseHeaderConfig,
|
||||
RowData,
|
||||
SortStatus,
|
||||
TableData
|
||||
} from '@b2b/hq_table'
|
||||
import router from '@ohos.router'
|
||||
import util from '@ohos.util'
|
||||
import { HQTableComponent, StockListPageParam } from 'biz_market'
|
||||
import { SubPageBar } from 'biz_selfcode'
|
||||
import common from '@ohos.app.ability.common'
|
||||
import { StatusBar } from '../widget/StatusBar'
|
||||
import { emitter } from '@kit.BasicServicesKit'
|
||||
import { EmitterConstants } from 'biz_common/Index'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct StockListPage {
|
||||
private frameId: number = 0
|
||||
private pageId: number = 0
|
||||
private headerConfig: Array<HeaderItem> = []
|
||||
//首次进入页面的默认排序字段
|
||||
private defaultSortId?: number
|
||||
//默认排序类型
|
||||
private defaultSortStatus: SortStatus = SortStatus.DESC
|
||||
//是否需要根据默认排序表头重新设置表头位置
|
||||
private isNeedReRankHeader: boolean = false
|
||||
//页面标题
|
||||
private title: string = ""
|
||||
@State hasInit: boolean = false
|
||||
@Provide tableData: TableData = TableData.createEmptyTableData()
|
||||
@State header: RowData = new RowData([], [], new OriginalData())
|
||||
@State requestNotifier: number = 0;
|
||||
@State barHeight: number = 0;
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
|
||||
private relinkCb = () => {
|
||||
this.requestNotifier++;
|
||||
}
|
||||
aboutToAppear() {
|
||||
let param = router.getParams() as StockListPageParam
|
||||
this.title = param.pageTitle
|
||||
this.frameId = param.pageId
|
||||
this.pageId = param.pageId
|
||||
this.defaultSortId = param.defaultSortId
|
||||
this.defaultSortStatus = param.defaultSortStatus
|
||||
this.isNeedReRankHeader = param.isNeedReRankHeader
|
||||
|
||||
if (param.headerConfig) {
|
||||
this.headerConfig = param.headerConfig
|
||||
this.initHeaderRow()
|
||||
} else if (param.headerConfigFile) {
|
||||
getContext(this).resourceManager.getRawFileContent(param.headerConfigFile, (error, value) => {
|
||||
if (error) {
|
||||
console.log(error.message)
|
||||
} else {
|
||||
let textDecoder = util.TextDecoder.create('utf-8')
|
||||
this.headerConfig.push(...parseHeaderConfig(textDecoder.decodeWithStream(value)))
|
||||
this.initHeaderRow()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.subscribeNetRelink()
|
||||
}
|
||||
|
||||
subscribeNetRelink() {
|
||||
emitter.on({
|
||||
eventId:EmitterConstants.NETWORK_TCP_RECONNECT,
|
||||
},this.relinkCb)
|
||||
}
|
||||
|
||||
aboutToDisappear() {
|
||||
emitter.off(EmitterConstants.NETWORK_TCP_RECONNECT,this.relinkCb)
|
||||
}
|
||||
|
||||
private initHeaderRow() {
|
||||
if (this.headerConfig.length > 0) {
|
||||
let headerRow = buildHeaderRowData(this.headerConfig)
|
||||
this.initDefaultSort(headerRow)
|
||||
this.header = headerRow
|
||||
this.hasInit = true
|
||||
}
|
||||
}
|
||||
|
||||
private initDefaultSort(header: RowData) {
|
||||
if (typeof this.defaultSortId !== 'undefined') {
|
||||
header.forEachCell((cellData: CellData) => {
|
||||
if (cellData.dataId === this.defaultSortId) {
|
||||
cellData.sortStatus = this.defaultSortStatus
|
||||
header.currentSortCell = cellData
|
||||
}
|
||||
})
|
||||
|
||||
if (this.isNeedReRankHeader && header.currentSortCell) {
|
||||
let index = header.scrollableCols.indexOf(header.currentSortCell)
|
||||
if (index >= 0) {
|
||||
header.scrollableCols.splice(index, 1)
|
||||
header.scrollableCols.splice(1, 0, header.currentSortCell)
|
||||
|
||||
let configIndex = index + header.fixCols.length
|
||||
let configItem = this.headerConfig.splice(configIndex, 1)[0]
|
||||
this.headerConfig.splice(1 + header.fixCols.length, 0, configItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPageShow() {
|
||||
this.requestNotifier++
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
StatusBar()
|
||||
//Blank().height(px2vp(this.barHeight) +2).color($r('app.color.subpage_title_red')).width('100%')
|
||||
SubPageBar({
|
||||
title: this.title
|
||||
})
|
||||
Column() {
|
||||
if (this.hasInit) {
|
||||
HQTableComponent({
|
||||
frameId: this.frameId,
|
||||
pageId: this.pageId,
|
||||
header: this.header,
|
||||
headerConfig: this.headerConfig,
|
||||
requestNotifier: this.requestNotifier
|
||||
})
|
||||
}
|
||||
}.layoutWeight(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* SystemIndicatorsManage 验证测试页面
|
||||
*
|
||||
* Author: LY
|
||||
* Date: 2026-01-12
|
||||
*/
|
||||
|
||||
import { SystemIndicatorsManage, CategoryInfo } from 'biz_indicator'
|
||||
import { IndicatorInfoModel } from 'biz_indicator'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct SystemIndicatorsTestPage {
|
||||
@State testResult: string = '点击按钮开始测试'
|
||||
@State isLoading: boolean = false
|
||||
private context: Context = getContext(this)
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text('SystemIndicatorsManage 测试')
|
||||
.fontSize(22)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
.margin({ top: 50, bottom: 30 })
|
||||
|
||||
Button(this.isLoading ? '测试中...' : '开始测试')
|
||||
.width('80%')
|
||||
.height(50)
|
||||
.fontSize(18)
|
||||
.enabled(!this.isLoading)
|
||||
.onClick(() => {
|
||||
this.runTest()
|
||||
})
|
||||
|
||||
Scroll() {
|
||||
Text(this.testResult)
|
||||
.fontSize(13)
|
||||
.fontColor('#333333')
|
||||
.padding(16)
|
||||
}
|
||||
.width('90%')
|
||||
.layoutWeight(1)
|
||||
.margin({ top: 20 })
|
||||
.backgroundColor('#F5F5F5')
|
||||
.borderRadius(10)
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.padding({ bottom: 30 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
}
|
||||
|
||||
async runTest() {
|
||||
this.isLoading = true
|
||||
this.testResult = '========== 开始测试 ==========\n'
|
||||
|
||||
try {
|
||||
const manage = SystemIndicatorsManage.getInstance()
|
||||
|
||||
// [1] 从 biz_indicator HSP 模块的 rawfile 初始化数据
|
||||
this.testResult += '\n[1] 从 biz_indicator HSP 模块加载数据...\n'
|
||||
await manage.initDataFromModule(this.context)
|
||||
if (manage.isInitialized()) {
|
||||
this.testResult += ' ✅ 初始化成功\n'
|
||||
this.testResult += ` 版本: ${manage.getDataVersion()}\n`
|
||||
} else {
|
||||
this.testResult += ' ❌ 初始化失败\n'
|
||||
return
|
||||
}
|
||||
|
||||
// [2] 验证指标数量
|
||||
this.testResult += '\n[2] 验证指标数量...\n'
|
||||
const count = manage.getIndicatorCount()
|
||||
this.testResult += ` 指标总数: ${count}\n`
|
||||
if (count === 55) {
|
||||
this.testResult += ' ✅ 数量正确 (55个)\n'
|
||||
} else {
|
||||
this.testResult += ` ⚠️ 数量: ${count}\n`
|
||||
}
|
||||
|
||||
// [3] 获取全部指标
|
||||
this.testResult += '\n[3] 获取全部指标 getAllIndicators()...\n'
|
||||
const allIndicators = manage.getAllIndicators()
|
||||
this.testResult += ` 返回 ${allIndicators.length} 个指标\n`
|
||||
if (allIndicators.length > 0) {
|
||||
const first = allIndicators[0]
|
||||
this.testResult += ` 第一个: ${first.indicatorId} - ${first.name}\n`
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
}
|
||||
|
||||
// [4] 获取主图指标
|
||||
this.testResult += '\n[4] 获取主图指标 getMainIndicators()...\n'
|
||||
const mainIndicators = manage.getMainIndicators()
|
||||
this.testResult += ` 主图指标数: ${mainIndicators.length}\n`
|
||||
if (mainIndicators.length > 0) {
|
||||
const names: string[] = []
|
||||
for (let i = 0; i < Math.min(5, mainIndicators.length); i++) {
|
||||
names.push(mainIndicators[i].indicatorId)
|
||||
}
|
||||
this.testResult += ` 前5个: ${names.join(', ')}\n`
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
}
|
||||
|
||||
// [5] 获取副图指标
|
||||
this.testResult += '\n[5] 获取副图指标 getViceIndicators()...\n'
|
||||
const viceIndicators = manage.getViceIndicators()
|
||||
this.testResult += ` 副图指标数: ${viceIndicators.length}\n`
|
||||
if (viceIndicators.length > 0) {
|
||||
const names: string[] = []
|
||||
for (let i = 0; i < Math.min(5, viceIndicators.length); i++) {
|
||||
names.push(viceIndicators[i].indicatorId)
|
||||
}
|
||||
this.testResult += ` 前5个: ${names.join(', ')}\n`
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
}
|
||||
|
||||
// [6] 查询单个指标
|
||||
this.testResult += '\n[6] 查询单个指标 getIndicatorInfo("MA")...\n'
|
||||
const maIndicator = manage.getIndicatorInfo('MA')
|
||||
if (maIndicator) {
|
||||
this.testResult += ` 名称: ${maIndicator.name}\n`
|
||||
this.testResult += ` 源码长度: ${maIndicator.sourceCode.length} 字符\n`
|
||||
this.testResult += ` 参数数量: ${maIndicator.paramsArray.length}\n`
|
||||
this.testResult += ` 线条数量: ${maIndicator.lineStyleArray.length}\n`
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
} else {
|
||||
this.testResult += ' ❌ 未找到 MA 指标\n'
|
||||
}
|
||||
|
||||
// [7] 搜索功能
|
||||
this.testResult += '\n[7] 搜索指标 searchIndicators("MACD")...\n'
|
||||
const searchResults = manage.searchIndicators('MACD')
|
||||
this.testResult += ` 搜索结果数: ${searchResults.length}\n`
|
||||
if (searchResults.length > 0) {
|
||||
for (let i = 0; i < searchResults.length; i++) {
|
||||
const item = searchResults[i]
|
||||
this.testResult += ` - ${item.indicatorId}: ${item.name}\n`
|
||||
}
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
}
|
||||
|
||||
// [8] 获取分类列表
|
||||
this.testResult += '\n[8] 获取分类列表 getAllCategories()...\n'
|
||||
const categories = manage.getAllCategories()
|
||||
this.testResult += ` 分类数量: ${categories.length}\n`
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const cat = categories[i]
|
||||
this.testResult += ` - ${cat.code}: ${cat.name}\n`
|
||||
}
|
||||
this.testResult += ' ✅ 成功\n'
|
||||
|
||||
// 测试完成
|
||||
this.testResult += '\n==============================\n'
|
||||
this.testResult += '🎉 所有测试通过!\n'
|
||||
this.testResult += '==============================\n'
|
||||
|
||||
} catch (error) {
|
||||
this.testResult += `\n\n❌ 测试异常: ${error}\n`
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import router from '@ohos.router'
|
||||
import web_webview from '@ohos.web.webview';
|
||||
import window from '@ohos.window';
|
||||
import common from '@ohos.app.ability.common';
|
||||
|
||||
/**
|
||||
* 暂时使用的页面
|
||||
*/
|
||||
@Entry
|
||||
@Component
|
||||
export default struct TemporaryBlockWebPage {
|
||||
private url: string = '';
|
||||
private title: string = ''
|
||||
private webController: WebviewController = new web_webview.WebviewController;
|
||||
|
||||
@State barHeight:number = 0;
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
|
||||
async setSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 获取状态栏高度
|
||||
this.barHeight = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
async resetSystemBar() {
|
||||
// 获取当前应用窗口
|
||||
let windowClass: window.Window = await window.getLastWindow(this.context)
|
||||
// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
|
||||
await windowClass.setWindowSystemBarProperties({
|
||||
navigationBarColor: "#00000000",
|
||||
statusBarColor: "#00000000",
|
||||
navigationBarContentColor: "#FFFFFFFF",
|
||||
statusBarContentColor: "#FFFFFFFF"
|
||||
})
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
this.setSystemBar()
|
||||
const routerParam = router.getParams() as Record<string, string>;
|
||||
this.url = routerParam.url;
|
||||
this.title = routerParam.title || '';
|
||||
}
|
||||
|
||||
aboutToDisappear(){
|
||||
this.resetSystemBar()
|
||||
try {
|
||||
this.webController.stop()
|
||||
} catch (e) {
|
||||
// WebView may already be detached.
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Blank().height(px2vp(this.barHeight) +2).color($r('app.color.subpage_title_red')).width('100%')
|
||||
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
|
||||
Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
|
||||
Column() {
|
||||
Image($r('app.media.toolbar_arrow_left'))
|
||||
.width(19)
|
||||
.height(24)
|
||||
.fillColor('#FFFFFF')
|
||||
.onClick(() => {
|
||||
router.back();
|
||||
});
|
||||
}.margin({ right: 16 })
|
||||
Text(this.title).fontSize(19).fontColor('#FFFFFF')
|
||||
}.margin({ left: 16 })
|
||||
}
|
||||
.height(44).backgroundColor($r('app.color.navbar_bg_red')).width('100%')
|
||||
|
||||
Web({
|
||||
src: this.url,
|
||||
controller: this.webController
|
||||
}).domStorageAccess(true)
|
||||
.mixedMode(MixedMode.All)
|
||||
.flexGrow(1)
|
||||
.flexShrink(1)
|
||||
.javaScriptProxy({
|
||||
object: {},
|
||||
name: 'Falcon',
|
||||
methodList: [],
|
||||
controller: this.webController,
|
||||
})
|
||||
.onPageEnd(() => {
|
||||
// 暂时处理办法, 不支持用户关闭回顾按钮和历史分时,手动去掉
|
||||
this.webController.runJavaScript(
|
||||
`var elementToRemove = document.querySelector('.fixed.flex.items-center.bg-foreground');
|
||||
if (elementToRemove) {
|
||||
elementToRemove.parentNode.removeChild(elementToRemove);
|
||||
}
|
||||
var leftArrow = document.querySelector("#bottom-popup-window > div.ml-auto.mr-16 > div.date-picker-container.flex.absolute.top-59.right-26 > div.date-picker-arrow-container.mr-4.text-sec.text-16.z-99.pl-4");
|
||||
if (leftArrow) {
|
||||
leftArrow.style.color = "rgba(0,0,0,.2)";
|
||||
}
|
||||
var parentElement = document.querySelector("#bottom-popup-window > div.ml-auto.mr-16 > div.date-picker-container.flex.absolute.top-59.right-26")
|
||||
parentElement.addEventListener('click', function(event) {
|
||||
var targetElement = event.target.closest("#bottom-popup-window > div.ml-auto.mr-16 > div.date-picker-container.flex.absolute.top-59.right-26 > div.date-picker-arrow-container.mr-4.text-sec.text-16.z-99.pl-4");
|
||||
var dateEl = event.target.closest("#bottom-popup-window > div.ml-auto.mr-16 > div.date-picker-container.flex.absolute.top-59.right-26 > div.date-picker-content.flex.items-center.justify-center.ml-auto.mb-5.z-99.intraDay.expanded");
|
||||
if (targetElement || dateEl) {
|
||||
event.stopPropagation();
|
||||
targetElement.style.color = "rgba(0,0,0,.2)";
|
||||
};
|
||||
}, true);
|
||||
`)
|
||||
})
|
||||
}
|
||||
.height('100%')
|
||||
.width('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
import { EmitterConstants, TradePageTabManager } from 'biz_common';
|
||||
import {
|
||||
MoniFutureAccountManager,
|
||||
MoniPage, TradeConstant, TradeHomePage, TradePage, WeituoLoginPage } from 'biz_trade';
|
||||
import { AccountProvider } from 'biz_trade/src/main/ets/features/account/models/AccountProvider';
|
||||
import { emitter } from '@kit.BasicServicesKit';
|
||||
import { TradeConstants } from 'biz_trade/src/main/ets/constants/TradeConstants';
|
||||
import { TradeApiInit } from 'biz_trade/src/main/ets/trade/api/TradeApiInit';
|
||||
import { TradeCustomTabManager } from 'biz_trade/src/main/ets/trade/manager/TradeCustomTabManager';
|
||||
import { HXUserService } from 'biz_hxservice';
|
||||
import { router } from '@kit.ArkUI';
|
||||
import { AutoLoginManager } from 'biz_trade/src/main/ets/trade/manager/AutoLoginManager';
|
||||
import { Account } from 'biz_trade/src/main/ets/constants/TypeConstants';
|
||||
import { TradeApiManager } from 'biz_trade/src/main/ets/trade/api/TradeApiManager';
|
||||
import { TradeOrderPageListType, fromValue } from 'biz_trade/src/main/ets/pages/homepage/TradeHomeConstants';
|
||||
import { TradeSettingUtils } from 'biz_trade/src/main/ets/utils/TradeSettingUtils';
|
||||
import { TradeParamManager } from 'biz_trade/src/main/ets/utils/TradeParamManager';
|
||||
|
||||
const TAB_INDEX_REAL = 0
|
||||
const TAB_INDEX_MONI = 1
|
||||
|
||||
/**
|
||||
* 交易tab的通用父页面,主要用来实现顶部tab切换(A股、港美股、基金、期货)
|
||||
*/
|
||||
@Preview
|
||||
@Component
|
||||
export default struct TradeBizPageContainer {
|
||||
@ObjectLink tabManager: TradePageTabManager;
|
||||
@State @Watch('onTabIndexChange') currentIndex: number = 0;
|
||||
/**
|
||||
* 是否有登录的实盘账号
|
||||
*/
|
||||
@State hasRealLoggedAccount: boolean = false
|
||||
/**
|
||||
* 是否有登录的模拟账号
|
||||
*/
|
||||
@State hasSimulateLoggedAccount: boolean = false
|
||||
@State isVisible: boolean = false
|
||||
/**
|
||||
* 是否显示交易下单页面
|
||||
*/
|
||||
@State showTradeOrderPage: boolean = false
|
||||
/**
|
||||
* 交易下单tab index
|
||||
*/
|
||||
@State currentTabIndex: number = 0
|
||||
|
||||
/**
|
||||
* 当前账号
|
||||
*/
|
||||
@State currentAccount: Account = undefined
|
||||
private swiperController = new SwiperController()
|
||||
|
||||
/**
|
||||
* 登录订阅回调
|
||||
*/
|
||||
private hasLoginAccountCallBack = () => {
|
||||
this.hasRealLoggedAccount = AccountProvider.getInstance().hasRealLoggedAccount()
|
||||
this.hasSimulateLoggedAccount = AccountProvider.getInstance().hasSimulateLoggedAccount()
|
||||
this.currentAccount = AccountProvider.getInstance().getCurrentAccount()
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出订阅回调
|
||||
*/
|
||||
private loginOutAccountCallBack = () => {
|
||||
this.hasRealLoggedAccount = AccountProvider.getInstance().hasRealLoggedAccount()
|
||||
this.hasSimulateLoggedAccount = AccountProvider.getInstance().hasSimulateLoggedAccount()
|
||||
this.showTradeOrderPage = false
|
||||
this.currentAccount = undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易回报点击回调
|
||||
*/
|
||||
private CJHBPushCallBack = (eventData: emitter.EventData) => {
|
||||
if (this.showTradeOrderPage) {
|
||||
// 交易下单页面已经有监听处理
|
||||
return
|
||||
}
|
||||
// 处理在交易首页需要切换到交易下单再切列表
|
||||
if (eventData && eventData.data) {
|
||||
let tradeType: number | undefined = eventData.data[TradeConstant.KEY_TRADE_TYPE]
|
||||
if (tradeType) {
|
||||
TradeCustomTabManager.get().setTradeType(tradeType);
|
||||
}
|
||||
let tableIndex: number | undefined = eventData.data[TradeConstant.KEY_TABLE_INDEX]
|
||||
this.gotoOrderPage(fromValue(tableIndex ?? 0))
|
||||
}
|
||||
}
|
||||
|
||||
onTabIndexChange() {
|
||||
this.swiperController.changeIndex(this.currentIndex)
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
TradeApiInit.getInstance().initTradeCacheData();
|
||||
MoniFutureAccountManager.getInstance().addMoniAccount()
|
||||
this.tabManager.visibilityChangeListener = (isVisible) => {
|
||||
this.isVisible = isVisible
|
||||
if (isVisible) {
|
||||
this.onPageShow()
|
||||
}else {
|
||||
this.onPageHide()
|
||||
}
|
||||
}
|
||||
emitter.on({ eventId: TradeConstants.TRADE_ACCOUNT_LOGOUT }, this.loginOutAccountCallBack)
|
||||
emitter.on(EmitterConstants.CJHB_PUSH_CLICK, this.CJHBPushCallBack)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
emitter.off(TradeConstants.TRADE_ACCOUNT_LOGOUT, this.loginOutAccountCallBack)
|
||||
emitter.off(EmitterConstants.CJHB_PUSH_CLICK, this.CJHBPushCallBack)
|
||||
}
|
||||
|
||||
onPageShow(): void {
|
||||
const needEnterOrderPage = TradeParamManager.get().getNeedEnterOrderPage()
|
||||
this.hasRealLoggedAccount = AccountProvider.getInstance().hasRealLoggedAccount()
|
||||
this.hasSimulateLoggedAccount = AccountProvider.getInstance().hasSimulateLoggedAccount()
|
||||
this.currentAccount = AccountProvider.getInstance().getCurrentAccount()
|
||||
if (needEnterOrderPage) {
|
||||
this.showTradeOrderPage = TradeParamManager.get().getNeedEnterOrderPage()
|
||||
this.currentTabIndex = TradeOrderPageListType.POSITION
|
||||
TradeParamManager.get().setNeedEnterOrderPage(false)
|
||||
this.currentIndex = this.currentAccount?.isMoni() ? TAB_INDEX_MONI : TAB_INDEX_REAL
|
||||
}
|
||||
this.changeTabAccordingAccount()
|
||||
emitter.on({eventId: TradeConstants.TRADE_ACCOUNT_LOGIN}, this.hasLoginAccountCallBack)
|
||||
}
|
||||
|
||||
onPageHide(): void {
|
||||
emitter.off(TradeConstants.TRADE_ACCOUNT_LOGIN, this.hasLoginAccountCallBack)
|
||||
}
|
||||
|
||||
build() {
|
||||
Flex({ direction: FlexDirection.Column }) {
|
||||
if (this.currentAccount && ((this.hasRealLoggedAccount && this.currentIndex == TAB_INDEX_REAL) || (this.hasSimulateLoggedAccount && this.currentIndex == TAB_INDEX_MONI))) {
|
||||
if (this.showTradeOrderPage) {
|
||||
TradePage({
|
||||
tradeTabShow: this.tabManager.tabVisibility,
|
||||
isShowStatusBar: false,
|
||||
currentTabIndex: this.currentTabIndex,
|
||||
attachPage: "TradePageFromIndex",
|
||||
clickBack: () => {
|
||||
this.onOrderPageBack()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.TitleBuilder()
|
||||
TradeHomePage({
|
||||
tradeTabShow: this.tabManager.tabVisibility,
|
||||
tradeAccount: this.currentAccount,
|
||||
jumpOrderPage: (listType) => {
|
||||
this.gotoOrderPage(listType)
|
||||
},
|
||||
loginCallBack: {
|
||||
onSwitchSuccess: () => {
|
||||
this.loginSuccess(false)
|
||||
},
|
||||
onSwitchFailed: () => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.TitleBuilder()
|
||||
Divider().vertical(false).height(0.5).color($r('app.color.elements_others_divider_primary'))
|
||||
Swiper() {
|
||||
if (this.currentIndex == TAB_INDEX_MONI) {
|
||||
MoniPage({
|
||||
onForeground: this.isVisible,
|
||||
loginSuccess: () => {
|
||||
this.loginSuccess()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
WeituoLoginPage({
|
||||
tradeTabShow: this.tabManager.tabVisibility,
|
||||
loginSucc: () => {
|
||||
this.loginSuccess()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
.index(this.currentIndex)
|
||||
.indicator(false)
|
||||
.disableSwipe(true)
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
|
||||
@Builder
|
||||
TitleBuilder() {
|
||||
Row() {
|
||||
Blank()
|
||||
.width(40)
|
||||
.height('100%')
|
||||
|
||||
Row() {
|
||||
Row() {
|
||||
Text("实盘")
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.fontSize(this.currentIndex === TAB_INDEX_REAL ? 22 : 16)
|
||||
.fontWeight(this.currentIndex === TAB_INDEX_REAL ? FontWeight.Medium : 400)
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({ top: this.currentIndex === TAB_INDEX_REAL ? 0 : 6, right: 12 })
|
||||
.onClick(() => {
|
||||
if (this.currentIndex == TAB_INDEX_REAL) {
|
||||
return
|
||||
}
|
||||
this.currentIndex = TAB_INDEX_REAL;
|
||||
this.selectTabReLogin()
|
||||
})
|
||||
|
||||
Text("模拟")
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.fontSize(this.currentIndex === TAB_INDEX_REAL ? 16 : 22)
|
||||
.fontWeight(this.currentIndex === TAB_INDEX_REAL ? 400 : FontWeight.Medium)
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({ top: this.currentIndex === TAB_INDEX_REAL ? 6 : 0 })
|
||||
.onClick(() => {
|
||||
if (this.currentIndex == TAB_INDEX_MONI) {
|
||||
return
|
||||
}
|
||||
AutoLoginManager.getInstance().cancelAutoLoginRequest('click moni btn')
|
||||
this.currentIndex = TAB_INDEX_MONI;
|
||||
this.selectTabReLogin()
|
||||
})
|
||||
}
|
||||
.height(22)
|
||||
}
|
||||
.height('100%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.layoutWeight(1)
|
||||
|
||||
Row() {
|
||||
Image($r('app.media.icon_bot_24'))
|
||||
.fillColor($r('app.color.elements_icon_primary_02'))
|
||||
.width(24)
|
||||
.height(24)
|
||||
.objectFit(ImageFit.Contain)
|
||||
.onClick(() => {
|
||||
this.onAgentClick()
|
||||
})
|
||||
}
|
||||
.margin({ right: 16 })
|
||||
}
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.height(44)
|
||||
.width('100%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换tab重新登录
|
||||
*/
|
||||
private selectTabReLogin() {
|
||||
let tradeAccount: Account = undefined
|
||||
if (this.currentIndex == TAB_INDEX_REAL) {
|
||||
tradeAccount = AccountProvider.getInstance().getLastRealLoginAccount()
|
||||
} else {
|
||||
tradeAccount = AccountProvider.getInstance().getLastSimulateLoginAccount()
|
||||
}
|
||||
TradeApiManager.getInstance().reLoginAccount('trade tab', tradeAccount, () => {
|
||||
this.loginSuccess(false)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
private loginSuccess(canEnterOrderPage: boolean = true) {
|
||||
this.hasRealLoggedAccount = AccountProvider.getInstance().hasRealLoggedAccount()
|
||||
this.hasSimulateLoggedAccount = AccountProvider.getInstance().hasSimulateLoggedAccount()
|
||||
this.currentAccount = AccountProvider.getInstance().getCurrentAccount()
|
||||
// 登录后根据设置自动进入下单页(切换tab触发的登录除外)
|
||||
if (this.currentAccount && canEnterOrderPage && TradeSettingUtils.getInstance().isDirectEnterOrderPage()) {
|
||||
this.currentTabIndex = TradeOrderPageListType.POSITION
|
||||
this.showTradeOrderPage = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从交易下单页返回
|
||||
*/
|
||||
private onOrderPageBack() {
|
||||
this.showTradeOrderPage = false
|
||||
this.changeTabAccordingAccount()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据账号切换tab
|
||||
*/
|
||||
private changeTabAccordingAccount() {
|
||||
if (this.hasRealLoggedAccount && this.hasSimulateLoggedAccount) {
|
||||
if (AccountProvider.getInstance().getCurrentAccount()?.isMoni()) {
|
||||
this.currentIndex = TAB_INDEX_MONI
|
||||
} else {
|
||||
this.currentIndex = TAB_INDEX_REAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到交易下单页
|
||||
*/
|
||||
private gotoOrderPage(listType: TradeOrderPageListType) {
|
||||
this.currentTabIndex = listType
|
||||
this.showTradeOrderPage = true
|
||||
}
|
||||
|
||||
private onAgentClick() {
|
||||
if (this.autoLogin()) {
|
||||
return
|
||||
}
|
||||
router.pushUrl({
|
||||
url: 'pages/WebViewComponentPage', params: {
|
||||
url: 'https://eq.10jqka.com.cn/ai/webapp/?source=ths_liejin_qihuo&llm=1&showHeader=1&navLeftShow=0&comefrom=%1s&isSupportInputUpload=1&fullscreen=1',
|
||||
title: '',
|
||||
updateH5Title: '0'
|
||||
}
|
||||
},
|
||||
router.RouterMode.Single, (err) => {
|
||||
if (err) {
|
||||
console.error(`go to message page failed , cause : ${err.message}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private autoLogin(): boolean {
|
||||
if (!HXUserService.getInstance().isLoggedIn()) {
|
||||
const topRoutePath = router.getState().path
|
||||
router.pushUrl({
|
||||
url: 'pages/login/LoginPage',
|
||||
params: {
|
||||
flexSwitchFlag: true,
|
||||
sourcePage: `${topRoutePath}${router.getState().name}`
|
||||
}
|
||||
})
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import router from '@ohos.router'
|
||||
import { TrendForecastPageParams, TrendForecastView } from 'biz_forecast'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct TrendForecastPage {
|
||||
@State params: TrendForecastPageParams = new TrendForecastPageParams()
|
||||
|
||||
aboutToAppear(): void {
|
||||
const routerParams = router.getParams() as Record<string, string | number | boolean | undefined>
|
||||
this.params = TrendForecastPageParams.fromRecord(routerParams)
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
TrendForecastView({
|
||||
params: this.params
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
import router from '@ohos.router'
|
||||
import { WebViewComponent, CustomWebviewController, WebViewRightBarButton, NavigationBarViewModel, WebViewCallbackRegistry } from 'biz_webview';
|
||||
import display from '@ohos.display';
|
||||
import window from '@ohos.window';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import { StatusBar } from '../widget/StatusBar';
|
||||
import { WebViewUrlUtil } from 'biz_webview/src/main/ets/Util/WebViewUrlUtil';
|
||||
import { KeyCode } from '@kit.InputKit';
|
||||
import { handleBackEvent } from 'biz_common/src/main/ets/pc/PCAdapter';
|
||||
import { VideoComponent } from 'lib_media';
|
||||
import { GlobalContext, WebJumpLinkWhiteManager } from 'biz_common';
|
||||
import { getResourceString } from '@b2c/first_page/src/main/ets/util/ResourceUtil';
|
||||
|
||||
// 返回回调注册 key
|
||||
const BACK_PRESS_CALLBACK_KEY = 'backPressCallbackKey'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
export default struct WebViewComponentPage {
|
||||
private webViewController: CustomWebviewController = new CustomWebviewController()
|
||||
private url: string = ''
|
||||
@Prop title: string | Resource = ''
|
||||
@Prop fullscreen: boolean = false
|
||||
@Prop showStatusBar: boolean = true
|
||||
private refreshOnWidthChange: boolean = false;
|
||||
@State onShow: boolean = false
|
||||
@State barHeight: number = 0;
|
||||
navigationBarHandler: NavigationBarViewModel = new NavigationBarViewModel();
|
||||
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
|
||||
@StorageLink('displayBreakPoint') @Watch('displayChanged') curBp: string = 'sm'
|
||||
useCookie: boolean = false
|
||||
@Provide videoPlayerUrl: string | null = ''
|
||||
@State videoComponentHeight: number = 225
|
||||
@State updateH5Title: boolean = true
|
||||
// 是否显示三方资讯标题
|
||||
@State isShowOtherTitle: boolean = false;
|
||||
@State otherUrl: string | null = null;
|
||||
//登录状态改变后刷新web
|
||||
isLoginRefreshWeb:boolean = false
|
||||
// 自定义返回事件回调
|
||||
private onBackPressCallback?: () => void
|
||||
private backPressCallbackKey?: string
|
||||
|
||||
registerJSEvent() {
|
||||
display.getDefaultDisplaySync()
|
||||
}
|
||||
|
||||
// 网页标题变更
|
||||
onTitleReceive = (title: string) => {
|
||||
this.title = title
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
let routerParam = router.getParams() as Record<string, string>;
|
||||
this.url = routerParam.url;
|
||||
routerParam = WebViewUrlUtil.assign(routerParam, this.url)
|
||||
let defaultTitle = ''
|
||||
try {
|
||||
defaultTitle = this.context.resourceManager.getStringSync($r('app.string.webview_default_title').id);
|
||||
this.refreshOnWidthChange = routerParam.refreshOnWidthChange === "true" ? true : false;
|
||||
} catch (error) {
|
||||
console.error(`getStringSync failed, error code: ${error.code}, message: ${error.message}.`)
|
||||
}
|
||||
this.title = routerParam.title || defaultTitle;
|
||||
const webId = routerParam['webid']
|
||||
this.fullscreen =
|
||||
routerParam['fullscreen'] == '1' || routerParam['notitlebar'] == '1' || routerParam['ishiddenbar'] == '1' ||
|
||||
webId == '2305' || webId == '4224' ||
|
||||
routerParam['express'] == 'fullscreen_web'
|
||||
if (this.fullscreen) {
|
||||
this.showStatusBar = false
|
||||
}
|
||||
this.isLoginRefreshWeb = ""+routerParam['isLoginRefreshWeb'] === "true" ? true : false;
|
||||
if (routerParam['showStatusBar'] == '1' && routerParam['StayHighForStatusBar'] != '0' || webId == '4224') {
|
||||
this.showStatusBar = true
|
||||
}
|
||||
if (""+routerParam['useCookie'] === "true") {
|
||||
this.useCookie = true
|
||||
}
|
||||
this.updateH5Title = routerParam['updateH5Title'] != '-1'
|
||||
this.isShowOtherTitle = routerParam['isShowOtherTitle'] == 'true'
|
||||
|
||||
const isOtherUrl = WebJumpLinkWhiteManager.getInstance().isNotInWhiteUrls(this.url);
|
||||
if (this.isShowOtherTitle && isOtherUrl) {
|
||||
// 更改标题为三方风险提示
|
||||
this.title = getResourceString(GlobalContext.get(), $r('app.string.bridge_risk_statement'))
|
||||
this.otherUrl = this.url
|
||||
}
|
||||
|
||||
// 获取返回事件回调
|
||||
const callbackKey = routerParam[BACK_PRESS_CALLBACK_KEY]
|
||||
if (callbackKey) {
|
||||
this.backPressCallbackKey = callbackKey
|
||||
this.onBackPressCallback = WebViewCallbackRegistry.getBackPressCallback(callbackKey)
|
||||
}
|
||||
this.registerJSEvent()
|
||||
this.initThirdArticleWhiteList()
|
||||
}
|
||||
|
||||
aboutToDisappear() {
|
||||
if (this.backPressCallbackKey) {
|
||||
WebViewCallbackRegistry.unregisterBackPressCallback(this.backPressCallbackKey)
|
||||
this.backPressCallbackKey = undefined
|
||||
}
|
||||
|
||||
// 停止 WebView 加载,释放 Chromium 渲染资源
|
||||
try {
|
||||
if (this.webViewController && this.webViewController.isAttached) {
|
||||
this.webViewController.stop()
|
||||
}
|
||||
} catch (e) {
|
||||
// WebView 可能已销毁
|
||||
}
|
||||
}
|
||||
|
||||
// 拦截系统返回上一级
|
||||
onBackPress() {
|
||||
// 通知业务方用户点击了返回
|
||||
if (this.onBackPressCallback) {
|
||||
this.onBackPressCallback()
|
||||
}
|
||||
|
||||
if (this.webViewController && this.webViewController.isAttached && this.webViewController.accessBackward()) {
|
||||
this.webViewController.backward()
|
||||
return true
|
||||
}
|
||||
return !this.navigationBarHandler.canGoBack()
|
||||
}
|
||||
|
||||
onPageShow() {
|
||||
this.onShow = true
|
||||
}
|
||||
|
||||
onPageHide() {
|
||||
if (this.onShow == true) {
|
||||
this.onShow = false
|
||||
}
|
||||
}
|
||||
|
||||
playerVideoVisibility(): Visibility {
|
||||
const url = this.videoPlayerUrl
|
||||
if (!url || url == null || url == '') {
|
||||
return Visibility.None
|
||||
}
|
||||
return Visibility.Visible
|
||||
}
|
||||
|
||||
displayChanged() {
|
||||
this.videoPlayerUrl = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化第三方风险文章白名单列表
|
||||
* 这里做一个兜底,防止客户端启动后,第一次请求白名单接口失败
|
||||
* 导致功能不正常
|
||||
*/
|
||||
private initThirdArticleWhiteList() {
|
||||
if (WebJumpLinkWhiteManager.getInstance().isWhiteListEmpty()) {
|
||||
WebJumpLinkWhiteManager.getInstance().loadWhiteJumpLink();
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.showStatusBar) {
|
||||
StatusBar()
|
||||
}
|
||||
if (!this.fullscreen) {
|
||||
RelativeContainer() {
|
||||
Image($r("app.media.icon_arrow_left_24"))
|
||||
.fillColor($r('app.color.elements_icon_tertiary'))
|
||||
.width(24)
|
||||
.height(24)
|
||||
.alignRules({
|
||||
center: { anchor: "__container__", align: VerticalAlign.Center },
|
||||
left: { anchor: "__container__", align: HorizontalAlign.Start }
|
||||
})
|
||||
.margin({ left: 16 })
|
||||
.hoverEffect(HoverEffect.Highlight)
|
||||
.onClick(() => {
|
||||
if (!this.webViewController.isAttached) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.onBackPressCallback) {
|
||||
this.onBackPressCallback()
|
||||
}
|
||||
|
||||
if (this.webViewController.accessBackward()) {
|
||||
this.webViewController.backward()
|
||||
} else if (this.navigationBarHandler.canGoBack()) {
|
||||
router.back();
|
||||
}
|
||||
})
|
||||
.id('left')
|
||||
Text(this.title)
|
||||
.maxFontSize(19)
|
||||
.minFontSize(17)
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.textAlign(TextAlign.Center)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.constraintSize({ maxWidth: '70%' })
|
||||
.alignRules({
|
||||
center: { anchor: "__container__", align: VerticalAlign.Center },
|
||||
middle: { anchor: "__container__", align: HorizontalAlign.Center }
|
||||
})
|
||||
WebViewRightBarButton({ navigationBarHandler: this.navigationBarHandler })
|
||||
.alignRules({
|
||||
center: { anchor: "__container__", align: VerticalAlign.Center },
|
||||
right: { anchor: "__container__", align: HorizontalAlign.End }
|
||||
})
|
||||
.margin({ right: 8 })
|
||||
}
|
||||
.height(44).backgroundColor($r("app.color.surface_layer1_foreground")).width('100%')
|
||||
}
|
||||
|
||||
VideoComponent({ onShow: this.onShow })
|
||||
.width('100%')
|
||||
.height(this.videoComponentHeight)
|
||||
.visibility(this.playerVideoVisibility())
|
||||
.onAreaChange((oldValue, newValue) => {
|
||||
const ratio = 9 / 16
|
||||
let newWidth = newValue.width as number
|
||||
if (newValue.width > 0 && this.videoComponentHeight !== newWidth * ratio) {
|
||||
this.videoComponentHeight = newWidth * ratio
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
WebViewComponent({
|
||||
urlString: this.url,
|
||||
onShow: this.onShow,
|
||||
webViewController: this.webViewController,
|
||||
onTitleReceive: this.onTitleReceive,
|
||||
navigationBarHandler: this.navigationBarHandler,
|
||||
refreshOnWidthChange: this.refreshOnWidthChange,
|
||||
useCookie: this.useCookie,
|
||||
isLoginRefreshWeb:this.isLoginRefreshWeb,
|
||||
overScrollMode: OverScrollMode.NEVER,
|
||||
updateH5Title: this.updateH5Title,
|
||||
isShowOtherTitle: this.isShowOtherTitle,
|
||||
otherUrl: this.otherUrl,
|
||||
receiveFullScreenEvent: (isFullScreen: boolean) => {
|
||||
this.fullscreen = isFullScreen
|
||||
if (isFullScreen) {
|
||||
this.showStatusBar = false
|
||||
}
|
||||
},
|
||||
onMediaUrlChanged: (url: string | null) => {
|
||||
this.videoPlayerUrl = url
|
||||
},
|
||||
})
|
||||
.margin({ top: 0 })
|
||||
.layoutWeight(1)
|
||||
|
||||
}
|
||||
|
||||
.onKeyEvent((event: KeyEvent) => {
|
||||
handleBackEvent(event)
|
||||
})
|
||||
.height('100%')
|
||||
.width('100%')
|
||||
.id('biz_webview/container')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
import { promptAction, PromptAction, router } from '@kit.ArkUI';
|
||||
import { AppInfoConfig, GlobalContext, HXLog } from 'biz_common';
|
||||
import { WebTitleView } from 'biz_login/src/main/ets/view/WebTitleView';
|
||||
import pasteboard from '@ohos.pasteboard';
|
||||
import { AppUtil, ToastUtil } from '@pura/harmony-utils';
|
||||
import { WTClientInfoUtil } from 'biz_trade/src/main/ets/utils/WTClientInfoUtil';
|
||||
import { url } from '@kit.ArkTS';
|
||||
import { resourceManager } from '@kit.LocalizationKit';
|
||||
import { common, UIAbility } from '@kit.AbilityKit';
|
||||
|
||||
interface IAboutItem {
|
||||
title: Resource;
|
||||
showArrow: boolean;
|
||||
showDivider: boolean;
|
||||
routeName?: string;
|
||||
isJumpWeb:boolean
|
||||
jumpWebUrl?:Resource;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct AboutUs {
|
||||
@State appVersion: string|Resource = "版本 "+AppInfoConfig.getVersionName();
|
||||
@State deviceId: string = "C1864EBD-9F88-496D-A424-253F7163521D";
|
||||
|
||||
private aboutItems: IAboutItem[] = [
|
||||
{
|
||||
title: $r('app.string.about_us_product_intro'),
|
||||
showArrow: true,
|
||||
showDivider: true,
|
||||
isJumpWeb:false,
|
||||
routeName: "pages/aboutus/ProductIntroduction"
|
||||
},
|
||||
{
|
||||
title: $r('app.string.about_us_contact'),
|
||||
showArrow: true,
|
||||
showDivider: true,
|
||||
isJumpWeb:false,
|
||||
routeName: "pages/aboutus/ContactUs"
|
||||
},
|
||||
{
|
||||
title: $r('app.string.about_us_privacy_policy'),
|
||||
showArrow: true,
|
||||
showDivider: true,
|
||||
isJumpWeb:true,
|
||||
jumpWebUrl:$r('app.string.revoke_privacy_protocol_url')
|
||||
},
|
||||
{
|
||||
title: $r('app.string.about_us_personal_info_download'),
|
||||
showArrow: true,
|
||||
showDivider: true,
|
||||
isJumpWeb:true,
|
||||
jumpWebUrl:$r('app.string.user_info_download_url')
|
||||
},
|
||||
{
|
||||
title: $r('app.string.user_agreement_text'),
|
||||
showArrow: true,
|
||||
showDivider: true,
|
||||
isJumpWeb:true,
|
||||
jumpWebUrl:$r('app.string.about_user_agreement_url')
|
||||
},
|
||||
{
|
||||
title: $r('app.string.about_us_recommendation_settings'),
|
||||
showArrow: true,
|
||||
showDivider: true,
|
||||
isJumpWeb:true,
|
||||
jumpWebUrl: $r('app.string.about_direct_promotion')
|
||||
}
|
||||
]
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.deviceId = WTClientInfoUtil.getDeviceId()
|
||||
}
|
||||
|
||||
private copyDataToPasteBoard(dataText:string){
|
||||
try {
|
||||
let sysPasteBoard = pasteboard.getSystemPasteboard()
|
||||
let data = pasteboard.createPlainTextData(dataText)
|
||||
sysPasteBoard.setDataSync(data)
|
||||
promptAction.showToast({message:$r('app.string.copy_success'),duration:3000,alignment:Alignment.Center})
|
||||
}catch (e){
|
||||
promptAction.showToast({message:$r('app.string.copy_fail'),duration:3000,alignment:Alignment.Center})
|
||||
}
|
||||
}
|
||||
|
||||
private onItemClick(item:IAboutItem){
|
||||
if (item.isJumpWeb && item.jumpWebUrl) {
|
||||
let jumpUrl = AppUtil.getContext().resourceManager.getStringSync(item.jumpWebUrl)
|
||||
let webTitle = AppUtil.getContext().resourceManager.getStringSync(item.title)
|
||||
if (jumpUrl && webTitle) {
|
||||
router.pushUrl({
|
||||
url: 'pages/WebViewComponentPage', params: {
|
||||
url: jumpUrl,
|
||||
title: webTitle,
|
||||
updateH5Title: '0',
|
||||
useCookie:true
|
||||
}
|
||||
})
|
||||
}
|
||||
}else {
|
||||
if (item.routeName) {
|
||||
router.pushUrl({ url: item.routeName });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private openInSystemBrowser(url:string){
|
||||
try {
|
||||
|
||||
let context = getContext(this) as common.UIAbilityContext;
|
||||
context.startAbility({action:'ohos.want.action.viewData',uri:url})
|
||||
}catch (e) {
|
||||
promptAction.showToast({message:$r('app.string.web_link_fail'),duration:3000,alignment:Alignment.Center})
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Row().width('100%').height(px2vp(GlobalContext.statusBarHeight)).backgroundColor($r('app.color.surface_layer1_foreground')).flexShrink(0)
|
||||
WebTitleView({ title: '' })
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
|
||||
Column() {
|
||||
Image($r('app.media.theme_icon_system_config_app'))
|
||||
.width(100)
|
||||
.height(120)
|
||||
.margin({ top: 20 })
|
||||
|
||||
Text(this.appVersion)
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.margin({ top: 8, bottom: 40 })
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
|
||||
Column() {
|
||||
ForEach(this.aboutItems, (item: IAboutItem, index: number) => {
|
||||
Row() {
|
||||
Text(item.title)
|
||||
.fontSize(16)
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.layoutWeight(1)
|
||||
|
||||
if (item.value) {
|
||||
Text(item.value)
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.margin({ right: 4 })
|
||||
|
||||
Image($r('app.media.icon_arrow_right_line'))
|
||||
.fillColor($r('app.color.elements_icon_primary_02'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
|
||||
}
|
||||
|
||||
if (item.showArrow) {
|
||||
Image($r('app.media.icon_arrow_right_line'))
|
||||
.fillColor($r('app.color.elements_icon_primary_02'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height(56)
|
||||
.padding({ left: 16, right: 16 })
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.onClick(() => {
|
||||
this.onItemClick(item)
|
||||
})
|
||||
|
||||
if (item.showDivider) {
|
||||
Divider()
|
||||
.strokeWidth(0.5)
|
||||
.color($r('app.color.elements_others_divider_primary'))
|
||||
.margin({ left: 16, right: 16 })
|
||||
}
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.margin({ left: 12, right: 12 })
|
||||
|
||||
Column() {
|
||||
Row() {
|
||||
Column(){
|
||||
Text($r('app.string.about_us_device_id'))
|
||||
.fontSize(16)
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.align(Alignment.Start)
|
||||
Text(this.deviceId)
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.elements_text_primary_02'))
|
||||
.align(Alignment.Start)
|
||||
.margin({top:6})
|
||||
}.layoutWeight(1)
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.justifyContent(FlexAlign.Start)
|
||||
Image($r('app.media.theme_icon_system_config_copy'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
}
|
||||
.width('100%')
|
||||
.height(56)
|
||||
.padding({ left: 16, right: 16 })
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.onClick(() => {
|
||||
this.copyDataToPasteBoard(this.deviceId)
|
||||
})
|
||||
Divider()
|
||||
.strokeWidth(0.5)
|
||||
.color($r('app.color.elements_others_divider_primary'))
|
||||
.margin({ left: 16, right: 16 })
|
||||
}
|
||||
.width('100%')
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
.margin({ left: 12, right: 12 })
|
||||
|
||||
Column() {
|
||||
Text($r('app.string.about_us_disclaimer'))
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.elements_text_primary_01'))
|
||||
.margin({ top: 40 })
|
||||
.onClick(() => {
|
||||
let item = {
|
||||
title: $r('app.string.about_us_disclaimer'),
|
||||
showArrow: true,
|
||||
showDivider: true,
|
||||
isJumpWeb:true,
|
||||
jumpWebUrl: $r('app.string.about_statement_url')
|
||||
} as IAboutItem
|
||||
this.onItemClick(item)
|
||||
})
|
||||
Column(){
|
||||
Text($r('app.string.about_us_icp'))
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.elements_text_secondary'))
|
||||
.margin({ top: 16 })
|
||||
|
||||
Row() {
|
||||
Image($r('app.media.icon_system_config_app_2'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
.margin({ right: 4 })
|
||||
|
||||
Text($r('app.string.about_us_platform_desc'))
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.elements_text_secondary'))
|
||||
}
|
||||
.margin({ top: 8 })
|
||||
}.width('100%')
|
||||
.onClick(()=>{
|
||||
// let item = {
|
||||
// title: $r('app.string.about_beianhao_query_text'),
|
||||
// showArrow: true,
|
||||
// showDivider: true,
|
||||
// isJumpWeb:true,
|
||||
// jumpWebUrl: $r('app.string.about_beianhao_query_url')
|
||||
// } as IAboutItem
|
||||
// this.onItemClick(item)
|
||||
this.openInSystemBrowser(AppUtil.getContext().resourceManager.getStringSync($r('app.string.about_beianhao_query_url')))
|
||||
})
|
||||
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
|
||||
Blank()
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||
}
|
||||
}
|
||||