摄影棚把选好的照片交给后期人员时,麻烦往往出现在“已经发过一份”之后。第一份交付清单记录P001白花原图4000×3000,包含三个点和一个矩形区域,意见已经整理;P002紫花缺少原图副本,P003粉花仍有待复核意见,因此这两张保留在工作区。过了一段时间,P002的副本补齐,P003的意见也得到处理。若此时直接覆盖第一份交付清单,接收方看到的照片数量和批注修订便与先前版本不同,无法解释哪些信息是后来新增的。

前面的七篇已经完成系统相册选择、应用内工作副本、原图像素坐标、缩放平移逆变换、撤销重做、意见关联和逐资产交付核查。本篇为摄影选片系列收尾:从冻结快照生成独立的交付版本,比较两个版本的资产变化,形成具有可追溯修订的清单文件,再提供归档预览。设计的重点不是把当前所有照片统一打包,而是确保每次交付都有明确的资产身份、来源修订、批注集合和差异说明。

摄影作品交付版本与清单差异概览

交付版本不是活动工作区的别名

活动工作区的PhotoEntry允许重新选图、调整排序和修改批注;交付清单描述的是某一刻已经满足条件的资产快照。P001的源资产ID保持P001,但一个版本中的批注修订号可能为7,另一个版本则可能为8。若把这两个版本指向同一个可变对象,后来的编辑就会改变过去的交付记录。工程继续沿用DeliveryValidator.ets和FreezeManifestService.ets,新增model/DeliveryRevision.ets、service/ManifestDiff.ets、service/ManifestWriter.ets及pages/DeliveryArchivePage.ets。

每条交付资产保存稳定assetId、本地副本的相对名称、源图宽高、图像修订号、批注修订号、意见修订号及最终意见状态。清单本身有独立的版本序号、冻结时间和来源会话标识。这里保留的是后期交付必需的元数据,原图URI不应直接显示在普通清单上;清单中的文件名也不应根据列表当前行号生成,因为重新排序可能造成接收方对错文件。

export interface DeliveryItem {
  assetId: string;
  copyName: string;
  width: number;
  height: number;
  imageRevision: number;
  annotationRevision: number;
  noteRevision: number;
  annotationIds: string[];
}
export interface DeliveryManifest {
  manifestRevision: number;
  sourceSession: string;
  createdAt: number;
  items: DeliveryItem[];
}
export function normalizeItem(item: DeliveryItem): DeliveryItem {
  const ids = Array.from(new Set<string>(item.annotationIds));
  ids.sort((a: string, b: string) => a.localeCompare(b));
  return { ...item, annotationIds: ids };
}

冻结前需要再次检查源修订

第七篇已经把P001判定为可交付,把P002和P003列为待处理。这里不把“在上次页面中显示可交付”直接理解为“现在依然可交付”。冻结之前,应重新读取每个候选资产的副本索引与意见修订,并和用户预览时的快照逐项比较。如果某张照片的标记被删除,或者意见被重新打开,冻结动作应返回具体的版本差异,不应该用旧缓存继续生成交付文件。

同一个会话可以拥有多个交付修订,但每个修订必须有唯一的资产ID集合。确认冻结时先复制资产对象,排序批注ID,再按稳定资产ID排序。源图宽高、工作副本名称、修订号都是清单中的必需字段;有缺失的字段要指出是哪一项,而不是悄悄填零。P001的三个点与矩形应只引用明确存在的目标标记,原图像素坐标应继续保存在批注数据里,不受交付预览采用的手机或平板尺寸影响。

