You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1017 lines
49 KiB
1017 lines
49 KiB
using AIK.DataAccess;
|
|
using AIK.Models;
|
|
using AIK.Models.ApiModels;
|
|
using AIK.Models.ApiModels.Brand;
|
|
using AIK.Models.SQLiteModels;
|
|
using AIK.Service.IService;
|
|
using Dapper;
|
|
using HidSharp;
|
|
using System.Data.SQLite;
|
|
using Microsoft.VisualBasic;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
|
|
namespace AIK.Service.Service
|
|
{
|
|
public class SqliteCacheService : IDataCacheService, IDisposable
|
|
{
|
|
|
|
//private readonly SQLiteConnection _connection;//弃用(原单例模式)
|
|
private readonly string _databasePath;
|
|
private bool _disposed = false;
|
|
|
|
public SqliteCacheService()
|
|
{
|
|
//_connection = SQLiteCore.CreateConnection();//弃用(原单例模式)
|
|
_databasePath = SQLiteCore.DatabasePath;
|
|
InitializeDatabase();
|
|
}
|
|
|
|
private void InitializeDatabase()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
try
|
|
{
|
|
var conn = pooledConn.InnerConnection; // 借用连接
|
|
#region 创建表
|
|
// 创建缓存表
|
|
var createTableSql = @"
|
|
CREATE TABLE IF NOT EXISTS CacheItems (
|
|
CacheKey TEXT PRIMARY KEY,
|
|
Data TEXT NOT NULL,
|
|
DataType TEXT NOT NULL,
|
|
CreatedAt DATETIME NOT NULL,
|
|
ExpiresAt DATETIME NOT NULL,
|
|
LastAccessed DATETIME NOT NULL,
|
|
AccessCount INTEGER DEFAULT 0,
|
|
Metadata TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS IX_CacheItems_ExpiresAt ON CacheItems(ExpiresAt);
|
|
CREATE INDEX IF NOT EXISTS IX_CacheItems_LastAccessed ON CacheItems(LastAccessed);
|
|
CREATE INDEX IF NOT EXISTS IX_CacheItems_DataType ON CacheItems(DataType);
|
|
";
|
|
|
|
// 3. 创建拆分缓存父表 SplitCache_Parent(无 COMMENT)
|
|
var createSplitParentTableSql = @"
|
|
CREATE TABLE IF NOT EXISTS SplitCache_Parent (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
CacheKey TEXT NOT NULL,
|
|
ParentData TEXT NOT NULL, -- 父级核心数据(无列表)
|
|
SubItemCount INTEGER DEFAULT 0, -- 子级数据条数
|
|
DataType TEXT NOT NULL, -- 父级数据类型
|
|
CreatedAt DATETIME NOT NULL,
|
|
ExpiresAt DATETIME NOT NULL,
|
|
LastAccessed DATETIME NOT NULL,
|
|
AccessCount INTEGER DEFAULT 0,
|
|
Metadata TEXT
|
|
);";
|
|
|
|
// 4. 为 SplitCache_Parent 表创建独立索引
|
|
var createSplitParentIndexSql = @"
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheParent_ExpiresAt ON SplitCache_Parent(ExpiresAt);
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheParent_LastAccessed ON SplitCache_Parent(LastAccessed);";
|
|
|
|
// 5. 创建拆分缓存子表 SplitCache_ModelList(无 COMMENT,字段含义用代码注释说明)
|
|
var createSplitModelListTableSql = @"
|
|
CREATE TABLE IF NOT EXISTS SplitCache_ModelList (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
ParentTableId INTEGER NOT NULL,
|
|
CacheKey TEXT NOT NULL, -- 关联 SplitCache_Parent.CacheKey
|
|
ParentID INTEGER NOT NULL, -- 关联 VehModelClass.ID
|
|
|
|
KeyId INTEGER NOT NULL, -- 对应 VehKeyClass.id
|
|
KeyKeyId TEXT NOT NULL, -- 对应 VehKeyClass.keyId
|
|
Name TEXT NOT NULL, -- 对应 VehKeyClass.Name
|
|
ModelName TEXT NOT NULL, -- 对应 VehKeyClass.modelName
|
|
ModelEngName TEXT NOT NULL, -- 对应 VehKeyClass.modelEngName
|
|
ModelPuName TEXT NOT NULL, -- 对应 VehKeyClass.modelPuName
|
|
ModelXiName TEXT NOT NULL, -- 对应 VehKeyClass.modelXiName
|
|
ModelInName TEXT NOT NULL, -- 对应 VehKeyClass.modelInName
|
|
Rate TEXT NOT NULL, -- 对应 VehKeyClass.rate(频率)
|
|
Usable TEXT NOT NULL, -- 对应 VehKeyClass.usable(子机中文描述)
|
|
UsableEn TEXT NOT NULL, -- 对应 VehKeyClass.usableEn(子机英文描述)
|
|
UsablePu TEXT NOT NULL, -- 对应 VehKeyClass.usablePu
|
|
UsableXi TEXT NOT NULL, -- 对应 VehKeyClass.usableXi
|
|
Information TEXT, -- 对应 VehKeyClass.information(参考信息)
|
|
PicUrl TEXT NOT NULL, -- 对应 VehKeyClass.picUrl(图片路径)
|
|
FirmwareNo TEXT NOT NULL, -- 对应 VehKeyClass.firmwareNo(Bin文件名)
|
|
SourceUrl TEXT NOT NULL, -- 对应 VehKeyClass.sourceUrl(Bin文件路径)
|
|
SpCode TEXT NOT NULL, -- 对应 VehKeyClass.spCode(烧录提示)
|
|
Support TEXT NOT NULL, -- 对应 VehKeyClass.support(支持设备类型)
|
|
ChipType TEXT, -- 对应 VehKeyClass.chipType(芯片类型)
|
|
CreateTime TEXT NOT NULL, -- 对应 VehKeyClass.createTime(创建时间)
|
|
|
|
KeyMatchContent TEXT, -- 对应 keyMatch.content(匹配方法中文)
|
|
KeyMatchContentEn TEXT, -- 对应 keyMatch.contentEn(匹配方法英文)
|
|
KeyMatchContentPu TEXT, -- 对应 keyMatch.contentPu
|
|
KeyMatchContentXi TEXT, -- 对应 keyMatch.contentXi
|
|
KeyMatchContentIn TEXT, -- 对应 keyMatch.contentIn
|
|
|
|
IsSelected BOOLEAN NOT NULL DEFAULT 0, -- 对应 VehKeyClass.IsSelected
|
|
LocalImagePath TEXT, -- 对应 VehKeyClass.LocalImagePath(本地图片路径)
|
|
|
|
-- 外键约束:删除父表数据时,自动删除子表关联数据
|
|
FOREIGN KEY (ParentTableId) REFERENCES SplitCache_Parent(Id) ON DELETE CASCADE
|
|
);";
|
|
|
|
var createKZInfoListTableSql = @"CREATE TABLE IF NOT EXISTS SplitCache_KzInfoList (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
ModelListId INTEGER NOT NULL, -- 关键:指向 SplitCache_ModelList.Id
|
|
|
|
binAddress TEXT,
|
|
binAddressArray TEXT, -- JSON: [""0x1000"", ...]
|
|
chipType TEXT,
|
|
cloudRemote BOOLEAN NOT NULL DEFAULT 0,
|
|
dzxp BOOLEAN NOT NULL DEFAULT 0,
|
|
firmwareNo TEXT,
|
|
fixedByte BOOLEAN NOT NULL DEFAULT 0,
|
|
fixedByteArray TEXT, -- JSON
|
|
fixedByteArrays TEXT,
|
|
sourceUrl TEXT,
|
|
sp BOOLEAN NOT NULL DEFAULT 0,
|
|
spCode TEXT,
|
|
tsRand BOOLEAN NOT NULL DEFAULT 0,
|
|
tsRandStr TEXT,
|
|
type TEXT,
|
|
updateByte BOOLEAN NOT NULL DEFAULT 0,
|
|
usable TEXT,
|
|
ysSource TEXT,
|
|
ysSourceNo TEXT,
|
|
zsId TEXT,
|
|
CreateTime TEXT NOT NULL DEFAULT (datetime('now')),
|
|
|
|
FOREIGN KEY (ModelListId) REFERENCES SplitCache_ModelList(Id) ON DELETE CASCADE
|
|
);";
|
|
|
|
|
|
// 6. 为 SplitCache_ModelList 表创建独立索引
|
|
var createSplitModelListIndexSql = @"
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheModelList_ParentTableId ON SplitCache_ModelList(ParentTableId); -- 外键关联查询(核心索引)
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheModelList_CacheKey ON SplitCache_ModelList(CacheKey); -- 按 CacheKey 批量查询
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheModelList_ParentID ON SplitCache_ModelList(ParentID); -- 按业务 ParentID 查询
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheModelList_KeyId ON SplitCache_ModelList(KeyId);
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheModelList_FirmwareNo ON SplitCache_ModelList(FirmwareNo);
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheModelList_Support ON SplitCache_ModelList(Support);
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheModelList_ChipType ON SplitCache_ModelList(ChipType);";
|
|
|
|
|
|
//6.为 SplitCache_KzInfoList 表创建独立索引
|
|
var createSplitKzInfoListIndexSql = @"
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_ModelListId ON SplitCache_KzInfoList(ModelListId); -- 外键关联查询(核心索引)
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_ChipType ON SplitCache_KzInfoList(chipType); -- 按芯片类型筛选
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_FirmwareNo ON SplitCache_KzInfoList(firmwareNo); -- 按固件编号查询
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_SpCode ON SplitCache_KzInfoList(spCode); -- 按子机code快速定位
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_Type ON SplitCache_KzInfoList(type); -- 按所属子机类型分类查询
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_Usable ON SplitCache_KzInfoList(usable); -- 按可用子机描述匹配
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_SourceUrl ON SplitCache_KzInfoList(sourceUrl); -- 按固件下载地址查询(用于去重或校验)
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_ZsId ON SplitCache_KzInfoList(zsId); -- 按遥控再生类型ID查询
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_Sp ON SplitCache_KzInfoList(sp); -- 快速筛选特殊子机
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_CloudRemote ON SplitCache_KzInfoList(cloudRemote); -- 按是否云遥控过滤
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_Dzxp ON SplitCache_KzInfoList(dzxp); -- 按是否电子芯片过滤
|
|
CREATE INDEX IF NOT EXISTS IX_SplitCacheKzInfoList_UpdateByte ON SplitCache_KzInfoList(updateByte); -- 按是否需修改bin文件筛选";
|
|
|
|
// 用户token表
|
|
var createUserTokenTableSql = @"
|
|
CREATE TABLE IF NOT EXISTS SysUserToken (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
Token TEXT NOT NULL,
|
|
CreatedAt DATETIME NOT NULL,
|
|
ExpiresAt DATETIME NOT NULL
|
|
);";
|
|
//用户信息表
|
|
var createUserInfoTableSql = @"
|
|
CREATE TABLE IF NOT EXISTS SysUser (
|
|
Id INTEGER PRIMARY KEY,
|
|
CreateTime TEXT,
|
|
DeptId INTEGER,
|
|
Email TEXT,
|
|
Image TEXT,
|
|
Local TEXT,
|
|
NickName TEXT,
|
|
Phone TEXT,
|
|
Remark TEXT,
|
|
Sex TEXT,
|
|
UpdateTime TEXT,
|
|
UserName TEXT,
|
|
UserStatus TEXT,
|
|
UserType TEXT,
|
|
Uuid TEXT UNIQUE,
|
|
WorkerAccount TEXT,
|
|
WorkerId INTEGER
|
|
);";
|
|
#endregion
|
|
|
|
conn.Execute(createTableSql);
|
|
|
|
conn.Execute(createSplitParentTableSql);
|
|
conn.Execute(createSplitParentIndexSql); // 独立执行索引创建
|
|
conn.Execute(createSplitModelListTableSql);
|
|
conn.Execute(createSplitModelListIndexSql); // 独立执行索引创建
|
|
conn.Execute(createKZInfoListTableSql);
|
|
conn.Execute(createSplitKzInfoListIndexSql); // 独立执行索引创建
|
|
|
|
conn.Execute(createUserTokenTableSql);
|
|
conn.Execute(createUserInfoTableSql);
|
|
|
|
|
|
// 清理过期缓存
|
|
_ = ClearExpiredAsync();
|
|
_ = ClearSplitCacheExpiredAsync(); // 新增拆分缓存的过期清理
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception($"初始化缓存数据库失败: {ex.Message}", ex);
|
|
}
|
|
}
|
|
#region 拆分缓存
|
|
#region 拆分缓存-写入(拆分 VehModelClass 到父子表)
|
|
/// <summary>
|
|
/// 拆分 VehModelClass 数据(父级+子级列表)到拆分缓存表
|
|
/// </summary>
|
|
public async Task SetSplitVehModelDataAsync(string cacheKey, VehModelClass vehModel, TimeSpan expiry)
|
|
{
|
|
if (vehModel == null || vehModel.modelList == null || !vehModel.modelList.Any())
|
|
throw new ArgumentNullException(nameof(vehModel), "VehModelClass 或 modelList 不能为空");
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var conn = pooledConn.InnerConnection;
|
|
// 事务保证原子性(仅操作新表)
|
|
using (var transaction = conn.BeginTransaction())
|
|
{
|
|
try
|
|
{
|
|
// 1. 序列化父级核心数据(VehModelClass 非列表字段)
|
|
var parentData = JsonConvert.SerializeObject(new
|
|
{
|
|
vehModel.ID,
|
|
vehModel.typeName,
|
|
vehModel.typeEngName,
|
|
vehModel.typePuName,
|
|
vehModel.typeXiName,
|
|
vehModel.typeInName
|
|
}, new JsonSerializerSettings
|
|
{
|
|
NullValueHandling = NullValueHandling.Ignore,
|
|
Formatting = Formatting.None // 紧凑序列化,减少存储体积
|
|
});
|
|
|
|
// 2. 写入父表 SplitCache_Parent
|
|
var splitParent = new SplitCacheParent
|
|
{
|
|
CacheKey = cacheKey,
|
|
ParentData = parentData,
|
|
SubItemCount = vehModel.modelList.Count,
|
|
DataType = typeof(VehModelClass).FullName,
|
|
CreatedAt = DateTime.Now,
|
|
ExpiresAt = DateTime.Now.Add(expiry),
|
|
LastAccessed = DateTime.Now,
|
|
AccessCount = 0,
|
|
Metadata = JsonConvert.SerializeObject(new
|
|
{
|
|
VehModelID = vehModel.ID,
|
|
ParentDataSize = Encoding.UTF8.GetByteCount(parentData),
|
|
SubItemCount = vehModel.modelList.Count,
|
|
CacheExpiryMinutes = expiry.TotalMinutes
|
|
})
|
|
};
|
|
|
|
var insertParentSql = @"
|
|
INSERT INTO SplitCache_Parent (
|
|
CacheKey, ParentData, SubItemCount, DataType, CreatedAt, ExpiresAt, LastAccessed, AccessCount, Metadata
|
|
) VALUES (
|
|
@CacheKey, @ParentData, @SubItemCount, @DataType, @CreatedAt, @ExpiresAt, @LastAccessed, @AccessCount, @Metadata
|
|
)";
|
|
|
|
await conn.ExecuteAsync(insertParentSql, splitParent, transaction);
|
|
var parentTableId = await conn.ExecuteScalarAsync<long>(
|
|
"SELECT last_insert_rowid()",
|
|
transaction: transaction);
|
|
// 2. 插入 ModelList 并收集 KzInfo 待插入
|
|
var allKzInfoItems = new List<SplitCacheKzInfoListItem>();
|
|
|
|
foreach (var keyItem in vehModel.modelList)
|
|
{
|
|
// 插入单条 ModelList
|
|
var modelListItem = new
|
|
{
|
|
ParentTableId = (int)parentTableId,
|
|
CacheKey = cacheKey,
|
|
ParentID = vehModel.ID,
|
|
KeyId = keyItem.id,
|
|
KeyKeyId = keyItem.keyId ?? string.Empty,
|
|
Name = keyItem.Name ?? string.Empty,
|
|
ModelName = keyItem.modelName ?? string.Empty,
|
|
ModelEngName = keyItem.modelEngName ?? string.Empty,
|
|
ModelPuName = keyItem.modelPuName ?? string.Empty,
|
|
ModelXiName = keyItem.modelXiName ?? string.Empty,
|
|
ModelInName = keyItem.modelInName ?? string.Empty,
|
|
Rate = keyItem.rate ?? string.Empty,
|
|
Usable = keyItem.usable ?? string.Empty,
|
|
UsableEn = keyItem.usableEn ?? string.Empty,
|
|
UsablePu = keyItem.usablePu ?? string.Empty,
|
|
UsableXi = keyItem.usableXi ?? string.Empty,
|
|
Information = keyItem.information ?? string.Empty,
|
|
PicUrl = keyItem.picUrl ?? string.Empty,
|
|
FirmwareNo = keyItem.firmwareNo ?? string.Empty,
|
|
SourceUrl = keyItem.sourceUrl ?? string.Empty,
|
|
SpCode = keyItem.spCode ?? string.Empty,
|
|
Support = keyItem.support ?? string.Empty,
|
|
ChipType = keyItem.chipType ?? string.Empty,
|
|
CreateTime = keyItem.createTime ?? string.Empty,
|
|
KeyMatchContent = keyItem.keyMatch?.content ?? string.Empty,
|
|
KeyMatchContentEn = keyItem.keyMatch?.contentEn ?? string.Empty,
|
|
KeyMatchContentPu = keyItem.keyMatch?.contentPu ?? string.Empty,
|
|
KeyMatchContentXi = keyItem.keyMatch?.contentXi ?? string.Empty,
|
|
KeyMatchContentIn = keyItem.keyMatch?.contentIn ?? string.Empty,
|
|
IsSelected = keyItem.IsSelected,
|
|
LocalImagePath = keyItem.LocalImagePath ?? string.Empty
|
|
};
|
|
|
|
await conn.ExecuteAsync(@"
|
|
INSERT INTO SplitCache_ModelList (
|
|
ParentTableId, CacheKey, ParentID, KeyId, KeyKeyId, Name, ModelName, ModelEngName, ModelPuName, ModelXiName, ModelInName,
|
|
Rate, Usable, UsableEn, UsablePu, UsableXi, Information, PicUrl, FirmwareNo, SourceUrl, SpCode,
|
|
Support, ChipType, CreateTime, KeyMatchContent, KeyMatchContentEn, KeyMatchContentPu,
|
|
KeyMatchContentXi, KeyMatchContentIn, IsSelected, LocalImagePath
|
|
) VALUES (
|
|
@ParentTableId, @CacheKey, @ParentID, @KeyId, @KeyKeyId, @Name, @ModelName, @ModelEngName, @ModelPuName, @ModelXiName, @ModelInName,
|
|
@Rate, @Usable, @UsableEn, @UsablePu, @UsableXi, @Information, @PicUrl, @FirmwareNo, @SourceUrl, @SpCode,
|
|
@Support, @ChipType, @CreateTime, @KeyMatchContent, @KeyMatchContentEn, @KeyMatchContentPu,
|
|
@KeyMatchContentXi, @KeyMatchContentIn, @IsSelected, @LocalImagePath
|
|
)", modelListItem, transaction);
|
|
|
|
var modelListId = await conn.ExecuteScalarAsync<long>("SELECT last_insert_rowid()", transaction: transaction);
|
|
|
|
// 3. 收集该 keyItem 下的所有 KzInfo
|
|
if (keyItem.kzInfoList != null && keyItem.kzInfoList.Any())
|
|
{
|
|
foreach (var kz in keyItem.kzInfoList)
|
|
{
|
|
allKzInfoItems.Add(new SplitCacheKzInfoListItem
|
|
{
|
|
ModelListId = (int)modelListId, // 关键:关联到刚插入的 ModelList 记录
|
|
binAddress = kz.binAddress ?? string.Empty,
|
|
binAddressArray = kz.binAddressArray != null ? JsonConvert.SerializeObject(kz.binAddressArray) : "[]",
|
|
chipType = kz.chipType ?? string.Empty,
|
|
cloudRemote = kz.cloudRemote,
|
|
dzxp = kz.dzxp,
|
|
firmwareNo = kz.firmwareNo ?? string.Empty,
|
|
fixedByte = kz.fixedByte,
|
|
fixedByteArray = kz.fixedByteArray != null ? JsonConvert.SerializeObject(kz.fixedByteArray) : "[]",
|
|
fixedByteArrays = kz.fixedByteArrays ?? string.Empty,
|
|
sourceUrl = kz.sourceUrl ?? string.Empty,
|
|
sp = kz.sp,
|
|
spCode = kz.spCode ?? string.Empty,
|
|
tsRand = kz.tsRand,
|
|
tsRandStr = kz.tsRandStr ?? string.Empty,
|
|
type = kz.type ?? string.Empty,
|
|
updateByte = kz.updateByte,
|
|
usable = kz.usable ?? string.Empty,
|
|
ysSource = kz.ysSource ?? string.Empty,
|
|
ysSourceNo = kz.ysSourceNo ?? string.Empty,
|
|
zsId = kz.zsId ?? string.Empty,
|
|
CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. 批量插入所有 KzInfo
|
|
if (allKzInfoItems.Any())
|
|
{
|
|
await conn.ExecuteAsync(@"
|
|
INSERT INTO SplitCache_KzInfoList (
|
|
ModelListId, binAddress, binAddressArray, chipType, cloudRemote, dzxp, firmwareNo,
|
|
fixedByte, fixedByteArray, fixedByteArrays, sourceUrl, sp, spCode, tsRand, tsRandStr,
|
|
type, updateByte, usable, ysSource, ysSourceNo, zsId, CreateTime
|
|
) VALUES (
|
|
@ModelListId, @binAddress, @binAddressArray, @chipType, @cloudRemote, @dzxp, @firmwareNo,
|
|
@fixedByte, @fixedByteArray, @fixedByteArrays, @sourceUrl, @sp, @spCode, @tsRand, @tsRandStr,
|
|
@type, @updateByte, @usable, @ysSource, @ysSourceNo, @zsId, @CreateTime
|
|
)", allKzInfoItems, transaction);
|
|
}
|
|
|
|
// 提交事务
|
|
transaction.Commit();
|
|
//System.Diagnostics.Debug.WriteLine($"拆分缓存写入成功:CacheKey={cacheKey},父级ID={vehModel.ID},子级条数={vehModel.modelList.Count}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
transaction.Rollback();
|
|
//System.Diagnostics.Debug.WriteLine($"写入拆分缓存失败:{ex.Message}");
|
|
throw new Exception($"写入拆分缓存失败(CacheKey={cacheKey})", ex);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
#region 拆分缓存-查询(合并父子表为 VehModelClass)
|
|
/// <summary>
|
|
/// 查询拆分缓存,返回完整的 List<VehModelClass> 实体
|
|
/// </summary>
|
|
public async Task<List<VehModelClass>> GetSplitVehModelDataAsync(string cacheKey)
|
|
{
|
|
if (string.IsNullOrEmpty(cacheKey))
|
|
throw new ArgumentNullException(nameof(cacheKey), "缓存Key不能为空");
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
try
|
|
{
|
|
// 1. 查父表,验证缓存是否存在且未过期
|
|
var getParentSql = @"
|
|
SELECT * FROM SplitCache_Parent
|
|
WHERE CacheKey = @CacheKey AND ExpiresAt > @Now";
|
|
|
|
var splitParents = await _connection.QueryAsync<SplitCacheParent>(getParentSql, new
|
|
{
|
|
CacheKey = cacheKey,
|
|
Now = DateTime.Now
|
|
});
|
|
var splitParentList = splitParents.ToList();
|
|
|
|
if (splitParentList.Count == 0)
|
|
{
|
|
//System.Diagnostics.Debug.WriteLine($"拆分缓存未命中:CacheKey={cacheKey}");
|
|
return null;
|
|
}
|
|
|
|
// 2. 更新父表访问统计
|
|
await UpdateSplitParentAccessStatsAsync(cacheKey);
|
|
|
|
// 3. 查子表(关联 CacheKey,获取所有 VehKeyClass 对应的记录)
|
|
var getChildSql = "SELECT * FROM SplitCache_ModelList WHERE CacheKey = @CacheKey";
|
|
var modelListItems = (await _connection.QueryAsync<SplitCacheModelListItem>(getChildSql, new
|
|
{
|
|
CacheKey = cacheKey
|
|
})).ToList();
|
|
var result = new List<VehModelClass>();
|
|
// 4.批量查询所有关联的 KzInfo(通过 ModelList.Id)
|
|
var modelListIds = modelListItems.Select(m => m.Id).ToArray();
|
|
var getKzInfoSql = $"SELECT * FROM SplitCache_KzInfoList WHERE ModelListId IN ({string.Join(",", modelListIds)})";
|
|
var kzInfoItems = (await _connection.QueryAsync<SplitCacheKzInfoListItem>(getKzInfoSql)).ToList();
|
|
|
|
// 按 ModelListId 分组,便于快速查找
|
|
var kzInfoLookup = kzInfoItems
|
|
.GroupBy(k => k.ModelListId)
|
|
.ToDictionary(g => g.Key, g => g.ToList());
|
|
|
|
foreach (var splitParent in splitParentList)
|
|
{
|
|
// 4. 校验子级数据完整性
|
|
if (modelListItems.Count != splitParent.SubItemCount)
|
|
{
|
|
var errorMsg = $"拆分缓存子级数据不完整:CacheKey={cacheKey},预期 {splitParent.SubItemCount} 条,实际 {modelListItems.Count} 条";
|
|
//System.Diagnostics.Debug.WriteLine(errorMsg);
|
|
await DeleteSplitVehModelDataAsync(cacheKey); // 删除无效缓存
|
|
return null;
|
|
}
|
|
|
|
// 5. 反序列化父级数据(VehModelClass 核心字段)
|
|
var parentCoreData = JsonConvert.DeserializeObject<dynamic>(splitParent.ParentData);
|
|
var vehModel = new VehModelClass
|
|
{
|
|
ID = parentCoreData?.ID ?? 0,
|
|
typeName = parentCoreData?.typeName ?? string.Empty,
|
|
typeEngName = parentCoreData?.typeEngName ?? string.Empty,
|
|
typePuName = parentCoreData?.typePuName ?? string.Empty,
|
|
typeXiName = parentCoreData?.typeXiName ?? string.Empty,
|
|
typeInName = parentCoreData?.typeInName ?? string.Empty,
|
|
modelList = new List<VehKeyClass>()
|
|
};
|
|
|
|
// 6. 转换子表数据为 VehKeyClass 实体(含 keyMatch 嵌套类)
|
|
foreach (var item in modelListItems)
|
|
{
|
|
var vehKey = new VehKeyClass
|
|
{
|
|
parentId = item.ParentID,
|
|
id = item.KeyId,
|
|
keyId = item.KeyKeyId,
|
|
Name = item.Name,
|
|
modelName = item.ModelName,
|
|
modelEngName = item.ModelEngName,
|
|
modelPuName = item.ModelPuName,
|
|
modelXiName = item.ModelXiName,
|
|
modelInName = item.ModelInName,
|
|
rate = item.Rate,
|
|
usable = item.Usable,
|
|
usableEn = item.UsableEn,
|
|
usablePu = item.UsablePu,
|
|
usableXi = item.UsableXi,
|
|
information = item.Information,
|
|
picUrl = item.PicUrl,
|
|
firmwareNo = item.FirmwareNo,
|
|
sourceUrl = item.SourceUrl,
|
|
spCode = item.SpCode,
|
|
support = item.Support,
|
|
chipType = item.ChipType,
|
|
createTime = item.CreateTime,
|
|
// 重组 keyMatch 嵌套类
|
|
keyMatch = new keyMatch
|
|
{
|
|
content = item.KeyMatchContent,
|
|
contentEn = item.KeyMatchContentEn,
|
|
contentPu = item.KeyMatchContentPu,
|
|
contentXi = item.KeyMatchContentXi,
|
|
contentIn = item.KeyMatchContentIn
|
|
},
|
|
// UI 相关字段(BitmapImage 需业务层自行加载,此处仅设置 LocalImagePath)
|
|
LocalImagePath = item.LocalImagePath,
|
|
IsSelected = item.IsSelected,
|
|
kzInfoList = new List<KzInfo>() // 初始化
|
|
};
|
|
// 挂载对应的 KzInfo 列表
|
|
if (kzInfoLookup.TryGetValue(item.Id, out var kzList))
|
|
{
|
|
foreach (var kzItem in kzList)
|
|
{
|
|
vehKey.kzInfoList.Add(new KzInfo
|
|
{
|
|
binAddress = kzItem.binAddress,
|
|
binAddressArray = !string.IsNullOrEmpty(kzItem.binAddressArray)
|
|
? JsonConvert.DeserializeObject<List<string>>(kzItem.binAddressArray)
|
|
: new List<string>(),
|
|
chipType = kzItem.chipType,
|
|
cloudRemote = kzItem.cloudRemote,
|
|
dzxp = kzItem.dzxp,
|
|
firmwareNo = kzItem.firmwareNo,
|
|
fixedByte = kzItem.fixedByte,
|
|
fixedByteArray = !string.IsNullOrEmpty(kzItem.fixedByteArray)
|
|
? JsonConvert.DeserializeObject<List<string>>(kzItem.fixedByteArray)
|
|
: new List<string>(),
|
|
fixedByteArrays = kzItem.fixedByteArrays,
|
|
sourceUrl = kzItem.sourceUrl,
|
|
sp = kzItem.sp,
|
|
spCode = kzItem.spCode,
|
|
tsRand = kzItem.tsRand,
|
|
tsRandStr = kzItem.tsRandStr,
|
|
type = kzItem.type,
|
|
updateByte = kzItem.updateByte,
|
|
usable = kzItem.usable,
|
|
ysSource = kzItem.ysSource,
|
|
ysSourceNo = kzItem.ysSourceNo,
|
|
zsId = kzItem.zsId
|
|
});
|
|
}
|
|
}
|
|
vehModel.modelList.Add(vehKey);
|
|
}
|
|
result.Add(vehModel);
|
|
}
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//System.Diagnostics.Debug.WriteLine($"查询拆分缓存失败:{ex.Message}");
|
|
return null;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 拆分缓存-删除(同步删除父子表数据)
|
|
/// <summary>
|
|
/// 删除指定 CacheKey 的拆分缓存(父子表同步删除)
|
|
/// </summary>
|
|
public async Task DeleteSplitVehModelDataAsync(string cacheKey)
|
|
{
|
|
if (string.IsNullOrEmpty(cacheKey))
|
|
throw new ArgumentNullException(nameof(cacheKey), "缓存Key不能为空");
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
using (var transaction = _connection.BeginTransaction())
|
|
{
|
|
try
|
|
{
|
|
// 1. 先删除子表(外键约束 ON DELETE CASCADE 也会自动删除,但手动删除更清晰)
|
|
var deleteChildCount = await _connection.ExecuteAsync(
|
|
"DELETE FROM SplitCache_ModelList WHERE CacheKey = @CacheKey",
|
|
new { CacheKey = cacheKey },
|
|
transaction
|
|
);
|
|
|
|
// 2. 再删除父表
|
|
var deleteParentCount = await _connection.ExecuteAsync(
|
|
"DELETE FROM SplitCache_Parent WHERE CacheKey = @CacheKey",
|
|
new { CacheKey = cacheKey },
|
|
transaction
|
|
);
|
|
|
|
transaction.Commit();
|
|
System.Diagnostics.Debug.WriteLine($"拆分缓存删除成功:CacheKey={cacheKey},删除子级{deleteChildCount}条,父级{deleteParentCount}条");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
transaction.Rollback();
|
|
System.Diagnostics.Debug.WriteLine($"删除拆分缓存失败:{ex.Message}");
|
|
throw new Exception($"删除拆分缓存失败(CacheKey={cacheKey})", ex);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 拆分缓存-过期清理(独立清理,不影响旧缓存)
|
|
/// <summary>
|
|
/// 清理拆分缓存中过期的数据(父子表同步清理)
|
|
/// </summary>
|
|
public async Task ClearSplitCacheExpiredAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
using (var transaction = _connection.BeginTransaction())
|
|
{
|
|
try
|
|
{
|
|
// 1. 查询所有过期的父表 CacheKey
|
|
var expiredKeysSql = "SELECT CacheKey FROM SplitCache_Parent WHERE ExpiresAt <= @Now";
|
|
var expiredKeys = (await _connection.QueryAsync<string>(expiredKeysSql, new { Now = DateTime.Now }, transaction)).ToList();
|
|
|
|
if (!expiredKeys.Any())
|
|
{
|
|
transaction.Commit();
|
|
//System.Diagnostics.Debug.WriteLine("无过期拆分缓存数据");
|
|
return;
|
|
}
|
|
|
|
// 2. 批量删除过期子表数据
|
|
var deleteChildCount = await _connection.ExecuteAsync(
|
|
"DELETE FROM SplitCache_ModelList WHERE CacheKey IN @ExpiredKeys",
|
|
new { ExpiredKeys = expiredKeys },
|
|
transaction
|
|
);
|
|
|
|
// 3. 批量删除过期父表数据
|
|
var deleteParentCount = await _connection.ExecuteAsync(
|
|
"DELETE FROM SplitCache_Parent WHERE CacheKey IN @ExpiredKeys",
|
|
new { ExpiredKeys = expiredKeys },
|
|
transaction
|
|
);
|
|
|
|
transaction.Commit();
|
|
//System.Diagnostics.Debug.WriteLine($"清理过期拆分缓存成功:共{expiredKeys.Count}组,删除子级{deleteChildCount}条,父级{deleteParentCount}条");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
transaction.Rollback();
|
|
//System.Diagnostics.Debug.WriteLine($"清理过期拆分缓存失败:{ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 拆分缓存-辅助方法
|
|
/// <summary>
|
|
/// 更新拆分缓存父表的访问统计
|
|
/// </summary>
|
|
private async Task UpdateSplitParentAccessStatsAsync(string cacheKey)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = @"
|
|
UPDATE SplitCache_Parent
|
|
SET LastAccessed = @LastAccessed, AccessCount = AccessCount + 1
|
|
WHERE CacheKey = @CacheKey";
|
|
|
|
await _connection.ExecuteAsync(sql, new
|
|
{
|
|
CacheKey = cacheKey,
|
|
LastAccessed = DateTime.Now
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查拆分缓存是否存在(未过期)
|
|
/// </summary>
|
|
public async Task<bool> SplitCacheExistsAsync(string cacheKey)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = "SELECT 1 FROM SplitCache_Parent WHERE CacheKey = @CacheKey AND ExpiresAt > @Now";
|
|
var result = await _connection.ExecuteScalarAsync<int?>(sql, new
|
|
{
|
|
CacheKey = cacheKey,
|
|
Now = DateTime.Now
|
|
});
|
|
return result.HasValue;
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region 缓存基础方法
|
|
public async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> dataFetchFunc, TimeSpan expiry) where T : class
|
|
{
|
|
// 先尝试从缓存获取
|
|
var cachedData = await GetAsync<T>(key);
|
|
if (cachedData != null)
|
|
{
|
|
if (cachedData switch
|
|
{
|
|
ICollection { Count: > 0 } => true,
|
|
Array { Length: > 0 } => true,
|
|
string { Length: > 0 } => true,
|
|
_ => false
|
|
})
|
|
{
|
|
return cachedData;
|
|
}
|
|
//return cachedData;
|
|
}
|
|
|
|
// 缓存未命中,从数据源获取
|
|
var freshData = await dataFetchFunc();
|
|
|
|
if (freshData != null)
|
|
{
|
|
// 异步保存到缓存(不阻塞当前操作)
|
|
_ = SetAsync(key, freshData, expiry);
|
|
}
|
|
|
|
return freshData;
|
|
}
|
|
|
|
public async Task<T> GetAsync<T>(string key) where T : class
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
try
|
|
{
|
|
var sql = @"
|
|
SELECT * FROM CacheItems
|
|
WHERE CacheKey = @Key AND ExpiresAt > @Now";
|
|
|
|
var cacheItem = await _connection.QueryFirstOrDefaultAsync<CacheItem>(sql, new
|
|
{
|
|
Key = key,
|
|
Now = DateTime.Now
|
|
});
|
|
|
|
if (cacheItem != null)
|
|
{
|
|
// 更新访问统计
|
|
await UpdateAccessStatsAsync(key);
|
|
|
|
// 反序列化数据
|
|
return DeserializeData<T>(cacheItem.Data);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//System.Diagnostics.Debug.WriteLine($"缓存读取失败: {ex.Message}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task SetAsync<T>(string key, T data, TimeSpan expiry) where T : class
|
|
{
|
|
if (data == null) return;
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
try
|
|
{
|
|
var cacheItem = new CacheItem
|
|
{
|
|
CacheKey = key,
|
|
Data = SerializeData(data),
|
|
DataType = typeof(T).FullName,
|
|
CreatedAt = DateTime.Now,
|
|
ExpiresAt = DateTime.Now.Add(expiry),
|
|
LastAccessed = DateTime.Now,
|
|
AccessCount = 0,
|
|
Metadata = JsonConvert.SerializeObject(new
|
|
{
|
|
DataSize = GetDataSize(data),
|
|
ItemCount = data is System.Collections.ICollection collection ? collection.Count : 0
|
|
})
|
|
};
|
|
|
|
var sql = @"
|
|
INSERT OR REPLACE INTO CacheItems
|
|
(CacheKey, Data, DataType, CreatedAt, ExpiresAt, LastAccessed, AccessCount, Metadata)
|
|
VALUES
|
|
(@CacheKey, @Data, @DataType, @CreatedAt, @ExpiresAt, @LastAccessed, @AccessCount, @Metadata)";
|
|
|
|
await _connection.ExecuteAsync(sql, cacheItem);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//System.Diagnostics.Debug.WriteLine($"缓存保存失败: {ex.Message}");
|
|
|
|
}
|
|
}
|
|
|
|
public async Task<bool> ExistsAsync(string key)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = "SELECT 1 FROM CacheItems WHERE CacheKey = @Key AND ExpiresAt > @Now";
|
|
var result = await _connection.ExecuteScalarAsync<int?>(sql, new
|
|
{
|
|
Key = key,
|
|
Now = DateTime.Now
|
|
});
|
|
return result.HasValue;
|
|
}
|
|
|
|
public async Task RemoveAsync(string key)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
await _connection.ExecuteAsync("DELETE FROM CacheItems WHERE CacheKey = @Key", new { Key = key });
|
|
}
|
|
|
|
public async Task ClearExpiredAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
await _connection.ExecuteAsync("DELETE FROM CacheItems WHERE ExpiresAt <= @Now", new { Now = DateTime.Now });
|
|
}
|
|
|
|
public async Task ClearAllAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
await _connection.ExecuteAsync("DELETE FROM CacheItems");
|
|
await _connection.ExecuteAsync("DELETE FROM SplitCache_Parent");
|
|
await _connection.ExecuteAsync("DELETE FROM SplitCache_ModelList");
|
|
}
|
|
|
|
public async Task<IEnumerable<CacheItem>> GetCacheStatsAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
return await _connection.QueryAsync<CacheItem>(
|
|
"SELECT * FROM CacheItems ORDER BY LastAccessed DESC");
|
|
}
|
|
|
|
public async Task<long> GetCacheSizeAsync()
|
|
{
|
|
try
|
|
{
|
|
var fileInfo = new FileInfo(_databasePath);
|
|
return fileInfo.Exists ? fileInfo.Length : 0;
|
|
}
|
|
catch
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private async Task UpdateAccessStatsAsync(string key)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = @"
|
|
UPDATE CacheItems
|
|
SET LastAccessed = @LastAccessed, AccessCount = AccessCount + 1
|
|
WHERE CacheKey = @Key";
|
|
|
|
await _connection.ExecuteAsync(sql, new
|
|
{
|
|
Key = key,
|
|
LastAccessed = DateTime.Now
|
|
});
|
|
}
|
|
|
|
private string SerializeData<T>(T data)
|
|
{
|
|
return JsonConvert.SerializeObject(data, new JsonSerializerSettings
|
|
{
|
|
TypeNameHandling = TypeNameHandling.Auto,
|
|
Formatting = Formatting.Indented,
|
|
NullValueHandling = NullValueHandling.Ignore
|
|
});
|
|
}
|
|
|
|
private T DeserializeData<T>(string data)
|
|
{
|
|
return JsonConvert.DeserializeObject<T>(data, new JsonSerializerSettings
|
|
{
|
|
TypeNameHandling = TypeNameHandling.Auto
|
|
});
|
|
}
|
|
|
|
private long GetDataSize<T>(T data)
|
|
{
|
|
try
|
|
{
|
|
var json = SerializeData(data);
|
|
return System.Text.Encoding.UTF8.GetByteCount(json);
|
|
}
|
|
catch
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
_connection?.Close();
|
|
_connection?.Dispose();
|
|
_disposed = true;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 用户Token
|
|
public async Task StoreUserTokenAsync(string token, TimeSpan? expiry = null)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var createdAt = DateTime.Now;
|
|
DateTime? expiresAt = DateTime.Now.AddYears(10);
|
|
if (expiry.HasValue)
|
|
{
|
|
expiresAt = createdAt.Add(expiry.Value);
|
|
}
|
|
var sql = @"
|
|
INSERT INTO SysUserToken (Token, CreatedAt, ExpiresAt)
|
|
VALUES (@Token, @CreatedAt, @ExpiresAt)";
|
|
await _connection.ExecuteAsync(sql, new
|
|
{
|
|
Token = token,
|
|
CreatedAt = createdAt,
|
|
ExpiresAt = expiresAt
|
|
});
|
|
}
|
|
public async Task<bool> IsUserTokenValidAsync(string token)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = @"
|
|
SELECT 1 FROM SysUserToken
|
|
WHERE Token = @Token AND ( ExpiresAt > @Now)";
|
|
var result = await _connection.ExecuteScalarAsync<int?>(sql, new
|
|
{
|
|
Token = token,
|
|
Now = DateTime.Now
|
|
});
|
|
return result.HasValue;
|
|
}
|
|
public async Task ClearUserTokenAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = "DELETE FROM SysUserToken ";
|
|
await _connection.ExecuteAsync(sql);
|
|
}
|
|
public async Task ClearExpiredUserTokensAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = "DELETE FROM SysUserToken WHERE ExpiresAt <= @Now";
|
|
await _connection.ExecuteAsync(sql, new { Now = DateTime.Now });
|
|
}
|
|
public async Task<string?> GetLatestUserTokenAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = @"
|
|
SELECT Token FROM SysUserToken
|
|
WHERE ExpiresAt > @Now
|
|
ORDER BY CreatedAt DESC LIMIT 1";
|
|
return await _connection.ExecuteScalarAsync<string?>(sql, new { Now = DateTime.Now });
|
|
}
|
|
public async Task<bool> StoreUserInfoAsync(UserModel user)
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = @"
|
|
INSERT OR REPLACE INTO SysUser
|
|
(Id, CreateTime, DeptId, Email, Image, Local, NickName, Phone, Remark,
|
|
Sex, UpdateTime, UserName, UserStatus, UserType, Uuid, WorkerAccount, WorkerId)
|
|
VALUES
|
|
(@Id, @CreateTime, @DeptId, @Email, @Image, @Local, @NickName, @Phone, @Remark,
|
|
@Sex, @UpdateTime, @UserName, @UserStatus, @UserType, @Uuid, @WorkerAccount, @WorkerId)";
|
|
var rowsAffected = await _connection.ExecuteAsync(sql, user);
|
|
return rowsAffected > 0;
|
|
}
|
|
public async Task ClearUserInfoAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = "DELETE FROM SysUser";
|
|
await _connection.ExecuteAsync(sql);
|
|
}
|
|
public async Task<UserModel?> GetUserInfoAsync()
|
|
{
|
|
using var pooledConn = SQLiteCore.GetConnection();
|
|
var _connection = pooledConn.InnerConnection;
|
|
var sql = @"
|
|
SELECT * FROM SysUser LIMIT 1";
|
|
return await _connection.QueryFirstOrDefaultAsync<UserModel?>(sql);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
|