class LazyLoader {
constructor(dataManager) {
this.dataManager = dataManager;
this.cache = new Map();
this.entityTypeMap = {
bancai: 'bancais',
dingdan: 'dingdans',
mupi: 'mupis',
chanpin: 'chanpins',
kucun: 'kucuns',
dingdan_bancai: 'dingdan_bancais',
chanpin_zujian: 'chanpin_zujians',
zujian: 'zujians',
caizhi: 'caizhis',
dingdan_chanpin: 'dingdan_chanpins',
user: 'users',
jinhuo: 'jinhuos'
};
this.proxies = new WeakMap();
}
createProxy(entity, entityType) {
// 1. 优先检查缓存
if (this.proxies.has(entity)) {
return this.proxies.get(entity);
}
const cacheKey = `${entityType}_${entity.id}`;
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
// 2. 代理检测简化
const handler = {
get: (target, prop, receiver) => {
// 特殊属性处理
if (prop === 'id') return target.id;
const value = Reflect.get(target, prop, receiver);
// 基本类型直接返回
if (typeof value !== 'object' || value === null) {
return value;
}
// 处理数组引用
if (Array.isArray(value)) {
const refType = this.getReferenceType(prop);
return refType ? this.loadReferences(value, refType) : value;
}
// 处理对象引用
const refType = this.getReferenceType(prop);
if (refType) {
//console.log(refType)
return this.loadReference(value, refType);
}
// 检查是否已经代理过这个对象
if (this.proxies.has(value)) {
return this.proxies.get(value);
}
return value;
}
};
const proxy = new Proxy(entity, handler);
// 3. 创建后立即缓存
this.proxies.set(entity, proxy);
this.cache.set(cacheKey, proxy);
return proxy;
}
getEntityTypeFromRef(prop) {
const baseProp = prop.replace(/\d/g, '');
return baseProp in this.entityTypeMap ?
this.entityTypeMap[baseProp] :
`${baseProp}s`;
}
getReferenceType(prop) {
try {
const baseProp = prop.replace(/\d/g, '');
if (this.entityTypeMap[baseProp]) return this.entityTypeMap[baseProp];
const pluralProp = `${baseProp}s`;
if (this.dataManager._rawData[pluralProp]) return pluralProp;
} catch (error) {
console.log(prop+" ---- "+error)
}
return null;
}
loadReference(ref, refType) {
if (!ref?.id) { return ref;}
const cacheKey = `${refType}_${ref.id}`;
// 4. 统一使用缓存机制
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
const entities = this.dataManager._rawData[refType] || [];
const entity = entities.find(e => e.id === ref.id);
if (!entity) {
console.warn(`Entity not found: ${refType} with id ${ref.id}`);
return ref;
}
// 5. 使用createProxy确保代理一致性
const prosty= this.createProxy(entity, refType);
return prosty
}
loadReferences(refs, refType) {
return refs.map(ref => this.loadReference(ref, refType));
}
resolveReferences(entity) {
for (const attr in entity) {
const refType = this.getReferenceType(attr);
if (!refType) continue;
if (Array.isArray(entity[attr])) {
entity[attr] = entity[attr].map(item =>
this.dataManager._rawData[refType]?.find(e => e.id === item.id) || item
);
} else if (entity[attr]?.id) {
entity[attr] =
this.dataManager._rawData[refType]?.find(e => e.id === entity[attr].id) ||
entity[attr];
}
}
return entity;
}
clearCache() {
this.cache.clear();
}
}
class MiniProgramDataManager {
constructor(baseUrl = '') {
this.baseUrl = baseUrl;
this.debug = true;
this.networkAvailable = false;
this.isSyncing = false;
this.lastSync = null;
this.syncInterval = 5 * 60 * 1000;
this.storageKey = 'miniProgramData';
this._rawData = this.createEmptyData();
this.lazyLoader = new LazyLoader(this);
this.callbacks = {
all: [],
bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [],
chanpin_zujians: [], dingdan_bancais: [], zujians: [], caizhis: [],
dingdan_chanpins: [], users: [], jinhuos: []
};
this.initNetwork();
this.loadDataFromStorage();
this.startAutoSync();
}
createEmptyData() {
return {
bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [],
dingdan_bancais: [], chanpin_zujians: [], zujians: [], caizhis: [],
dingdan_chanpins: [], users: [], jinhuos: [],
_lastModified: null, _lastSync: null
};
}
get data() {
const handler = {
get: (target, prop) => {
if (prop.startsWith('_')) return target[prop];
if (Array.isArray(target[prop])) {
return target[prop].map(item =>
this.lazyLoader.createProxy(item, prop.replace(/s$/, ''))
);
}
return target[prop];
},
set: (target, prop, value) => {
target[prop] = value;
return true;
}
};
return new Proxy(this._rawData, handler);
}
async initialize() {
try {
await this.syncData();
return true;
} catch (error) {
if (this._rawData._lastSync) return true;
throw error;
}
}
startAutoSync() {
this.autoSyncTimer = setInterval(() => {
!this.isSyncing && this.syncData();
}, this.syncInterval);
}
stopAutoSync() {
clearInterval(this.autoSyncTimer);
}
async initNetwork() {
try {
const { networkType } = await wx.getNetworkType();
this.networkAvailable = networkType !== 'none';
} catch {
this.networkAvailable = false;
}
}
async syncData() {
if (this.isSyncing) return;
this.isSyncing = true;
try {
const since = this._rawData._lastSync;
await this.fetchAll(since);
this.lazyLoader.clearCache();
this.saveDataToStorage();
this.triggerCallbacks('refresh', 'all', this.data);
} catch (error) {
console.error('Sync failed:', error);
this.triggerCallbacks('sync_error', 'all', { error });
if (!this._rawData._lastSync) throw error;
} finally {
this.isSyncing = false;
}
}
async fetchAll(since) {
try {
const params = since ? { since } : {};
const resolvedData = this.baseUrl
? await this.request('/app/all', 'GET', params)
: this.createEmptyData();
Object.keys(this._rawData).forEach(key => {
if (key.startsWith('_') || !resolvedData[key]) return;
if (since) {
resolvedData[key].forEach(newItem => {
const index = this._rawData[key].findIndex(item => item.id === newItem.id);
index >= 0
? this._rawData[key][index] = newItem
: this._rawData[key].push(newItem);
});
} else {
this._rawData[key] = resolvedData[key];
}
});
this._rawData._lastSync = new Date().toISOString();
this.saveDataToStorage();
return true;
} catch (error) {
console.error('Fetch error:', error);
this.triggerCallbacks('fetch_error', 'all', { error });
throw error;
}
}
async request(url, method, data, retryCount = 3) {
return new Promise((resolve, reject) => {
const fullUrl = `${this.baseUrl}${url}`;
const requestTask = () => {
wx.request({
url: fullUrl,
method,
data,
header: { 'Content-Type': 'application/json' },
success: (res) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data.data);
} else {
const err = new Error(res.data?.message || 'API error');
retryCount > 1
? setTimeout(requestTask, 1000, retryCount - 1)
: reject(err);
}
},
fail: (err) => {
retryCount > 1
? setTimeout(requestTask, 1000, retryCount - 1)
: reject(new Error(`Network error: ${err.errMsg}`));
}
});
};
requestTask();
});
}
registerCallback(entity, callback) {
this.callbacks[entity]?.push(callback) || this.callbacks.all.push(callback);
}
unregisterCallback(entity, callback) {
const arr = this.callbacks[entity] || this.callbacks.all;
const index = arr.indexOf(callback);
if (index !== -1) arr.splice(index, 1);
}
triggerCallbacks(operation, entity, data) {
this.callbacks.all.forEach(cb => cb(operation, entity, data));
this.callbacks[entity]?.forEach(cb => cb(operation, data));
}
async crudOperation(operation, entity, data) {
try {
const result = await this.request(`/app/${operation}/${entity}`, 'POST', data);
this.updateLocalData(operation, entity, result || data);
this.triggerCallbacks(operation, entity, result || data);
return result;
} catch (error) {
this.triggerCallbacks(`${operation}_error`, entity, { data, error });
throw error;
}
}
updateLocalData(operation, entity, data) {
const key = `${entity}s`;
const collection = this._rawData[key] || [];
switch (operation) {
case 'add':
collection.push(data);
break;
case 'update':
const index = collection.findIndex(item => item.id === data.id);
index >= 0
? collection[index] = data
: collection.push(data);
break;
case 'delete':
const deleteIndex = collection.findIndex(item => item.id === data.id);
if (deleteIndex >= 0) collection.splice(deleteIndex, 1);
break;
}
this._rawData._lastModified = new Date().toISOString();
this.lazyLoader.clearCache();
this.saveDataToStorage();
}
loadDataFromStorage() {
try {
const storedData = wx.getStorageSync(this.storageKey);
if (storedData) this._rawData = storedData;
} catch (error) {
console.error('Storage load error:', error);
}
}
saveDataToStorage() {
try {
wx.setStorageSync(this.storageKey, this._rawData);
} catch (error) {
console.error('Storage save error:', error);
wx.showToast({ title: '数据保存失败', icon: 'none' });
}
}
async addEntity(entity, data) {
return this.crudOperation('add', entity, data);
}
async updateEntity(entity, data) {
return this.crudOperation('update', entity, data);
}
async deleteEntity(entity, id) {
return this.crudOperation('delete', entity, { id });
}
async transactionalOperation(endpoint, data) {
try {
await this.request(`/app/Transactional/${endpoint}`, 'POST', data);
await this.syncData();
return true;
} catch (error) {
this.triggerCallbacks('transaction_error', endpoint, { data, error });
throw error;
}
}
}
module.exports = MiniProgramDataManager;
@startuml
' 基础类与接口
abstract class EntityBasis {
+ Integer id
+ Date lastUpdated
+ Boolean deleted
+ Date deletedAt
+ getId()
+ setId(Integer)
}
interface EntityBasisId {
+ getId()
+ setId(Integer)
}
abstract class SimpleEntity {
+ Integer id
+ String name
}
' 实体类定义
class Mupi {
+ Boolean you
+ String name
+ List<Bancai> bancaisForMupi1
+ List<Bancai> bancaisForMupi2
}
class User {
+ String name
+ String andy
+ String pass
+ int role
+ int incumbency
}
class Chanpin_zujian {
+ Double one_howmany
+ Double zujianshu
}
class WechatUser {
+ String openid
+ String sessionKey
+ String nickname
+ String avatarUrl
+ String phoneNumber
+ Long userId
+ Date createTime
+ Integer status
}
class Kucun {
+ Integer shuliang
}
class Dingdan_chanpin {
+ Integer shuliang
}
class Information {
+ Integer Status
+ String text
+ Object data
}
class Zujian {
+ String name
+ List<Chanpin_zujian> chanping_zujian
}
class Dingdan_bancai {
+ Integer shuliang
+ Integer currentUserId
}
class Dingdan {
+ String number
+ Date xiadan
+ Date jiaohuo
+ List<Dingdan_chanpin> dingdan_chanpin
}
class Jinhuo {
+ Integer shuliang
+ Date date
+ String text
+ Integer theTypeOfOperation
}
class Caizhi {
+ String name
+ List<Bancai> bancai
}
class Bancai {
+ Double houdu
}
' 继承关系
EntityBasis --|> EntityBasisId
SimpleEntity --|> EntityBasisId
Mupi --|> EntityBasis
User --|> EntityBasis
Chanpin_zujian --|> EntityBasis
WechatUser --|> EntityBasis
Kucun --|> EntityBasis
Dingdan_chanpin --|> EntityBasis
Zujian --|> EntityBasis
Dingdan_bancai --|> EntityBasis
Dingdan --|> EntityBasis
Jinhuo --|> EntityBasis
Caizhi --|> EntityBasis
Bancai --|> EntityBasis
' 关联关系
' 一对一
Bancai -- Kucun : 1:1 (kucun)
' 一对多/多对一
Chanpin "1" -- "*" Chanpin_zujian : 包含 (chanpin_zujian)
Zujian "1" -- "*" Chanpin_zujian : 关联 (chanping_zujian)
Bancai "1" -- "*" Chanpin_zujian : 被使用 (bancai)
Dingdan "1" -- "*" Dingdan_chanpin : 包含 (dingdan_chanpin)
Chanpin "1" -- "*" Dingdan_chanpin : 被订购 (chanpin)
Dingdan "1" -- "*" Dingdan_bancai : 包含 (dingdan)
Chanpin "1" -- "*" Dingdan_bancai : 关联 (chanpin)
Zujian "1" -- "*" Dingdan_bancai : 关联 (zujian)
Bancai "1" -- "*" Dingdan_bancai : 被订购 (bancai)
User "1" -- "*" Jinhuo : 操作 (user)
Dingdan_bancai "1" -- "*" Jinhuo : 关联 (dingdan_bancai)
' 单向关联
WechatUser -- User : 绑定 (userId)
@enduml---------------------------------------pin_zujian ---- RangeError: Maximum call stack size exceeded
2MiniProgramDataManager.js? [sm]:98 dingdan_chanpin ---- RangeError: Maximum call stack size exceeded
MiniProgramDataManager.js? [sm]:98 chanpin_zujian ---- RangeError: Maximum call stack size exceeded
2MiniProgramDataManager.js? [sm]:98 dingdan_chanpin ---- RangeError: Maximum call stack size exceeded
MiniProgramDataManager.js? [sm]:98 chanpin_zujian ---- RangeError: Maximum call stack size exceeded
2MiniProgramDataManager.js? [sm]:98 dingdan_chanpin ---- RangeError: Maximum call stack size exceeded
MiniProgramDataManager.js? [sm]:98 chanpin_zujian ---- RangeError: Maximum call stack size exceeded
MiniProgramDataManager.js? [sm]:98 dingdan_chanpin ---- RangeError: Maximum call stack size exceeded
MiniProgramDataManager.js? [sm]:98 chanpin_zujian ---- RangeError: Maximum call stack size exceeded
MiniProgramDataManager.js? [sm]:98 chanping_zujian ---- RangeError: Maximum call stack size exceeded
2MiniProgramDataManager.js? [sm]:98 dingdan_chanpin ---- RangeError: Maximum call stack size exceeded
MiniProgramDataManager.js? [sm]:98 chanpin_zujian ---- RangeError: Maximum call stack size exceeded--------------- getReferenceType(prop) {
try {
const baseProp = prop.replace(/\d/g, '');
if (this.entityTypeMap[baseProp]) return this.entityTypeMap[baseProp];
const pluralProp = `${baseProp}s`;
if (this.dataManager._rawData[pluralProp]) return pluralProp;
} catch (error) {
console.log(prop+" ---- "+error)
}一直触发
最新发布