export function freezeDelivery(items: DeliveryItem[], previousRevision: number,
  session: string, now: number): DeliveryManifest {
  const keys = new Set<string>();
  const frozen: DeliveryItem[] = items.map((entry: DeliveryItem) => {
    if (entry.assetId.length === 0 || entry.copyName.length === 0) {
      throw new Error('资产身份或副本名称缺失');
    }
    if (keys.has(entry.assetId)) throw new Error('重复资产:' + entry.assetId);
    if (entry.width <= 0 || entry.height <= 0) throw new Error('原图尺寸异常');
    keys.add(entry.assetId);
    return normalizeItem(entry);
  });
  frozen.sort((a: DeliveryItem, b: DeliveryItem) => a.assetId.localeCompare(b.assetId));
  return { manifestRevision: previousRevision + 1, sourceSession: session,
    createdAt: now, items: frozen };
}

归档不是发布成功的标记,而是“这份内容已经按某个版本固定下来”。如果用户在冻结之后再次编辑P001的批注,活动工作区继续变化,但旧交付修订仍然列出原来的批注版本。新的交付需要再走一次逐资产核查,生成新的清单修订。可以允许同一资产在多个版本中出现,却不应该把旧版文件原地替换成新版。由此接收方可以明确知道自己拿到的是哪一个版本,哪些照片首次出现,以及哪些批注发生过修改。

DevEco Studio中的交付版本模型、差异算法与清单预览

版本差异必须基于稳定身份比较

比较两个清单时不能把数组下标当作资产身份。假设第一份清单只有P001,第二份包含P001与P002,如果后期人员把P002排到列表首位,按行号比较会误报P001被删除并新增另一张照片。正确的做法是以assetId建立字典,再分别找出新增、移除和修订变化。对于同一资产,图像修订、批注修订与意见修订独立比较;仅排序变化不构成内容改变,批注ID列表则先规范排序再进行比较。

差异结果也应区分“新增资产”和“同一资产更新”。P002补齐副本后首次进入清单,属于新增;P001的批注修订从7变成8,属于更新;P003尚未通过意见核查时不能因为出现在工作区就进入交付。对于每条更新,可以记录修订号前后变化,具体批注文本仍通过授权的详情页查看。这样既能让交接人员快速浏览变更,又避免在报告中重复散布不必要的原图路径和意见全文。

export type DeltaKind = 'ADDED' | 'REMOVED' | 'MODIFIED';
export interface DeliveryDelta { assetId: string; kind: DeltaKind; }
export function compareDelivery(oldItems: DeliveryItem[], newItems: DeliveryItem[]): DeliveryDelta[] {
  const oldMap = new Map<string, DeliveryItem>();
  const newMap = new Map<string, DeliveryItem>();
  oldItems.forEach((item: DeliveryItem) => oldMap.set(item.assetId, item));
  newItems.forEach((item: DeliveryItem) => newMap.set(item.assetId, item));
  const result: DeliveryDelta[] = [];
  newMap.forEach((item: DeliveryItem, id: string) => {
    const before = oldMap.get(id);
    if (!before) { result.push({ assetId: id, kind: 'ADDED' }); return; }
    if (JSON.stringify(normalizeItem(before)) !== JSON.stringify(normalizeItem(item))) {
      result.push({ assetId: id, kind: 'MODIFIED' });
    }
  });
  oldMap.forEach((_item: DeliveryItem, id: string) => {
    if (!newMap.has(id)) result.push({ assetId: id, kind: 'REMOVED' });
  });
  return result.sort((a: DeliveryDelta, b: DeliveryDelta) => a.assetId.localeCompare(b.assetId));
}

清单文件写入应用空间并保留版本

在工程中可以先把清单序列化为JSON文件,放在应用的filesDir下,再由独立的用户触发流程完成实际交付。JSON内容至少包含清单版本、来源会话、冻结时间和资产数组;如果原图和批注资料需要一同交付,应在创建交付集合时明确文件对应关系,不能只凭一个短文件名假定接收方拥有全部素材。清单文件应采用唯一的修订文件名;发生写入异常时,不更新界面上的“当前归档版本”,保留此前版本以供继续查看。

