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