diff --git a/packages-user/data-base/src/flag/saveLoad.test.ts b/packages-user/data-base/src/flag/saveLoad.test.ts index 13b6d3f..aaee451 100644 --- a/packages-user/data-base/src/flag/saveLoad.test.ts +++ b/packages-user/data-base/src/flag/saveLoad.test.ts @@ -64,15 +64,15 @@ describe('FlagSystem save and load round trips', () => { expect(system.occupied('extra')).toBe(false); }); - // 验证 loadState 会重建字段对象并保留恢复值 - it('rebuilds field objects on loadState', () => { + // 验证 loadState 在同实例上原地恢复字段值(#06-17-5) + it('keeps field objects on loadState', () => { const system = new FlagSystem(); const before = system.insertField('score', 7); const saved = system.saveState(); system.loadState(saved); - expect(system.getField('score')).not.toBe(before); + expect(system.getField('score')).toBe(before); expect(system.getFieldValue('score')).toBe(7); }); }); diff --git a/packages-user/data-base/src/flag/system.test.ts b/packages-user/data-base/src/flag/system.test.ts index 225ca86..3874d6e 100644 --- a/packages-user/data-base/src/flag/system.test.ts +++ b/packages-user/data-base/src/flag/system.test.ts @@ -169,3 +169,61 @@ describe('FlagSystem value accessors', () => { expect(system.getFieldValue('lives')).toBe(3); }); }); + +describe('FlagSystem same-reference load (#06-17-5)', () => { + // 验证同 key 字段跨读档为同一实例,且数值恢复到存档点 + it('keeps the same field instance and restores its value', () => { + const system = new modules.FlagSystem(); + const before = system.getOrInsert('score', 5); + before.set(7); + + const saved = system.saveState(); + before.set(99); + + system.loadState(saved); + + expect(system.getField('score')).toBe(before); + expect(before.get()).toBe(7); + }); + + // 验证读档覆盖后 getOrInsert 仍返回此前持有的同一实例 + it('reuses the held field instance through getOrInsert after load', () => { + const system = new modules.FlagSystem(); + const before = system.getOrInsert('count', 1); + + const saved = system.saveState(); + before.set(99); + system.loadState(saved); + + expect(system.getOrInsert('count', 42)).toBe(before); + expect(before.get()).toBe(1); + }); + + // 验证复杂字段值在原地复用实例上以存档克隆恢复 + it('restores structured field values on the same instance', () => { + const system = new modules.FlagSystem(); + const before = system.getOrInsert('nested', { list: [1, 2] }); + + const saved = system.saveState(); + before.set({ list: [9] }); + system.loadState(saved); + + expect(system.getField('nested')).toBe(before); + expect(before.get()).toEqual({ list: [1, 2] }); + }); + + // 验证存档中不存在的字段在读档后被删除(以存档为准) + it('deletes fields absent from the save', () => { + const system = new modules.FlagSystem(); + system.setFieldValue('score', 7); + const kept = system.getField('score'); + + const saved = system.saveState(); + system.setFieldValue('extra', 1); + + system.loadState(saved); + + expect(system.getField('score')).toBe(kept); + expect(system.occupied('extra')).toBe(false); + }); +}); diff --git a/packages-user/data-base/src/flag/system.ts b/packages-user/data-base/src/flag/system.ts index ed98767..0234d75 100644 --- a/packages-user/data-base/src/flag/system.ts +++ b/packages-user/data-base/src/flag/system.ts @@ -63,11 +63,26 @@ export class FlagSystem implements IFlagSystem { } loadState(state: IFlagSystemSave): void { - this.fieldMap.clear(); + // 按 key 复用现有字段原地写值,使外部持有的引用跨读档仍然有效 for (const [key, data] of state.fields) { - const field = new FlagCommonField(this, key, undefined); - field.fromStructured(data); - this.fieldMap.set(key, field); + const existing = this.fieldMap.get(key); + if (existing) { + existing.fromStructured(data); + } else { + const field = new FlagCommonField( + this, + key, + undefined + ); + field.fromStructured(data); + this.fieldMap.set(key, field); + } + } + // 以存档为准:存档中不存在的字段一律删除 + for (const key of this.fieldMap.keys()) { + if (!state.fields.has(key)) { + this.fieldMap.delete(key); + } } } } diff --git a/packages-user/data-base/src/flag/types.ts b/packages-user/data-base/src/flag/types.ts index 3071a80..fdcf166 100644 --- a/packages-user/data-base/src/flag/types.ts +++ b/packages-user/data-base/src/flag/types.ts @@ -1,6 +1,6 @@ //#region 字段 -import { ISaveableContent } from '@user/data-common'; +import { ISaveableContent, SaveCompression } from '@user/data-common'; export interface IFlagCommonField { /** 此字段所处的 Flag 系统 */ @@ -133,6 +133,15 @@ export interface IFlagSystem extends ISaveableContent { * @param defaultValue 字段默认值 */ getFieldValueDefaults(field: PropertyKey, defaultValue: T): T; + + /** + * 读取 Flag 系统存档。存档中已存在的字段会复用原实例并在其实例上原地写值, + * 因此外部通过 {@link getField} / {@link getOrInsert} 持有的字段引用跨读档仍然有效; + * 存档中不存在的字段一律删除,即读档结果以存档为准。 + * @param state Flag 系统存档 + * @param compression 压缩级别 + */ + loadState(state: IFlagSystemSave, compression: SaveCompression): void; } //#endregion