1/** 2 * @file Describe the file 3 * Copyright (c) 2023 Huawei Device Co., Ltd. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import data_rdb from '@ohos.data.relationalStore'; 18import dataSharePredicates from '@ohos.data.dataSharePredicates'; 19 20import { Log } from "@ohos/common/src/main/ets/utils/Log"; 21import { isEmptyStr } from '@ohos/common/src/main/ets/utils/TextUtils'; 22import { isContainsOneOfKeys } from '@ohos/common/src/main/ets/utils/ValuesUtils'; 23import { getBundleNameByUri } from '@ohos/common/src/main/ets/utils/UrlUtils'; 24 25import { EventColumns, UPDATE_INSTANCES_COLUMNS } from '@ohos/datastructure/src/main/ets/events/EventColumns'; 26import { Events } from '@ohos/datastructure/src/main/ets/events/Events'; 27import { InstancesColumns } from '@ohos/datastructure/src/main/ets/instances/InstancesColumns'; 28 29import { notifyProviderChange, OPERATION_UNKNOWN, } from '../../commonevents/notify/ProviderChangeNotifier'; 30import { DefaultProcessor } from "./../DefaultProcessor"; 31import { ErrorCode } from '../../constants/ErrorCode'; 32import { deleteAllInstances, expandAllByRange, expandOneInRange } from './InstanceExpandHelper'; 33import { generateInstancesQuerySql } from './InstancesQuerySqlGenerator'; 34import { isInExpandedRange, updateExpandedRange, getExpandedBegin, getExpandedEnd } from "./ExpandRangeManager"; 35import { InstancesQueryParams, parseParamsFromUri, isValidQueryParams } from './InstanceQueryParams'; 36import { BusinessError } from '@ohos.base'; 37 38const TAG = "InstancesProcessor"; 39 40/** 41 * the instances table processor 42 * 43 * @since 2022-05-28 44 */ 45export class InstancesProcessor extends DefaultProcessor { 46 initParas() { 47 this.logTag = TAG; 48 } 49 50 async insertByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, values: data_rdb.ValuesBucket, callback: Function) { 51 Log.warn(this.logTag, 'not support insert operation'); 52 const err: BusinessError = { 53 code: ErrorCode.UN_SUPPORT_OPERATION, 54 name: 'UnSupportedOperationException', 55 message: 'Instances Table not support insert operation' 56 }; 57 callback(err, 0); 58 } 59 60 async insertByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, values: data_rdb.ValuesBucket, callback: Function) { 61 this.insertByHighAuthority(rdbStore, uri, values, callback); 62 } 63 64 async deleteByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 65 Log.warn(this.logTag, 'not support delete operation'); 66 const err: BusinessError = { 67 code: ErrorCode.UN_SUPPORT_OPERATION, 68 name: 'UnSupportedOperationException', 69 message: 'Instances Table not support delete operation' 70 }; 71 callback(err, 0); 72 } 73 74 async deleteByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 75 this.deleteByHighAuthority(rdbStore, uri, predicates, callback); 76 } 77 78 async updateByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, values: data_rdb.ValuesBucket, predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 79 Log.warn(this.logTag, 'not support update operation'); 80 const err: BusinessError = { 81 code: ErrorCode.UN_SUPPORT_OPERATION, 82 name: 'UnSupportedOperationException', 83 message: 'Instances Table not support update operation' 84 }; 85 callback(err, 0); 86 } 87 88 async updateByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, values: data_rdb.ValuesBucket, predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 89 this.updateByHighAuthority(rdbStore, uri, values, predicates, callback); 90 } 91 92 async queryByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, columns: Array<string>, predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 93 const callerName = ''; 94 this.queryByInstanceUri(rdbStore, uri, columns, callerName, callback); 95 } 96 97 async queryByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, columns: Array<string>, predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 98 const callerName = getBundleNameByUri(uri); 99 this.queryByInstanceUri(rdbStore, uri, columns, callerName, callback); 100 } 101 102 /** 103 * 解析 uri,根据 uri 进行 Instance 的联表查询 104 * 105 * @param rdbStore rdb数据库 106 * @param uri DataShare的uri,包含查询参数 107 * @param columns 查询的列名 108 * @param callerName 调用方app的bundle_name 109 * @param callback 回调方法 110 */ 111 async queryByInstanceUri(rdbStore: data_rdb.RdbStore, uri: string, columns: Array<string>, callerName: string, callback: Function) { 112 const params: InstancesQueryParams = parseParamsFromUri(uri) as InstancesQueryParams; 113 if (!isValidQueryParams(params)) { 114 const err: BusinessError = { 115 code: ErrorCode.ILLEGAL_ARGUMENT_ERROR, 116 name: 'IllegalArgumentError', 117 message: 'query Instances must has begin&end or searchText params like ?begin=${begin}&end=${end}' + 118 '&searchText=${searchText}' 119 }; 120 callback(err, null); 121 return; 122 } 123 if (params.isValidBeginEnd()) { 124 await acquireExpandAll(rdbStore, params.getBegin(), params.getEnd()); 125 } 126 doInstancesQuery(rdbStore, columns, params, callerName, callback); 127 } 128} 129 130/** 131 * 请求扩展Instances 132 * 1.在已扩展范围内,则不扩展 133 * 2.不在已扩展范围内,则按未扩展部分扩展全部 134 * 135 * @param begin 请求扩展的开始时间 136 * @param end 请求扩展的结束时间 137 */ 138export async function acquireExpandAll(rdbStore: data_rdb.RdbStore, acquireBegin: number, acquireEnd: number) { 139 Log.info(TAG, `acquireExpandAll begin from ${acquireBegin} to ${acquireEnd}`); 140 const isInRange = await isInExpandedRange(acquireBegin, acquireEnd); 141 if (isInRange) { 142 Log.info(TAG, `acquireExpandAll no need to expand`); 143 return; 144 } 145 let expandedBegin = await getExpandedBegin(); 146 let expandedEnd = await getExpandedEnd(); 147 Log.info(TAG, `acquireExpandAll expanded range:${expandedBegin}/${expandedEnd}`); 148 149 let shouldSendBroadcast: boolean = false; 150 if (expandedEnd === 0) { 151 Log.info(TAG, "acquireExpandAll first expand"); 152 await deleteAllInstances(rdbStore); 153 let isSuccess = await expandAllByRange(rdbStore, acquireBegin, acquireEnd); 154 if (isSuccess) { 155 Log.info(TAG, `expandAllByRange successful from ${acquireBegin} to ${acquireEnd}`); 156 updateExpandedRange(acquireBegin, acquireEnd); 157 expandedBegin = acquireBegin; 158 expandedEnd = acquireEnd; 159 shouldSendBroadcast = true; 160 } 161 } 162 163 // 处理比已扩展时间早的部分 164 if (acquireBegin < expandedBegin) { 165 let isSuccess = await expandAllByRange(rdbStore, acquireBegin, expandedBegin); 166 if (isSuccess) { 167 Log.info(TAG, `expandAllByRange successful from ${acquireBegin} to ${expandedBegin}`); 168 updateExpandedRange(acquireBegin, expandedEnd); 169 expandedBegin = acquireBegin; 170 shouldSendBroadcast = true; 171 } 172 } 173 174 // 处理比已扩展时间晚的部分 175 if (acquireEnd > expandedEnd) { 176 let isSuccess = await expandAllByRange(rdbStore, expandedEnd, acquireEnd); 177 if (isSuccess) { 178 Log.info(TAG, `expandAllByRange successful from ${expandedEnd} to ${acquireEnd}`); 179 updateExpandedRange(expandedBegin, acquireEnd); 180 expandedEnd = acquireEnd; 181 shouldSendBroadcast = true; 182 } 183 } 184 Log.info(TAG, `acquireExpandAll end from ${acquireBegin} to ${acquireEnd}`); 185 186 if (shouldSendBroadcast) { 187 notifyProviderChange(InstancesColumns.TABLE_NAME, OPERATION_UNKNOWN); 188 } 189} 190 191/** 192 * 请求扩展单个日程,用于日程创建/更新时 193 * 194 * @param eventId 日程ID 195 * @param values ValuesBucket数据(创建时)或Events数据(更新时) 196 */ 197export async function acquireExpandOne(rdbStore: data_rdb.RdbStore, rowId: number, values: data_rdb.ValuesBucket | Events | undefined) { 198 const isSuccess = await expandOne(rdbStore, rowId, values); 199 if (isSuccess) { 200 notifyProviderChange(InstancesColumns.TABLE_NAME, OPERATION_UNKNOWN); 201 } else { 202 Log.warn(TAG, "expandOneByRange failed"); 203 } 204} 205 206/** 207 * 扩展单个日程,用于日程创建/更新时 208 * 209 * @param eventId 日程ID 210 * @param values ValuesBucket数据(创建时)或Events数据(更新时) 211 */ 212export async function expandOne(rdbStore: data_rdb.RdbStore, rowId: number, values: data_rdb.ValuesBucket | Events | undefined): Promise<boolean> { 213 if (values === null || values === undefined) { 214 Log.error(TAG, 'acquireExpandOne get invalid params'); 215 return false; 216 } 217 const expandedBegin = await getExpandedBegin(); 218 const expandedEnd = await getExpandedEnd(); 219 let event: Events; 220 if (values instanceof Events) { 221 event = values; 222 } else { 223 event = new Events(); 224 event.id = rowId; 225 parseEventsFromValues(values, event); 226 } 227 228 // 对于单个日程,仅对已全局扩展的时间范围内处理 229 const isSuccess = expandOneInRange(rdbStore, event, expandedBegin, expandedEnd); 230 return isSuccess; 231} 232 233/** 234 * 请求刷新一条Event的Instances 235 * 236 * @param rdbStore rdb数据库 237 * @param eventId 日程ID 238 * @param event 日程数据信息 239 */ 240export async function acquireUpdateOne(rdbStore: data_rdb.RdbStore, eventId: number, event: Events | undefined) { 241 const predicate = new dataSharePredicates.DataSharePredicates(); 242 predicate.equalTo(InstancesColumns.EVENT_ID, eventId); 243 await rdbStore.delete(InstancesColumns.TABLE_NAME, predicate); 244 acquireExpandOne(rdbStore, eventId, event); 245} 246 247/** 248 * 是否需要刷新Instances表,根据values内是否包含dtstart、rrule等关键字段来判断是否需要刷新Instances 249 * 250 * @param values 变更的values,以此判断是否需要刷新 251 * @return true 需要刷新; false 不需要刷新 252 */ 253export function isNeedRefreshInstances(values: data_rdb.ValuesBucket): boolean { 254 if (values === null || values === undefined) { 255 return false; 256 } 257 return isContainsOneOfKeys(values, UPDATE_INSTANCES_COLUMNS); 258} 259 260function parseEventsFromValues(values: data_rdb.ValuesBucket, event: Events): void { 261 event.dtStart = values[EventColumns.DTSTART] as number; 262 event.dtEnd = values[EventColumns.DTEND] as number; 263 event.rRule = values[EventColumns.RRULE] as string; 264 event.rDate = values[EventColumns.RDATE] as string; 265 event.exRule = values[EventColumns.EXRULE] as string; 266 event.exDate = values[EventColumns.EXDATE] as string; 267 event.allDay = values[EventColumns.ALLDAY] as number; 268 event.eventTimeZone = values[EventColumns.EVENT_TIMEZONE] as string; 269 event.originalId = values[EventColumns.ORIGINAL_ID] as number; 270 event.syncId = values[EventColumns.SYNC_ID] as string; 271 event.originalSyncId = values[EventColumns.ORIGINAL_SYNC_ID] as string; 272 event.lastDate = values[EventColumns.LAST_DATE] as number; 273 event.originalInstanceTime = values[EventColumns.ORIGINAL_INSTANCE_TIME] as number; 274 event.creator = values[EventColumns.CREATOR] as string; 275} 276 277function doInstancesQuery(rdbStore: data_rdb.RdbStore, columns: Array<string>, params: InstancesQueryParams, callerName: string, callback: Function): void { 278 const sql = generateInstancesQuerySql(columns, params, callerName); 279 if (!sql || isEmptyStr(sql)) { 280 Log.error(TAG, `generate empty sql, the params maybe invalid`); 281 return; 282 } 283 Log.info(TAG, `instances query sql=${sql}`); 284 try { 285 let instancesQueryFun = (err: BusinessError, resultSet: data_rdb.ResultSet) => { 286 callback(err, resultSet); 287 } 288 rdbStore.querySql(sql, [], instancesQueryFun); 289 } catch (err) { 290 Log.error(TAG, `doInstancesQuery querySql get err:${err?.message}, SQL:${sql}`); 291 } 292}