/*
* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import relationalStore from '@ohos.data.relationalStore'
import common from '@ohos.app.ability.common'
import { Contact } from '../model/Contact'
/**
* 数据库配置
*/
const STORE_CONFIG: relationalStore.StoreConfig = {
name: 'MedicineBox.db',
securityLevel: relationalStore.SecurityLevel.S1
}
/**
* 建表 SQL
*/
const SQL_CREATE_TABLE = `
CREATE TABLE IF NOT EXISTS CONTACT_TABLE_V2 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
phone TEXT,
company TEXT,
position TEXT,
email TEXT,
image TEXT,
userId INTEGER
)`
/**
* RdbStore 数据访问层(增强版)
*/
export class RdbStore {
private rdbStore: relationalStore.RdbStore | null = null
private readonly tableName: string = 'CONTACT_TABLE_V2'
/* ==================== 初始化 ==================== */
async getRdbStore(context: common.Context): Promise<relationalStore.RdbStore> {
if (this.rdbStore !== null) {
return this.rdbStore
}
return new Promise((resolve, reject) => {
relationalStore.getRdbStore(context, STORE_CONFIG, (err, store) => {
if (err) {
reject(err)
return
}
this.rdbStore = store
try {
this.rdbStore.executeSql(SQL_CREATE_TABLE)
} catch (e) {
console.error('[RdbStore] executeSql failed')
}
resolve(store)
})
})
}
/* ==================== 基础 CRUD(原样保留) ==================== */
async insert(contact: Contact): Promise<number> {
if (!this.rdbStore) {
return -1
}
const values: relationalStore.ValuesBucket = {
name: contact.name,
phone: contact.phone,
company: contact.company ?? '',
position: contact.position ?? '',
email: contact.email ?? '',
image: contact.image ?? '',
userId: contact.userId ?? 0
}
return this.rdbStore.insert(this.tableName, values)
}
async update(contact: Contact): Promise<number> {
if (!this.rdbStore || contact.id === undefined) {
return -1
}
const values: relationalStore.ValuesBucket = {
name: contact.name,
phone: contact.phone,
company: contact.company ?? '',
position: contact.position ?? '',
email: contact.email ?? '',
image: contact.image ?? ''
}
const predicates = new relationalStore.RdbPredicates(this.tableName)
predicates.equalTo('id', contact.id)
return this.rdbStore.update(values, predicates)
}
async delete(id: number): Promise<number> {
if (!this.rdbStore) {
return -1
}
const predicates = new relationalStore.RdbPredicates(this.tableName)
predicates.equalTo('id', id)
return this.rdbStore.delete(predicates)
}
async queryAll(userId: number): Promise<Contact[]> {
if (!this.rdbStore) {
return []
}
const predicates = new relationalStore.RdbPredicates(this.tableName)
predicates.equalTo('userId', userId)
return this.queryByPredicates(predicates)
}
async search(keyword: string, userId: number): Promise<Contact[]> {
if (!this.rdbStore) {
return []
}
const predicates = new relationalStore.RdbPredicates(this.tableName)
predicates.equalTo('userId', userId).and().contains('name', keyword)
return this.queryByPredicates(predicates)
}
/* ==================== 🔥 新增:分类查询 ==================== */
async queryByCategory(category: string, userId: number): Promise<Contact[]> {
if (!this.rdbStore) {
return []
}
const predicates = new relationalStore.RdbPredicates(this.tableName)
predicates
.equalTo('userId', userId)
.and()
.equalTo('company', category)
return this.queryByPredicates(predicates)
}
/* ==================== 🔥 新增:过期判断工具 ==================== */
async queryExpired(userId: number): Promise<Contact[]> {
const all = await this.queryAll(userId)
return all.filter(item => this.isExpired(item.phone))
}
async queryNearExpired(userId: number, days: number = 30): Promise<Contact[]> {
const all = await this.queryAll(userId)
return all.filter(item => this.isNearExpired(item.phone, days))
}
async countAll(userId: number): Promise<number> {
const all = await this.queryAll(userId)
return all.length
}
/* ==================== 🔥 新增:临期 / 过期查询 ==================== */
async countNearAndExpired(userId: number): Promise<number> {
const all = await this.queryAll(userId)
return all.filter(
item =>
this.isExpired(item.phone) ||
this.isNearExpired(item.phone, 30)
).length
}
private parseDate(dateStr: string): Date | null {
const parts = dateStr.split('-')
if (parts.length !== 3) {
return null
}
const year = Number(parts[0])
const month = Number(parts[1]) - 1
const day = Number(parts[2])
return new Date(year, month, day)
}
/* ==================== 🔥 新增:首页统计 ==================== */
private isExpired(phone: string): boolean {
const date = this.parseDate(phone)
if (!date) {
return false
}
return date.getTime() < Date.now()
}
private isNearExpired(phone: string, days: number): boolean {
const date = this.parseDate(phone)
if (!date) {
return false
}
const diff = date.getTime() - Date.now()
const diffDays = diff / (1000 * 60 * 60 * 24)
return diffDays >= 0 && diffDays <= days
}
/* ==================== 内部通用查询 ==================== */
private async queryByPredicates(
predicates: relationalStore.RdbPredicates
): Promise<Contact[]> {
if (!this.rdbStore) {
return []
}
return new Promise((resolve, reject) => {
this.rdbStore!.query(
predicates,
['id', 'name', 'phone', 'company', 'position', 'email', 'image', 'userId'],
(err, resultSet) => {
if (err) {
reject(err)
return
}
const result: Contact[] = []
if (resultSet.rowCount > 0) {
resultSet.goToFirstRow()
do {
const contact = new Contact(
resultSet.getString(resultSet.getColumnIndex('name')),
resultSet.getString(resultSet.getColumnIndex('phone'))
)
contact.id = resultSet.getLong(resultSet.getColumnIndex('id'))
contact.company = resultSet.getString(resultSet.getColumnIndex('company'))
contact.position = resultSet.getString(resultSet.getColumnIndex('position'))
contact.email = resultSet.getString(resultSet.getColumnIndex('email'))
contact.image = resultSet.getString(resultSet.getColumnIndex('image'))
contact.userId = resultSet.getLong(resultSet.getColumnIndex('userId'))
result.push(contact)
} while (resultSet.goToNextRow())
}
resultSet.close()
resolve(result)
}
)
})
}
}
/**
* 单例导出(所有页面统一使用)
*/
export default new RdbStore()