Core File Kit提供应用文件的打开、写入和关闭操作。为了避免旧文件尾部残留,使用新的修订文件名而不是直接覆盖旧文件;对于中途断电、空间不足等场景,还应在更高层引入临时文件与完成标记策略。下方的文件写入函数承担清单JSON本身,图片副本的整理和授权导出应是明确的后续步骤。应用沙箱路径适合保存工作版本,但接收方获取文件仍需要经过符合场景的授权和用户操作。

import { fileIo } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';

export function saveManifest(context: common.UIAbilityContext,
  manifest: DeliveryManifest): string {
  const path = context.filesDir + '/delivery_' + manifest.manifestRevision + '.json';
  const file = fileIo.openSync(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
  try {
    const content = JSON.stringify(manifest);
    fileIo.writeSync(file.fd, content);
  } finally {
    fileIo.closeSync(file);
  }
  return path;
}

持久化还需要定义失败处理的先后顺序。若原图副本丢失,应先标记对应资产不可交付,再决定是否生成一个只包含其余合格资产的新版本;若JSON写入过程中报错,不能因为此前已有旧文件就把当前版本标为归档完成。若用户取消交付,已经冻结的历史修订可以继续保留,活动会话的选择与批注也无需自动清空。这样取消、失败与成功三种结果都不会改变业务上已经确认的旧档案。

ArkUI列表只负责呈现版本与差异

交付归档页面左侧可展示版本序号及冻结时间,右侧显示该版本的资产清单;窄屏页面通过单栏跳转进入详情。无论界面形态怎样变化,选中的都是清单版本加资产ID,而不是列表位置。List与ForEach使用稳定键,支持按“新增、更新、移除、无变化”筛选。若用户从版本7切到版本8,页面应先清除旧版本的悬浮详情,再用版本8的资产集合验证焦点是否存在;选择P001时可以显示其原图尺寸和批注修订,而不在日志中暴露完整图片URI。

如果某个版本只有P001,页面应明确显示“交付资产1项、待补齐2项”,不能把P002、P003误放在已交付列表里。第二次交付若P002已经通过核查,差异页面应该显示“新增P002”,而不是把工作区所有照片统一标为已交付。这样摄影师和后期人员从版本差异中能够了解进展,而不会混淆“进入会话”“完成批注”和“进入交付清单”的阶段。页面筛选偏好可另行存储,不参与冻结文件的内容计算。

用确定的数据核查归档逻辑

准备两份可比较的清单:第一份包含P001,图像修订2、批注修订7、意见修订5;第二份包含P001和已补齐的P002,P001批注修订升至8。差异算法应得出“P001更新、P002新增”,不应受到输入数组排序变化的影响。进一步只调整P001的显示顺序,检查差异是否依然为空;删除P001的一个标记,检查新的批注修订是否变化;把P003的待复核意见直接改成完成但不更新对应修订,应由冻结前的核查拒绝不一致的内容。

还要覆盖异常分支:重复assetId阻止冻结,尺寸字段缺失时指出对应资产,保存文件失败时保持旧归档的可读性,加载新版本超时后仍能返回旧版本详情。快速切换归档版本时,利用查询代次忽略晚到结果;应用转入后台时保留编辑状态但不自动发布新的交付。HiLog记录版本号、合格资产数、差异分类计数、写入阶段和异常类型即可,避免输出外部图库URI、原始批注意见与交付者的个人信息。

摄影交付版本清单、差异分类和归档状态的手机页面

至此,摄影选片系列构建了从相册选片到工作副本、原图坐标批注、手势还原、意见追踪、交付核查和版本归档的连续链路。最重要的工程原则是让稳定资产身份贯穿整个过程,同时让每一次冻结都保存自己的修订信息。照片是否完成交付不由当前缩略图或列表勾选决定,而由通过核查并固定下来的版本清单决定。这样即使后期继续编辑工作区,旧清单依然能够被准确对照,新版本也能说明具体增加或更改了什么。

参考资料:Core File Kit应用文件访问、ArkUI列表开发、ArkTS语言概述。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