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, 51 values: data_rdb.ValuesBucket, callback: Function) { 52 Log.warn(this.logTag, 'not support insert operation'); 53 const err: BusinessError = { 54 code: ErrorCode.UN_SUPPORT_OPERATION, 55 name: 'UnSupportedOperationException', 56 message: 'Instances Table not support insert operation' 57 }; 58 callback(err, 0); 59 } 60 61 async insertByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, 62 values: data_rdb.ValuesBucket, callback: Function) { 63 this.insertByHighAuthority(rdbStore, uri, values, callback); 64 } 65 66 async deleteByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, 67 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 68 Log.warn(this.logTag, 'not support delete operation'); 69 const err: BusinessError = { 70 code: ErrorCode.UN_SUPPORT_OPERATION, 71 name: 'UnSupportedOperationException', 72 message: 'Instances Table not support delete operation' 73 }; 74 callback(err, 0); 75 } 76 77 async deleteByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, 78 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 79 this.deleteByHighAuthority(rdbStore, uri, predicates, callback); 80 } 81 82 async updateByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, values: data_rdb.ValuesBucket, 83 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 84 Log.warn(this.logTag, 'not support update operation'); 85 const err: BusinessError = { 86 code: ErrorCode.UN_SUPPORT_OPERATION, 87 name: 'UnSupportedOperationException', 88 message: 'Instances Table not support update operation' 89 }; 90 callback(err, 0); 91 } 92 93 async updateByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, values: data_rdb.ValuesBucket, 94 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 95 this.updateByHighAuthority(rdbStore, uri, values, predicates, callback); 96 } 97 98 async queryByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, columns: Array<string>, 99 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 100 const callerName = ''; 101 this.queryByInstanceUri(rdbStore, uri, columns, callerName, callback); 102 } 103 104 async queryByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, columns: Array<string>, 105 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 106 const callerName = getBundleNameByUri(uri); 107 this.queryByInstanceUri(rdbStore, uri, columns, callerName, callback); 108 } 109 110 /** 111 * 解析 uri,根据 uri 进行 Instance 的联表查询 112 * 113 * @param rdbStore rdb数据库 114 * @param uri DataShare的uri,包含查询参数 115 * @param columns 查询的列名 116 * @param callerName 调用方app的bundle_name 117 * @param callback 回调方法 118 */ 119 async queryByInstanceUri(rdbStore: data_rdb.RdbStore, uri: string, 120 columns: Array<string>, callerName: string, callback: Function) { 121 const params: InstancesQueryParams = parseParamsFromUri(uri) as InstancesQueryParams; 122 if (!isValidQueryParams(params)) { 123 const err: BusinessError = { 124 code: ErrorCode.ILLEGAL_ARGUMENT_ERROR, 125 name: 'IllegalArgumentError', 126 message: 'query Instances must has begin&end or searchText params like ?begin=${begin}&end=${end}' + 127 '&searchText=${searchText}' 128 }; 129 callback(err, null); 130 return; 131 } 132 if (params.isValidBeginEnd()) { 133 await acquireExpandAll(rdbStore, params.getBegin(), params.getEnd()); 134 } 135 doInstancesQuery(rdbStore, columns, params, callerName, callback); 136 } 137} 138 139/** 140 * 请求扩展Instances 141 * 1.在已扩展范围内,则不扩展 142 * 2.不在已扩展范围内,则按未扩展部分扩展全部 143 * 144 * @param begin 请求扩展的开始时间 145 * @param end 请求扩展的结束时间 146 */ 147export async function acquireExpandAll(rdbStore: data_rdb.RdbStore, acquireBegin: number, acquireEnd: number) { 148 Log.info(TAG, `acquireExpandAll begin from ${acquireBegin} to ${acquireEnd}`); 149 const isInRange = await isInExpandedRange(acquireBegin, acquireEnd); 150 if (isInRange) { 151 Log.info(TAG, `acquireExpandAll no need to expand`); 152 return; 153 } 154 let expandedBegin = await getExpandedBegin(); 155 let expandedEnd = await getExpandedEnd(); 156 Log.info(TAG, `acquireExpandAll expanded range:${expandedBegin}/${expandedEnd}`); 157 158 let shouldSendBroadcast: boolean = false; 159 if (expandedEnd === 0) { 160 Log.info(TAG, 'acquireExpandAll first expand'); 161 await deleteAllInstances(rdbStore); 162 let isSuccess = await expandAllByRange(rdbStore, acquireBegin, acquireEnd); 163 if (isSuccess) { 164 Log.info(TAG, `expandAllByRange successful from ${acquireBegin} to ${acquireEnd}`); 165 updateExpandedRange(acquireBegin, acquireEnd); 166 expandedBegin = acquireBegin; 167 expandedEnd = acquireEnd; 168 shouldSendBroadcast = true; 169 } 170 } 171 172 // 处理比已扩展时间早的部分 173 if (acquireBegin < expandedBegin) { 174 let isSuccess = await expandAllByRange(rdbStore, acquireBegin, expandedBegin); 175 if (isSuccess) { 176 Log.info(TAG, `expandAllByRange successful from ${acquireBegin} to ${expandedBegin}`); 177 updateExpandedRange(acquireBegin, expandedEnd); 178 expandedBegin = acquireBegin; 179 shouldSendBroadcast = true; 180 } 181 } 182 183 // 处理比已扩展时间晚的部分 184 if (acquireEnd > expandedEnd) { 185 let isSuccess = await expandAllByRange(rdbStore, expandedEnd, acquireEnd); 186 if (isSuccess) { 187 Log.info(TAG, `expandAllByRange successful from ${expandedEnd} to ${acquireEnd}`); 188 updateExpandedRange(expandedBegin, acquireEnd); 189 expandedEnd = acquireEnd; 190 shouldSendBroadcast = true; 191 } 192 } 193 Log.info(TAG, `acquireExpandAll end from ${acquireBegin} to ${acquireEnd}`); 194 195 if (shouldSendBroadcast) { 196 notifyProviderChange(InstancesColumns.TABLE_NAME, OPERATION_UNKNOWN); 197 } 198} 199 200/** 201 * 请求扩展单个日程,用于日程创建/更新时 202 * 203 * @param eventId 日程ID 204 * @param values ValuesBucket数据(创建时)或Events数据(更新时) 205 */ 206export async function acquireExpandOne(rdbStore: data_rdb.RdbStore, rowId: number, 207 values: data_rdb.ValuesBucket | Events | undefined) { 208 const isSuccess = await expandOne(rdbStore, rowId, values); 209 if (isSuccess) { 210 notifyProviderChange(InstancesColumns.TABLE_NAME, OPERATION_UNKNOWN); 211 } else { 212 Log.warn(TAG, 'expandOneByRange failed'); 213 } 214} 215 216/** 217 * 扩展单个日程,用于日程创建/更新时 218 * 219 * @param eventId 日程ID 220 * @param values ValuesBucket数据(创建时)或Events数据(更新时) 221 */ 222export async function expandOne(rdbStore: data_rdb.RdbStore, rowId: number, 223 values: data_rdb.ValuesBucket | Events | undefined): Promise<boolean> { 224 if (values === null || values === undefined) { 225 Log.error(TAG, 'acquireExpandOne get invalid params'); 226 return false; 227 } 228 const expandedBegin = await getExpandedBegin(); 229 const expandedEnd = await getExpandedEnd(); 230 let event: Events; 231 if (values instanceof Events) { 232 event = values; 233 } else { 234 event = new Events(); 235 event.id = rowId; 236 parseEventsFromValues(values, event); 237 } 238 239 // 对于单个日程,仅对已全局扩展的时间范围内处理 240 const isSuccess = expandOneInRange(rdbStore, event, expandedBegin, expandedEnd); 241 return isSuccess; 242} 243 244/** 245 * 请求刷新一条Event的Instances 246 * 247 * @param rdbStore rdb数据库 248 * @param eventId 日程ID 249 * @param event 日程数据信息 250 */ 251export async function acquireUpdateOne(rdbStore: data_rdb.RdbStore, eventId: number, event: Events | undefined) { 252 const predicate = new dataSharePredicates.DataSharePredicates(); 253 predicate.equalTo(InstancesColumns.EVENT_ID, eventId); 254 try { 255 await rdbStore.delete(InstancesColumns.TABLE_NAME, predicate); 256 } catch (err) { 257 Log.error(TAG, `acquireUpdateOne delete err:${err?.message}`); 258 } 259 260 acquireExpandOne(rdbStore, eventId, event); 261} 262 263/** 264 * 是否需要刷新Instances表,根据values内是否包含dtstart、rrule等关键字段来判断是否需要刷新Instances 265 * 266 * @param values 变更的values,以此判断是否需要刷新 267 * @return true 需要刷新; false 不需要刷新 268 */ 269export function isNeedRefreshInstances(values: data_rdb.ValuesBucket): boolean { 270 if (values === null || values === undefined) { 271 return false; 272 } 273 return isContainsOneOfKeys(values, UPDATE_INSTANCES_COLUMNS); 274} 275 276function parseEventsFromValues(values: data_rdb.ValuesBucket, event: Events): void { 277 event.dtStart = values[EventColumns.DTSTART] as number; 278 event.dtEnd = values[EventColumns.DTEND] as number; 279 event.rRule = values[EventColumns.RRULE] as string; 280 event.rDate = values[EventColumns.RDATE] as string; 281 event.exRule = values[EventColumns.EXRULE] as string; 282 event.exDate = values[EventColumns.EXDATE] as string; 283 event.allDay = values[EventColumns.ALLDAY] as number; 284 event.eventTimeZone = values[EventColumns.EVENT_TIMEZONE] as string; 285 event.originalId = values[EventColumns.ORIGINAL_ID] as number; 286 event.syncId = values[EventColumns.SYNC_ID] as string; 287 event.originalSyncId = values[EventColumns.ORIGINAL_SYNC_ID] as string; 288 event.lastDate = values[EventColumns.LAST_DATE] as number; 289 event.originalInstanceTime = values[EventColumns.ORIGINAL_INSTANCE_TIME] as number; 290 event.creator = values[EventColumns.CREATOR] as string; 291} 292 293function doInstancesQuery(rdbStore: data_rdb.RdbStore, columns: Array<string>, 294 params: InstancesQueryParams, callerName: string, callback: Function): void { 295 const sql = generateInstancesQuerySql(columns, params, callerName); 296 if (!sql || isEmptyStr(sql)) { 297 Log.error(TAG, `generate empty sql, the params maybe invalid`); 298 return; 299 } 300 301 try { 302 let instancesQueryFun = (err: BusinessError, resultSet: data_rdb.ResultSet) => { 303 callback(err, resultSet); 304 } 305 rdbStore.querySql(sql, [], instancesQueryFun); 306 } catch (err) { 307 Log.error(TAG, `doInstancesQuery querySql get err:${err?.message}`); 308 } 309}