缓存在某一时间戳状态下,对某行进行某个action的判断结果
This commit is contained in:
parent
f6d6204ce9
commit
16ce411444
|
|
@ -5,7 +5,6 @@ import { makeIntrinsicLogics } from 'oak-domain/lib/store/IntrinsicLogics';
|
||||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||||
import { LocalStorage } from '../features/localStorage';
|
import { LocalStorage } from '../features/localStorage';
|
||||||
import { cloneDeep } from 'oak-domain/lib/utils/lodash';
|
import { cloneDeep } from 'oak-domain/lib/utils/lodash';
|
||||||
|
|
||||||
async function initDataInStore(store, initialData, stat) {
|
async function initDataInStore(store, initialData, stat) {
|
||||||
store.resetInitialData(initialData, stat);
|
store.resetInitialData(initialData, stat);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict> extends Featu
|
||||||
private attrUpdateMatrix?;
|
private attrUpdateMatrix?;
|
||||||
private connector;
|
private connector;
|
||||||
private baseRelationAuth;
|
private baseRelationAuth;
|
||||||
|
private entityActionAuthDict;
|
||||||
constructor(storageSchema: StorageSchema<ED>, connector: Connector<ED, SyncContext<ED>>, frontendContextBuilder: (store: CacheStore<ED>) => SyncContext<ED>, checkers: Array<Checker<ED, keyof ED, SyncContext<ED>>>, localStorage: LocalStorage, common: CommonConfiguration<ED>);
|
constructor(storageSchema: StorageSchema<ED>, connector: Connector<ED, SyncContext<ED>>, frontendContextBuilder: (store: CacheStore<ED>) => SyncContext<ED>, checkers: Array<Checker<ED, keyof ED, SyncContext<ED>>>, localStorage: LocalStorage, common: CommonConfiguration<ED>);
|
||||||
/**
|
/**
|
||||||
* 处理cache中需要缓存的数据
|
* 处理cache中需要缓存的数据
|
||||||
|
|
@ -96,7 +97,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict> extends Featu
|
||||||
action: ED[T]['Action'];
|
action: ED[T]['Action'];
|
||||||
data?: ED[T]['Operation']['data'];
|
data?: ED[T]['Operation']['data'];
|
||||||
filter?: ED[T]['Operation']['filter'];
|
filter?: ED[T]['Operation']['filter'];
|
||||||
}, checkerTypes?: CheckerType[]): boolean | OakUserException<ED>;
|
}, checkerTypes?: CheckerType[]): boolean | { [A in ED[T]["Action"]]: boolean | OakUserException<ED>; }[ED[T]["Action"]];
|
||||||
redoOperation(opers: Array<{
|
redoOperation(opers: Array<{
|
||||||
entity: keyof ED;
|
entity: keyof ED;
|
||||||
operation: ED[keyof ED]['Operation'];
|
operation: ED[keyof ED]['Operation'];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Feature } from '../types/Feature';
|
import { Feature } from '../types/Feature';
|
||||||
import { pull, intersection, unset, union } from 'oak-domain/lib/utils/lodash';
|
import { set, pull, intersection, unset, union } from 'oak-domain/lib/utils/lodash';
|
||||||
import { CacheStore } from '../cacheStore/CacheStore';
|
import { CacheStore } from '../cacheStore/CacheStore';
|
||||||
import { OakRowUnexistedException, OakException, OakUserException } from 'oak-domain/lib/types/Exception';
|
import { OakRowUnexistedException, OakException, OakUserException } from 'oak-domain/lib/types/Exception';
|
||||||
import { assert } from 'oak-domain/lib/utils/assert';
|
import { assert } from 'oak-domain/lib/utils/assert';
|
||||||
|
|
@ -22,6 +22,8 @@ export class Cache extends Feature {
|
||||||
attrUpdateMatrix;
|
attrUpdateMatrix;
|
||||||
connector;
|
connector;
|
||||||
baseRelationAuth;
|
baseRelationAuth;
|
||||||
|
// 缓存在某一时间戳状态下,对某行进行某个action的判断结果
|
||||||
|
entityActionAuthDict;
|
||||||
constructor(storageSchema, connector, frontendContextBuilder, checkers, localStorage, common) {
|
constructor(storageSchema, connector, frontendContextBuilder, checkers, localStorage, common) {
|
||||||
super();
|
super();
|
||||||
this.syncEventsCallbacks = [];
|
this.syncEventsCallbacks = [];
|
||||||
|
|
@ -37,6 +39,7 @@ export class Cache extends Feature {
|
||||||
// 现在这个init变成了异步行为,不知道有没有影响。by Xc 20231126
|
// 现在这个init变成了异步行为,不知道有没有影响。by Xc 20231126
|
||||||
this.initPromise = new Promise((resolve) => this.initSavedLogic(resolve));
|
this.initPromise = new Promise((resolve) => this.initSavedLogic(resolve));
|
||||||
this.buildEntityGraph();
|
this.buildEntityGraph();
|
||||||
|
this.entityActionAuthDict = {};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 处理cache中需要缓存的数据
|
* 处理cache中需要缓存的数据
|
||||||
|
|
@ -86,7 +89,7 @@ export class Cache extends Feature {
|
||||||
this.refreshing++;
|
this.refreshing++;
|
||||||
const { result, opRecords, message } = await this.connector.callAspect(name, params, ignoreContext ? undefined : this.context || this.contextBuilder());
|
const { result, opRecords, message } = await this.connector.callAspect(name, params, ignoreContext ? undefined : this.context || this.contextBuilder());
|
||||||
callback && callback(result, opRecords);
|
callback && callback(result, opRecords);
|
||||||
if (opRecords) {
|
if (opRecords?.length) {
|
||||||
this.syncInner(opRecords);
|
this.syncInner(opRecords);
|
||||||
}
|
}
|
||||||
this.refreshing--;
|
this.refreshing--;
|
||||||
|
|
@ -257,15 +260,20 @@ export class Cache extends Feature {
|
||||||
syncInner(records) {
|
syncInner(records) {
|
||||||
this.begin();
|
this.begin();
|
||||||
this.cacheStore.sync(records, this.context);
|
this.cacheStore.sync(records, this.context);
|
||||||
|
const ts2 = this.cacheStore.getLastUpdateTs();
|
||||||
|
// 同步以后,当前所有缓存的action结果都不成立了
|
||||||
|
this.entityActionAuthDict = {};
|
||||||
// 唤起同步注册的回调
|
// 唤起同步注册的回调
|
||||||
this.syncEventsCallbacks.map((ele) => ele(records));
|
this.syncEventsCallbacks.map((ele) => ele(records));
|
||||||
this.context.commit();
|
this.context.commit();
|
||||||
this.context = undefined;
|
this.context = undefined;
|
||||||
}
|
}
|
||||||
sync(records) {
|
sync(records) {
|
||||||
|
if (records.length) {
|
||||||
this.syncInner(records);
|
this.syncInner(records);
|
||||||
this.publish();
|
this.publish();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 前端缓存做operation只可能是测试权限,必然回滚
|
* 前端缓存做operation只可能是测试权限,必然回滚
|
||||||
* @param entity
|
* @param entity
|
||||||
|
|
@ -325,10 +333,24 @@ export class Cache extends Feature {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
checkOperation(entity, operation, checkerTypes) {
|
checkOperation(entity, operation, checkerTypes) {
|
||||||
|
// 缓存同一id对同一action的判断,避免性能低下
|
||||||
|
const { action, data, filter } = operation;
|
||||||
|
let id;
|
||||||
|
let ts;
|
||||||
|
if (filter && !data && typeof filter.id === 'string') {
|
||||||
|
id = filter.id;
|
||||||
|
ts = this.cacheStore.getLastUpdateTs();
|
||||||
|
if (this.entityActionAuthDict[ts]?.[entity]?.[id]?.hasOwnProperty(action)) {
|
||||||
|
return this.entityActionAuthDict[ts][entity][id][action];
|
||||||
|
}
|
||||||
|
}
|
||||||
const rollback = this.begin(true);
|
const rollback = this.begin(true);
|
||||||
try {
|
try {
|
||||||
this.cacheStore.check(entity, operation, this.context, checkerTypes);
|
this.cacheStore.check(entity, operation, this.context, checkerTypes);
|
||||||
rollback && rollback();
|
rollback && rollback();
|
||||||
|
if (id && ts) {
|
||||||
|
set(this.entityActionAuthDict, `${ts}.${entity}.${id}.${action}`, true);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|
@ -336,6 +358,9 @@ export class Cache extends Feature {
|
||||||
if (err instanceof OakRowUnexistedException) {
|
if (err instanceof OakRowUnexistedException) {
|
||||||
// 有外键缺失,尝试发一下请求
|
// 有外键缺失,尝试发一下请求
|
||||||
this.fetchRows(err.getRows());
|
this.fetchRows(err.getRows());
|
||||||
|
if (id && ts) {
|
||||||
|
set(this.entityActionAuthDict, `${ts}.${entity}.${id}.${action}`, false);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
@ -345,6 +370,9 @@ export class Cache extends Feature {
|
||||||
/* if (!(err instanceof OakUserException)) {
|
/* if (!(err instanceof OakUserException)) {
|
||||||
throw err;
|
throw err;
|
||||||
} */
|
} */
|
||||||
|
if (id && ts) {
|
||||||
|
set(this.entityActionAuthDict, `${ts}.${entity}.${id}.${action}`, err);
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { cloneDeep, unset, uniq } from "oak-domain/lib/utils/lodash";
|
||||||
import { combineFilters, getRelevantIds } from "oak-domain/lib/store/filter";
|
import { combineFilters, getRelevantIds } from "oak-domain/lib/store/filter";
|
||||||
import { createOperationsFromModies } from 'oak-domain/lib/store/modi';
|
import { createOperationsFromModies } from 'oak-domain/lib/store/modi';
|
||||||
import { judgeRelation } from "oak-domain/lib/store/relation";
|
import { judgeRelation } from "oak-domain/lib/store/relation";
|
||||||
|
import { CreateAtAttribute, UpdateAtAttribute, DeleteAtAttribute } from "oak-domain/lib/types";
|
||||||
import { Feature } from '../types/Feature';
|
import { Feature } from '../types/Feature';
|
||||||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||||
export const MODI_NEXT_PATH_SUFFIX = ':next';
|
export const MODI_NEXT_PATH_SUFFIX = ':next';
|
||||||
|
|
@ -729,9 +730,14 @@ class ListNode extends EntityNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const id = item.id || generateNewId();
|
const id = item.id || generateNewId();
|
||||||
|
const now = Date.now();
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
data: Object.assign(item, { id }),
|
data: Object.assign(item, {
|
||||||
|
id,
|
||||||
|
[CreateAtAttribute]: now,
|
||||||
|
[UpdateAtAttribute]: now,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
@ -748,7 +754,9 @@ class ListNode extends EntityNode {
|
||||||
removeItemInner(id, lsn) {
|
removeItemInner(id, lsn) {
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {
|
||||||
|
[DeleteAtAttribute]: Date.now(),
|
||||||
|
},
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
|
@ -794,7 +802,9 @@ class ListNode extends EntityNode {
|
||||||
updateItemInner(lsn, data, id, action) {
|
updateItemInner(lsn, data, id, action) {
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: action || 'update',
|
action: action || 'update',
|
||||||
data,
|
data: Object.assign({}, data, {
|
||||||
|
[UpdateAtAttribute]: Date.now(),
|
||||||
|
}),
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
|
@ -1215,9 +1225,14 @@ class SingleNode extends EntityNode {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const now = Date.now();
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
data: Object.assign({}, data, { id }),
|
data: Object.assign({}, data, {
|
||||||
|
id,
|
||||||
|
[CreateAtAttribute]: now,
|
||||||
|
[UpdateAtAttribute]: now,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
this.refreshListChildren();
|
this.refreshListChildren();
|
||||||
this.setDirty();
|
this.setDirty();
|
||||||
|
|
@ -1238,7 +1253,9 @@ class SingleNode extends EntityNode {
|
||||||
undefinedAttrs.forEach((attr) => unset(data, attr));
|
undefinedAttrs.forEach((attr) => unset(data, attr));
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: action || 'update',
|
action: action || 'update',
|
||||||
data,
|
data: Object.assign({}, data, {
|
||||||
|
[UpdateAtAttribute]: Date.now(),
|
||||||
|
}),
|
||||||
filter: {
|
filter: {
|
||||||
id: this.getId(),
|
id: this.getId(),
|
||||||
}
|
}
|
||||||
|
|
@ -1269,7 +1286,9 @@ class SingleNode extends EntityNode {
|
||||||
assert(id);
|
assert(id);
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {
|
||||||
|
[DeleteAtAttribute]: Date.now(),
|
||||||
|
},
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ export class SubScriber extends Feature {
|
||||||
});
|
});
|
||||||
if (!optionInited) {
|
if (!optionInited) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
socket.on('connect_error', async () => {
|
socket.on('connect_error', async (err) => {
|
||||||
count++;
|
count++;
|
||||||
if (count > 50) {
|
if (count > 50) {
|
||||||
// 可能socket地址改变了,刷新重连
|
// 可能socket地址改变了,刷新重连
|
||||||
|
|
@ -113,7 +113,7 @@ export class SubScriber extends Feature {
|
||||||
}
|
}
|
||||||
socket.removeAllListeners();
|
socket.removeAllListeners();
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
this.socket = null;
|
this.url = undefined;
|
||||||
await this.connect();
|
await this.connect();
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -49,21 +49,21 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
|
||||||
redirectTo<T2_1 extends keyof ED>(options: {
|
redirectTo<T2_1 extends keyof ED>(options: {
|
||||||
url: string;
|
url: string;
|
||||||
} & OakNavigateToParameters<ED, T2_1>, state?: Record<string, any> | undefined, disableNamespace?: boolean | undefined): Promise<void>;
|
} & OakNavigateToParameters<ED, T2_1>, state?: Record<string, any> | undefined, disableNamespace?: boolean | undefined): Promise<void>;
|
||||||
addItem<T_1 extends keyof ED>(data: Omit<ED[T_1]["CreateSingle"]["data"], "id"> & {
|
addItem<T extends keyof ED>(data: Omit<ED[T]["CreateSingle"]["data"], "id"> & {
|
||||||
id?: string | undefined;
|
id?: string | undefined;
|
||||||
}, path?: string | undefined): string;
|
}, path?: string | undefined): string;
|
||||||
addItems<T_2 extends keyof ED>(data: (Omit<ED[T_2]["CreateSingle"]["data"], "id"> & {
|
addItems<T_1 extends keyof ED>(data: (Omit<ED[T_1]["CreateSingle"]["data"], "id"> & {
|
||||||
id?: string | undefined;
|
id?: string | undefined;
|
||||||
})[], path?: string | undefined): string[];
|
})[], path?: string | undefined): string[];
|
||||||
removeItem(id: string, path?: string | undefined): void;
|
removeItem(id: string, path?: string | undefined): void;
|
||||||
removeItems(ids: string[], path?: string | undefined): void;
|
removeItems(ids: string[], path?: string | undefined): void;
|
||||||
updateItem<T_3 extends keyof ED>(data: ED[T_3]["Update"]["data"], id: string, action?: ED[T_3]["Action"] | undefined, path?: string | undefined): void;
|
updateItem<T_2 extends keyof ED>(data: ED[T_2]["Update"]["data"], id: string, action?: ED[T_2]["Action"] | undefined, path?: string | undefined): void;
|
||||||
updateItems<T_4 extends keyof ED>(data: ED[T_4]["Update"]["data"], ids: string[], action?: ED[T_4]["Action"] | undefined, path?: string | undefined): void;
|
updateItems<T_3 extends keyof ED>(data: ED[T_3]["Update"]["data"], ids: string[], action?: ED[T_3]["Action"] | undefined, path?: string | undefined): void;
|
||||||
recoverItem(id: string, path?: string | undefined): void;
|
recoverItem(id: string, path?: string | undefined): void;
|
||||||
recoverItems(ids: string[], path?: string | undefined): void;
|
recoverItems(ids: string[], path?: string | undefined): void;
|
||||||
resetItem(id: string, path?: string | undefined): void;
|
resetItem(id: string, path?: string | undefined): void;
|
||||||
update<T_5 extends keyof ED>(data: ED[T_5]["Update"]["data"], action?: ED[T_5]["Action"] | undefined, path?: string | undefined): void;
|
update<T_4 extends keyof ED>(data: ED[T_4]["Update"]["data"], action?: ED[T_4]["Action"] | undefined, path?: string | undefined): void;
|
||||||
create<T_6 extends keyof ED>(data: Omit<ED[T_6]["CreateSingle"]["data"], "id">, path?: string | undefined): void;
|
create<T_5 extends keyof ED>(data: Omit<ED[T_5]["CreateSingle"]["data"], "id">, path?: string | undefined): void;
|
||||||
remove(path?: string | undefined): void;
|
remove(path?: string | undefined): void;
|
||||||
isCreation(path?: string | undefined): boolean;
|
isCreation(path?: string | undefined): boolean;
|
||||||
clean(lsn?: number | undefined, dontPublish?: true | undefined, path?: string | undefined): void;
|
clean(lsn?: number | undefined, dontPublish?: true | undefined, path?: string | undefined): void;
|
||||||
|
|
@ -75,9 +75,9 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
|
||||||
}[] | undefined): Promise<void>;
|
}[] | undefined): Promise<void>;
|
||||||
isDirty(path?: string | undefined): boolean;
|
isDirty(path?: string | undefined): boolean;
|
||||||
getFreshValue(path?: string | undefined): Partial<import("oak-domain/lib/types").GeneralEntityShape> | Partial<import("oak-domain/lib/types").GeneralEntityShape>[] | undefined;
|
getFreshValue(path?: string | undefined): Partial<import("oak-domain/lib/types").GeneralEntityShape> | Partial<import("oak-domain/lib/types").GeneralEntityShape>[] | undefined;
|
||||||
checkOperation<T2_2 extends keyof ED>(entity: T2_2, operation: Omit<ED[T2_2]["Operation"], "id">, checkerTypes?: (CheckerType | "relation")[] | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
checkOperation<T2_2 extends keyof ED>(entity: T2_2, operation: Omit<ED[T2_2]["Operation"], "id">, checkerTypes?: (CheckerType | "relation")[] | undefined): boolean | { [A in ED[T2_2]["Action"]]: boolean | import("oak-domain/lib/types").OakUserException<ED>; }[ED[T2_2]["Action"]];
|
||||||
tryExecute(path?: string | undefined, action?: string | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
tryExecute(path?: string | undefined, action?: string | undefined): boolean | { [A_1 in ED[keyof ED]["Action"]]: boolean | import("oak-domain/lib/types").OakUserException<ED>; }[ED[keyof ED]["Action"]];
|
||||||
getOperations<T_7 extends keyof ED>(path?: string | undefined): {
|
getOperations<T_6 extends keyof ED>(path?: string | undefined): {
|
||||||
entity: keyof ED;
|
entity: keyof ED;
|
||||||
operation: ED[keyof ED]["Operation"];
|
operation: ED[keyof ED]["Operation"];
|
||||||
}[] | undefined;
|
}[] | undefined;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ const DebugStore_1 = require("./DebugStore");
|
||||||
const IntrinsicLogics_1 = require("oak-domain/lib/store/IntrinsicLogics");
|
const IntrinsicLogics_1 = require("oak-domain/lib/store/IntrinsicLogics");
|
||||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||||
const localStorage_1 = require("../features/localStorage");
|
const localStorage_1 = require("../features/localStorage");
|
||||||
|
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||||
async function initDataInStore(store, initialData, stat) {
|
async function initDataInStore(store, initialData, stat) {
|
||||||
store.resetInitialData(initialData, stat);
|
store.resetInitialData(initialData, stat);
|
||||||
}
|
}
|
||||||
|
|
@ -41,8 +42,8 @@ async function materializeData(data, stat, localStorage) {
|
||||||
async function execWatcher(store, watcher, context) {
|
async function execWatcher(store, watcher, context) {
|
||||||
if (watcher.hasOwnProperty('actionData')) {
|
if (watcher.hasOwnProperty('actionData')) {
|
||||||
const { entity, action, filter, actionData } = watcher;
|
const { entity, action, filter, actionData } = watcher;
|
||||||
const filter2 = typeof filter === 'function' ? await filter() : filter;
|
const filter2 = typeof filter === 'function' ? await filter() : (0, lodash_1.cloneDeep)(filter);
|
||||||
const data = typeof actionData === 'function' ? await actionData() : actionData; // 这里有个奇怪的编译错误,不理解 by Xc
|
const data = typeof actionData === 'function' ? await actionData() : (0, lodash_1.cloneDeep)(actionData); // 这里有个奇怪的编译错误,不理解 by Xc
|
||||||
const result = await store.operate(entity, {
|
const result = await store.operate(entity, {
|
||||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||||
action: action,
|
action: action,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict> extends Featu
|
||||||
private attrUpdateMatrix?;
|
private attrUpdateMatrix?;
|
||||||
private connector;
|
private connector;
|
||||||
private baseRelationAuth;
|
private baseRelationAuth;
|
||||||
|
private entityActionAuthDict;
|
||||||
constructor(storageSchema: StorageSchema<ED>, connector: Connector<ED, SyncContext<ED>>, frontendContextBuilder: (store: CacheStore<ED>) => SyncContext<ED>, checkers: Array<Checker<ED, keyof ED, SyncContext<ED>>>, localStorage: LocalStorage, common: CommonConfiguration<ED>);
|
constructor(storageSchema: StorageSchema<ED>, connector: Connector<ED, SyncContext<ED>>, frontendContextBuilder: (store: CacheStore<ED>) => SyncContext<ED>, checkers: Array<Checker<ED, keyof ED, SyncContext<ED>>>, localStorage: LocalStorage, common: CommonConfiguration<ED>);
|
||||||
/**
|
/**
|
||||||
* 处理cache中需要缓存的数据
|
* 处理cache中需要缓存的数据
|
||||||
|
|
@ -96,7 +97,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict> extends Featu
|
||||||
action: ED[T]['Action'];
|
action: ED[T]['Action'];
|
||||||
data?: ED[T]['Operation']['data'];
|
data?: ED[T]['Operation']['data'];
|
||||||
filter?: ED[T]['Operation']['filter'];
|
filter?: ED[T]['Operation']['filter'];
|
||||||
}, checkerTypes?: CheckerType[]): boolean | OakUserException<ED>;
|
}, checkerTypes?: CheckerType[]): boolean | { [A in ED[T]["Action"]]: boolean | OakUserException<ED>; }[ED[T]["Action"]];
|
||||||
redoOperation(opers: Array<{
|
redoOperation(opers: Array<{
|
||||||
entity: keyof ED;
|
entity: keyof ED;
|
||||||
operation: ED[keyof ED]['Operation'];
|
operation: ED[keyof ED]['Operation'];
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ class Cache extends Feature_1.Feature {
|
||||||
attrUpdateMatrix;
|
attrUpdateMatrix;
|
||||||
connector;
|
connector;
|
||||||
baseRelationAuth;
|
baseRelationAuth;
|
||||||
|
// 缓存在某一时间戳状态下,对某行进行某个action的判断结果
|
||||||
|
entityActionAuthDict;
|
||||||
constructor(storageSchema, connector, frontendContextBuilder, checkers, localStorage, common) {
|
constructor(storageSchema, connector, frontendContextBuilder, checkers, localStorage, common) {
|
||||||
super();
|
super();
|
||||||
this.syncEventsCallbacks = [];
|
this.syncEventsCallbacks = [];
|
||||||
|
|
@ -40,6 +42,7 @@ class Cache extends Feature_1.Feature {
|
||||||
// 现在这个init变成了异步行为,不知道有没有影响。by Xc 20231126
|
// 现在这个init变成了异步行为,不知道有没有影响。by Xc 20231126
|
||||||
this.initPromise = new Promise((resolve) => this.initSavedLogic(resolve));
|
this.initPromise = new Promise((resolve) => this.initSavedLogic(resolve));
|
||||||
this.buildEntityGraph();
|
this.buildEntityGraph();
|
||||||
|
this.entityActionAuthDict = {};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 处理cache中需要缓存的数据
|
* 处理cache中需要缓存的数据
|
||||||
|
|
@ -89,7 +92,7 @@ class Cache extends Feature_1.Feature {
|
||||||
this.refreshing++;
|
this.refreshing++;
|
||||||
const { result, opRecords, message } = await this.connector.callAspect(name, params, ignoreContext ? undefined : this.context || this.contextBuilder());
|
const { result, opRecords, message } = await this.connector.callAspect(name, params, ignoreContext ? undefined : this.context || this.contextBuilder());
|
||||||
callback && callback(result, opRecords);
|
callback && callback(result, opRecords);
|
||||||
if (opRecords) {
|
if (opRecords?.length) {
|
||||||
this.syncInner(opRecords);
|
this.syncInner(opRecords);
|
||||||
}
|
}
|
||||||
this.refreshing--;
|
this.refreshing--;
|
||||||
|
|
@ -260,15 +263,20 @@ class Cache extends Feature_1.Feature {
|
||||||
syncInner(records) {
|
syncInner(records) {
|
||||||
this.begin();
|
this.begin();
|
||||||
this.cacheStore.sync(records, this.context);
|
this.cacheStore.sync(records, this.context);
|
||||||
|
const ts2 = this.cacheStore.getLastUpdateTs();
|
||||||
|
// 同步以后,当前所有缓存的action结果都不成立了
|
||||||
|
this.entityActionAuthDict = {};
|
||||||
// 唤起同步注册的回调
|
// 唤起同步注册的回调
|
||||||
this.syncEventsCallbacks.map((ele) => ele(records));
|
this.syncEventsCallbacks.map((ele) => ele(records));
|
||||||
this.context.commit();
|
this.context.commit();
|
||||||
this.context = undefined;
|
this.context = undefined;
|
||||||
}
|
}
|
||||||
sync(records) {
|
sync(records) {
|
||||||
|
if (records.length) {
|
||||||
this.syncInner(records);
|
this.syncInner(records);
|
||||||
this.publish();
|
this.publish();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 前端缓存做operation只可能是测试权限,必然回滚
|
* 前端缓存做operation只可能是测试权限,必然回滚
|
||||||
* @param entity
|
* @param entity
|
||||||
|
|
@ -328,10 +336,24 @@ class Cache extends Feature_1.Feature {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
checkOperation(entity, operation, checkerTypes) {
|
checkOperation(entity, operation, checkerTypes) {
|
||||||
|
// 缓存同一id对同一action的判断,避免性能低下
|
||||||
|
const { action, data, filter } = operation;
|
||||||
|
let id;
|
||||||
|
let ts;
|
||||||
|
if (filter && !data && typeof filter.id === 'string') {
|
||||||
|
id = filter.id;
|
||||||
|
ts = this.cacheStore.getLastUpdateTs();
|
||||||
|
if (this.entityActionAuthDict[ts]?.[entity]?.[id]?.hasOwnProperty(action)) {
|
||||||
|
return this.entityActionAuthDict[ts][entity][id][action];
|
||||||
|
}
|
||||||
|
}
|
||||||
const rollback = this.begin(true);
|
const rollback = this.begin(true);
|
||||||
try {
|
try {
|
||||||
this.cacheStore.check(entity, operation, this.context, checkerTypes);
|
this.cacheStore.check(entity, operation, this.context, checkerTypes);
|
||||||
rollback && rollback();
|
rollback && rollback();
|
||||||
|
if (id && ts) {
|
||||||
|
(0, lodash_1.set)(this.entityActionAuthDict, `${ts}.${entity}.${id}.${action}`, true);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|
@ -339,6 +361,9 @@ class Cache extends Feature_1.Feature {
|
||||||
if (err instanceof Exception_1.OakRowUnexistedException) {
|
if (err instanceof Exception_1.OakRowUnexistedException) {
|
||||||
// 有外键缺失,尝试发一下请求
|
// 有外键缺失,尝试发一下请求
|
||||||
this.fetchRows(err.getRows());
|
this.fetchRows(err.getRows());
|
||||||
|
if (id && ts) {
|
||||||
|
(0, lodash_1.set)(this.entityActionAuthDict, `${ts}.${entity}.${id}.${action}`, false);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
@ -348,6 +373,9 @@ class Cache extends Feature_1.Feature {
|
||||||
/* if (!(err instanceof OakUserException)) {
|
/* if (!(err instanceof OakUserException)) {
|
||||||
throw err;
|
throw err;
|
||||||
} */
|
} */
|
||||||
|
if (id && ts) {
|
||||||
|
(0, lodash_1.set)(this.entityActionAuthDict, `${ts}.${entity}.${id}.${action}`, err);
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||||
const filter_1 = require("oak-domain/lib/store/filter");
|
const filter_1 = require("oak-domain/lib/store/filter");
|
||||||
const modi_1 = require("oak-domain/lib/store/modi");
|
const modi_1 = require("oak-domain/lib/store/modi");
|
||||||
const relation_1 = require("oak-domain/lib/store/relation");
|
const relation_1 = require("oak-domain/lib/store/relation");
|
||||||
|
const types_1 = require("oak-domain/lib/types");
|
||||||
const Feature_1 = require("../types/Feature");
|
const Feature_1 = require("../types/Feature");
|
||||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||||
exports.MODI_NEXT_PATH_SUFFIX = ':next';
|
exports.MODI_NEXT_PATH_SUFFIX = ':next';
|
||||||
|
|
@ -732,9 +733,14 @@ class ListNode extends EntityNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const id = item.id || (0, uuid_1.generateNewId)();
|
const id = item.id || (0, uuid_1.generateNewId)();
|
||||||
|
const now = Date.now();
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
data: Object.assign(item, { id }),
|
data: Object.assign(item, {
|
||||||
|
id,
|
||||||
|
[types_1.CreateAtAttribute]: now,
|
||||||
|
[types_1.UpdateAtAttribute]: now,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
@ -751,7 +757,9 @@ class ListNode extends EntityNode {
|
||||||
removeItemInner(id, lsn) {
|
removeItemInner(id, lsn) {
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {
|
||||||
|
[types_1.DeleteAtAttribute]: Date.now(),
|
||||||
|
},
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
|
@ -797,7 +805,9 @@ class ListNode extends EntityNode {
|
||||||
updateItemInner(lsn, data, id, action) {
|
updateItemInner(lsn, data, id, action) {
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: action || 'update',
|
action: action || 'update',
|
||||||
data,
|
data: Object.assign({}, data, {
|
||||||
|
[types_1.UpdateAtAttribute]: Date.now(),
|
||||||
|
}),
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
|
@ -1218,9 +1228,14 @@ class SingleNode extends EntityNode {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const now = Date.now();
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
data: Object.assign({}, data, { id }),
|
data: Object.assign({}, data, {
|
||||||
|
id,
|
||||||
|
[types_1.CreateAtAttribute]: now,
|
||||||
|
[types_1.UpdateAtAttribute]: now,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
this.refreshListChildren();
|
this.refreshListChildren();
|
||||||
this.setDirty();
|
this.setDirty();
|
||||||
|
|
@ -1241,7 +1256,9 @@ class SingleNode extends EntityNode {
|
||||||
undefinedAttrs.forEach((attr) => (0, lodash_1.unset)(data, attr));
|
undefinedAttrs.forEach((attr) => (0, lodash_1.unset)(data, attr));
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: action || 'update',
|
action: action || 'update',
|
||||||
data,
|
data: Object.assign({}, data, {
|
||||||
|
[types_1.UpdateAtAttribute]: Date.now(),
|
||||||
|
}),
|
||||||
filter: {
|
filter: {
|
||||||
id: this.getId(),
|
id: this.getId(),
|
||||||
}
|
}
|
||||||
|
|
@ -1272,7 +1289,9 @@ class SingleNode extends EntityNode {
|
||||||
(0, assert_1.assert)(id);
|
(0, assert_1.assert)(id);
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {
|
||||||
|
[types_1.DeleteAtAttribute]: Date.now(),
|
||||||
|
},
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ class SubScriber extends Feature_1.Feature {
|
||||||
});
|
});
|
||||||
if (!optionInited) {
|
if (!optionInited) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
socket.on('connect_error', async () => {
|
socket.on('connect_error', async (err) => {
|
||||||
count++;
|
count++;
|
||||||
if (count > 50) {
|
if (count > 50) {
|
||||||
// 可能socket地址改变了,刷新重连
|
// 可能socket地址改变了,刷新重连
|
||||||
|
|
@ -117,7 +117,7 @@ class SubScriber extends Feature_1.Feature {
|
||||||
}
|
}
|
||||||
socket.removeAllListeners();
|
socket.removeAllListeners();
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
this.socket = null;
|
this.url = undefined;
|
||||||
await this.connect();
|
await this.connect();
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -49,21 +49,21 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
|
||||||
redirectTo<T2_1 extends keyof ED>(options: {
|
redirectTo<T2_1 extends keyof ED>(options: {
|
||||||
url: string;
|
url: string;
|
||||||
} & OakNavigateToParameters<ED, T2_1>, state?: Record<string, any> | undefined, disableNamespace?: boolean | undefined): Promise<void>;
|
} & OakNavigateToParameters<ED, T2_1>, state?: Record<string, any> | undefined, disableNamespace?: boolean | undefined): Promise<void>;
|
||||||
addItem<T_1 extends keyof ED>(data: Omit<ED[T_1]["CreateSingle"]["data"], "id"> & {
|
addItem<T extends keyof ED>(data: Omit<ED[T]["CreateSingle"]["data"], "id"> & {
|
||||||
id?: string | undefined;
|
id?: string | undefined;
|
||||||
}, path?: string | undefined): string;
|
}, path?: string | undefined): string;
|
||||||
addItems<T_2 extends keyof ED>(data: (Omit<ED[T_2]["CreateSingle"]["data"], "id"> & {
|
addItems<T_1 extends keyof ED>(data: (Omit<ED[T_1]["CreateSingle"]["data"], "id"> & {
|
||||||
id?: string | undefined;
|
id?: string | undefined;
|
||||||
})[], path?: string | undefined): string[];
|
})[], path?: string | undefined): string[];
|
||||||
removeItem(id: string, path?: string | undefined): void;
|
removeItem(id: string, path?: string | undefined): void;
|
||||||
removeItems(ids: string[], path?: string | undefined): void;
|
removeItems(ids: string[], path?: string | undefined): void;
|
||||||
updateItem<T_3 extends keyof ED>(data: ED[T_3]["Update"]["data"], id: string, action?: ED[T_3]["Action"] | undefined, path?: string | undefined): void;
|
updateItem<T_2 extends keyof ED>(data: ED[T_2]["Update"]["data"], id: string, action?: ED[T_2]["Action"] | undefined, path?: string | undefined): void;
|
||||||
updateItems<T_4 extends keyof ED>(data: ED[T_4]["Update"]["data"], ids: string[], action?: ED[T_4]["Action"] | undefined, path?: string | undefined): void;
|
updateItems<T_3 extends keyof ED>(data: ED[T_3]["Update"]["data"], ids: string[], action?: ED[T_3]["Action"] | undefined, path?: string | undefined): void;
|
||||||
recoverItem(id: string, path?: string | undefined): void;
|
recoverItem(id: string, path?: string | undefined): void;
|
||||||
recoverItems(ids: string[], path?: string | undefined): void;
|
recoverItems(ids: string[], path?: string | undefined): void;
|
||||||
resetItem(id: string, path?: string | undefined): void;
|
resetItem(id: string, path?: string | undefined): void;
|
||||||
update<T_5 extends keyof ED>(data: ED[T_5]["Update"]["data"], action?: ED[T_5]["Action"] | undefined, path?: string | undefined): void;
|
update<T_4 extends keyof ED>(data: ED[T_4]["Update"]["data"], action?: ED[T_4]["Action"] | undefined, path?: string | undefined): void;
|
||||||
create<T_6 extends keyof ED>(data: Omit<ED[T_6]["CreateSingle"]["data"], "id">, path?: string | undefined): void;
|
create<T_5 extends keyof ED>(data: Omit<ED[T_5]["CreateSingle"]["data"], "id">, path?: string | undefined): void;
|
||||||
remove(path?: string | undefined): void;
|
remove(path?: string | undefined): void;
|
||||||
isCreation(path?: string | undefined): boolean;
|
isCreation(path?: string | undefined): boolean;
|
||||||
clean(lsn?: number | undefined, dontPublish?: true | undefined, path?: string | undefined): void;
|
clean(lsn?: number | undefined, dontPublish?: true | undefined, path?: string | undefined): void;
|
||||||
|
|
@ -75,9 +75,9 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
|
||||||
}[] | undefined): Promise<void>;
|
}[] | undefined): Promise<void>;
|
||||||
isDirty(path?: string | undefined): boolean;
|
isDirty(path?: string | undefined): boolean;
|
||||||
getFreshValue(path?: string | undefined): Partial<import("oak-domain/lib/types").GeneralEntityShape> | Partial<import("oak-domain/lib/types").GeneralEntityShape>[] | undefined;
|
getFreshValue(path?: string | undefined): Partial<import("oak-domain/lib/types").GeneralEntityShape> | Partial<import("oak-domain/lib/types").GeneralEntityShape>[] | undefined;
|
||||||
checkOperation<T2_2 extends keyof ED>(entity: T2_2, operation: Omit<ED[T2_2]["Operation"], "id">, checkerTypes?: (CheckerType | "relation")[] | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
checkOperation<T2_2 extends keyof ED>(entity: T2_2, operation: Omit<ED[T2_2]["Operation"], "id">, checkerTypes?: (CheckerType | "relation")[] | undefined): boolean | { [A in ED[T2_2]["Action"]]: boolean | import("oak-domain/lib/types").OakUserException<ED>; }[ED[T2_2]["Action"]];
|
||||||
tryExecute(path?: string | undefined, action?: string | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
tryExecute(path?: string | undefined, action?: string | undefined): boolean | { [A_1 in ED[keyof ED]["Action"]]: boolean | import("oak-domain/lib/types").OakUserException<ED>; }[ED[keyof ED]["Action"]];
|
||||||
getOperations<T_7 extends keyof ED>(path?: string | undefined): {
|
getOperations<T_6 extends keyof ED>(path?: string | undefined): {
|
||||||
entity: keyof ED;
|
entity: keyof ED;
|
||||||
operation: ED[keyof ED]["Operation"];
|
operation: ED[keyof ED]["Operation"];
|
||||||
}[] | undefined;
|
}[] | undefined;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { EntityDict, OperateOption, SelectOption, OpRecord, AspectWrapper, Check
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { AspectDict as CommonAspectDict } from 'oak-common-aspect';
|
import { AspectDict as CommonAspectDict } from 'oak-common-aspect';
|
||||||
import { Feature } from '../types/Feature';
|
import { Feature } from '../types/Feature';
|
||||||
import { merge, pull, intersection, omit, unset, union } from 'oak-domain/lib/utils/lodash';
|
import { set, pull, intersection, omit, unset, union } from 'oak-domain/lib/utils/lodash';
|
||||||
import { CacheStore } from '../cacheStore/CacheStore';
|
import { CacheStore } from '../cacheStore/CacheStore';
|
||||||
import { OakRowUnexistedException, OakRowInconsistencyException, OakException, OakUserException } from 'oak-domain/lib/types/Exception';
|
import { OakRowUnexistedException, OakRowInconsistencyException, OakException, OakUserException } from 'oak-domain/lib/types/Exception';
|
||||||
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
||||||
|
|
@ -50,6 +50,17 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
private connector: Connector<ED, SyncContext<ED>>;
|
private connector: Connector<ED, SyncContext<ED>>;
|
||||||
private baseRelationAuth: BaseRelationAuth<ED>;
|
private baseRelationAuth: BaseRelationAuth<ED>;
|
||||||
|
|
||||||
|
// 缓存在某一时间戳状态下,对某行进行某个action的判断结果
|
||||||
|
private entityActionAuthDict: {
|
||||||
|
[ts: number]: {
|
||||||
|
[T in keyof ED]?: {
|
||||||
|
[I: string]: {
|
||||||
|
[A in ED[T]['Action']]: boolean | OakUserException<ED>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
storageSchema: StorageSchema<ED>,
|
storageSchema: StorageSchema<ED>,
|
||||||
connector: Connector<ED, SyncContext<ED>>,
|
connector: Connector<ED, SyncContext<ED>>,
|
||||||
|
|
@ -81,6 +92,7 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.buildEntityGraph();
|
this.buildEntityGraph();
|
||||||
|
this.entityActionAuthDict = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -151,7 +163,7 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
this.refreshing++;
|
this.refreshing++;
|
||||||
const { result, opRecords, message } = await this.connector.callAspect(name as string, params, ignoreContext ? undefined : this.context || this.contextBuilder());
|
const { result, opRecords, message } = await this.connector.callAspect(name as string, params, ignoreContext ? undefined : this.context || this.contextBuilder());
|
||||||
callback && callback(result, opRecords);
|
callback && callback(result, opRecords);
|
||||||
if (opRecords) {
|
if (opRecords?.length) {
|
||||||
this.syncInner(opRecords);
|
this.syncInner(opRecords);
|
||||||
}
|
}
|
||||||
this.refreshing--;
|
this.refreshing--;
|
||||||
|
|
@ -370,6 +382,10 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
this.begin();
|
this.begin();
|
||||||
this.cacheStore!.sync(records, this.context!);
|
this.cacheStore!.sync(records, this.context!);
|
||||||
|
|
||||||
|
const ts2 = this.cacheStore.getLastUpdateTs();
|
||||||
|
|
||||||
|
// 同步以后,当前所有缓存的action结果都不成立了
|
||||||
|
this.entityActionAuthDict = {};
|
||||||
// 唤起同步注册的回调
|
// 唤起同步注册的回调
|
||||||
this.syncEventsCallbacks.map((ele) => ele(records));
|
this.syncEventsCallbacks.map((ele) => ele(records));
|
||||||
this.context!.commit();
|
this.context!.commit();
|
||||||
|
|
@ -377,9 +393,11 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
sync(records: OpRecord<ED>[]) {
|
sync(records: OpRecord<ED>[]) {
|
||||||
|
if (records.length) {
|
||||||
this.syncInner(records);
|
this.syncInner(records);
|
||||||
this.publish();
|
this.publish();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 前端缓存做operation只可能是测试权限,必然回滚
|
* 前端缓存做operation只可能是测试权限,必然回滚
|
||||||
|
|
@ -455,10 +473,25 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
},
|
},
|
||||||
checkerTypes?: CheckerType[]
|
checkerTypes?: CheckerType[]
|
||||||
) {
|
) {
|
||||||
|
// 缓存同一id对同一action的判断,避免性能低下
|
||||||
|
const { action, data, filter } = operation;
|
||||||
|
let id: string | undefined;
|
||||||
|
let ts: number | undefined;
|
||||||
|
if (filter && !data && typeof filter.id === 'string') {
|
||||||
|
id = filter.id;
|
||||||
|
ts = this.cacheStore.getLastUpdateTs();
|
||||||
|
if (this.entityActionAuthDict[ts]?.[entity]?.[id]?.hasOwnProperty(action)) {
|
||||||
|
return this.entityActionAuthDict[ts]![entity]![id]![action]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const rollback = this.begin(true);
|
const rollback = this.begin(true);
|
||||||
try {
|
try {
|
||||||
this.cacheStore!.check(entity, operation, this.context!, checkerTypes);
|
this.cacheStore!.check(entity, operation, this.context!, checkerTypes);
|
||||||
rollback && rollback();
|
rollback && rollback();
|
||||||
|
if (id && ts) {
|
||||||
|
set(this.entityActionAuthDict, `${ts}.${entity as string}.${id}.${action}`, true);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
|
@ -466,6 +499,9 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
if (err instanceof OakRowUnexistedException) {
|
if (err instanceof OakRowUnexistedException) {
|
||||||
// 有外键缺失,尝试发一下请求
|
// 有外键缺失,尝试发一下请求
|
||||||
this.fetchRows(err.getRows());
|
this.fetchRows(err.getRows());
|
||||||
|
if (id && ts) {
|
||||||
|
set(this.entityActionAuthDict, `${ts}.${entity as string}.${id}.${action}`, false);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
@ -475,6 +511,9 @@ export class Cache<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||||
/* if (!(err instanceof OakUserException)) {
|
/* if (!(err instanceof OakUserException)) {
|
||||||
throw err;
|
throw err;
|
||||||
} */
|
} */
|
||||||
|
if (id && ts) {
|
||||||
|
set(this.entityActionAuthDict, `${ts}.${entity as string}.${id}.${action}`, err);
|
||||||
|
}
|
||||||
return err as OakUserException<ED>;
|
return err as OakUserException<ED>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { cloneDeep, difference, unset, merge, uniq, omit } from "oak-domain/lib/
|
||||||
import { combineFilters, getRelevantIds } from "oak-domain/lib/store/filter";
|
import { combineFilters, getRelevantIds } from "oak-domain/lib/store/filter";
|
||||||
import { createOperationsFromModies } from 'oak-domain/lib/store/modi';
|
import { createOperationsFromModies } from 'oak-domain/lib/store/modi';
|
||||||
import { judgeRelation } from "oak-domain/lib/store/relation";
|
import { judgeRelation } from "oak-domain/lib/store/relation";
|
||||||
import { EntityDict, StorageSchema, OpRecord, CreateOpResult, RemoveOpResult, AspectWrapper, AuthDefDict, CascadeRelationItem, CascadeActionItem, UpdateOpResult } from "oak-domain/lib/types";
|
import { EntityDict, StorageSchema, OpRecord, CreateOpResult, RemoveOpResult, AspectWrapper, AuthDefDict, CascadeRelationItem, CascadeActionItem, UpdateOpResult, CreateAtAttribute, UpdateAtAttribute, DeleteAtAttribute } from "oak-domain/lib/types";
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { AspectDict as CommonAspectDict } from 'oak-common-aspect';
|
import { AspectDict as CommonAspectDict } from 'oak-common-aspect';
|
||||||
|
|
||||||
|
|
@ -900,9 +900,14 @@ class ListNode<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const id = item.id || generateNewId();
|
const id = item.id || generateNewId();
|
||||||
|
const now = Date.now();
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
data: Object.assign(item, { id }),
|
data: Object.assign(item, {
|
||||||
|
id,
|
||||||
|
[CreateAtAttribute]: now,
|
||||||
|
[UpdateAtAttribute]: now,
|
||||||
|
}) as ED[T]['CreateSingle']['data'],
|
||||||
} as Omit<ED[T]['CreateSingle'], "id">);
|
} as Omit<ED[T]['CreateSingle'], "id">);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
@ -924,7 +929,9 @@ class ListNode<
|
||||||
private removeItemInner(id: string, lsn: number) {
|
private removeItemInner(id: string, lsn: number) {
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {
|
||||||
|
[DeleteAtAttribute]: Date.now(),
|
||||||
|
},
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
|
@ -990,7 +997,9 @@ class ListNode<
|
||||||
) {
|
) {
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: action || 'update',
|
action: action || 'update',
|
||||||
data,
|
data: Object.assign({}, data, {
|
||||||
|
[UpdateAtAttribute]: Date.now(),
|
||||||
|
}),
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
|
@ -1504,9 +1513,15 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
data: Object.assign({}, data, { id }),
|
data: Object.assign({}, data, {
|
||||||
|
id,
|
||||||
|
[CreateAtAttribute]: now,
|
||||||
|
[UpdateAtAttribute]: now,
|
||||||
|
} as ED[T]['CreateSingle']['data']),
|
||||||
} as Omit<ED[T]['CreateSingle'], "id">);
|
} as Omit<ED[T]['CreateSingle'], "id">);
|
||||||
this.refreshListChildren();
|
this.refreshListChildren();
|
||||||
this.setDirty();
|
this.setDirty();
|
||||||
|
|
@ -1531,7 +1546,9 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
||||||
|
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: action || 'update',
|
action: action || 'update',
|
||||||
data,
|
data: Object.assign({}, data, {
|
||||||
|
[UpdateAtAttribute]: Date.now(),
|
||||||
|
}),
|
||||||
filter: {
|
filter: {
|
||||||
id: this.getId()!,
|
id: this.getId()!,
|
||||||
}
|
}
|
||||||
|
|
@ -1565,7 +1582,9 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
||||||
assert(id);
|
assert(id);
|
||||||
this.ulManager.push(lsn, {
|
this.ulManager.push(lsn, {
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {
|
||||||
|
[DeleteAtAttribute]: Date.now(),
|
||||||
|
},
|
||||||
filter: {
|
filter: {
|
||||||
id,
|
id,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue