欢迎光临
我们一直在努力

固定资产Excel批量导入怎么防重复?资产编码、重复检查和错误回执

同一份资产台账,第一次导入后发现部门填错,修改Excel再上传一次;如果系统只会“插入一条资产”,第二次就可能多出一张卡。真正难的不是判断两个名字像不像,而是明确:哪种情况应当拒绝、哪种情况应当更新、哪种情况必须人工确认。

以“公司 + 资产编码”为业务唯一键。导入文件有120行,其中6行与系统已有资产编码相同:4行内容完全一致、1行原值不同、1行落在另一家公司。它们不能都当成“重复”。

本文以 Spring Boot + MySQL 8.x 的小型管理系统为教学边界。表名、字段和数值均为示例;上线前仍应依据资产制度、财务口径、数据量和现有程序进行评审。

实现时建议把一次业务动作拆成四层:Controller只接收请求和返回结果;Service加载当前状态、执行权限与业务校验;Repository使用参数化SQL读写;数据库用主键、唯一键、非空和金额精度守住最后边界。前端校验可以减少误操作,但不能替代服务端规则。

本文的SQL用于展示约束和核对思路,不是可直接在生产库执行的完整脚本。执行DDL前应确认MySQL版本、存储引擎、表规模、磁盘余量、主从延迟和锁等待;执行查询前应补充公司、期间等范围并查看执行计划。代码示例省略了DTO、Mapper和统一异常处理,但不会省略幂等、权限、事务和审计这些决定数据是否可信的部分。

如果系统已经存在历史数据,任何“新增唯一键”“改变状态含义”或“补齐关联ID”的操作都要先做数据剖析。先统计空值、重复值和非法状态,再选择清洗、映射或人工确认;不能因为新程序要求字段非空,就用0或空字符串批量填满旧记录。

正式改造前还应保存三份基线:当前表结构的SHOW CREATE TABLE结果、目标范围的数量与金额汇总、能够代表正常和异常路径的固定样本。发布后使用同一口径复查,并记录程序版本、数据库迁移版本和执行批次。如果需要回退,先判断新版本是否已经写入旧程序无法识别的数据;代码回退并不等于业务数据自动回退。对于大表新增索引或约束,还要先在相近数据量的环境评估执行时间与锁影响,不能只根据空测试库的耗时安排生产窗口。 文中的状态名和错误码用于说明设计方法。真正落地时应集中定义状态枚举、合法迁移和错误码说明,并为关键状态变化编写自动化测试,避免不同接口各自解释同一个状态。批量接口还应限制单批数量、记录处理进度并提供明确的重复提交策略;统计和导出任务不能绕开同样的数据权限与状态规则。监控指标至少区分请求失败、业务拒绝和异步待处理,避免把合理拦截误报成系统故障。对于可重试任务,应记录下一次执行时间、重试次数和最后错误;超过上限转人工处理,不能无限循环,也不能把待处理悄悄显示成完成。所有后台修复动作同样要经过授权、幂等和审计,并保留修复前后的核对证据,方便后续回归测试和责任追溯。接口返回、数据库记录和审计事件中的业务编号应保持一致,便于跨层定位、复核和比较结果。

目录

  • 一、先定义重复,别先写去重SQL
  • 二、用数据库唯一约束守住最后一道门
  • 三、把“相同、冲突、可更新”分开返回
  • 四、导入批次要有内容指纹与行级来源
  • 五、并发提交时让写入结果可解释
  • 六、错误回执要能让业务人员修完再传
  • 七、用四组样本验收
  • 八、服务层实现不能只相信页面参数
  • 九、事务、并发和外部调用的边界
  • 十、失败结果要能指导下一步处理
  • 十一、上线前用SQL核对业务结果
  • 十二、可直接使用的技术验收表
  • 一、先定义重复,别先写去重SQL

    文件内重复、与正式台账重复、与历史导入批次重复,是三层不同问题。编码相同但公司不同未必重复;编码相同但原值不同也不应静默覆盖。

    在这里插入图片描述

    图1:重复不是一种情况,而是三层判断。

    二、用数据库唯一约束守住最后一道门

    页面预检只是体验,UNIQUE(company_id, asset_code)才是并发导入下的最终底线。对允许编码为空的规则必须另行设计,不能以空字符串凑唯一。

    在这里插入图片描述

    图2:写入前后,都要保留结果。

    三、把“相同、冲突、可更新”分开返回

    同一业务键、字段摘要相同可以标为ALREADY_IMPORTED;关键字段不同标为CONFLICT;只有经明确版本或审批规则允许的字段才进入UPDATE_CANDIDATE。

    在这里插入图片描述

    图3:一行导入记录的四种结果。

    四、导入批次要有内容指纹与行级来源

    保存上传文件哈希、模板版本、工作表和行号。文件哈希相同不是唯一判断依据,但可以阻止用户在网络超时后连续重复提交。

    在这里插入图片描述

    图4:错误回执应返回什么。

    CREATE TABLE asset_card_demo (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    company_id BIGINT NOT NULL,
    asset_code VARCHAR(64) NOT NULL,
    asset_name VARCHAR(128) NOT NULL,
    original_cost DECIMAL(18,2) NOT NULL,
    import_digest CHAR(64) NOT NULL,
    UNIQUE KEY uk_company_asset_code (company_id, asset_code)
    );

    SELECT company_id, asset_code, COUNT(*) AS cnt
    FROM asset_import_row_demo
    WHERE batch_id = :batchId AND status = 'READY'
    GROUP BY company_id, asset_code
    HAVING COUNT(*) > 1;

    五、并发提交时让写入结果可解释

    先读再写不是原子操作。写入处捕获唯一键冲突后,回查已有记录,按“相同已存在”或“字段冲突”输出结果,不把数据库异常原样给用户。

    在这里插入图片描述

    图5:防重复验收清单。

    六、错误回执要能让业务人员修完再传

    回执至少有来源行、资产编码、错误码、当前系统值、导入值和建议动作。不要只给“导入失败”,否则下一次还是会用同一份错误文件。

    七、用四组样本验收

    覆盖文件内重复、历史重复、跨公司同编码、并发两次提交;验收的目标是每个来源行都有唯一结果,而不是“数据库没有报错”。

    八、服务层实现不能只相信页面参数

    接口收到的公司、部门、状态和金额只能视为请求数据,不能直接当成已经校验的业务事实。服务层至少要重新加载当前记录、计算操作者范围、校验当前状态,并将业务结果写成可识别的返回对象。下面代码只展示关键边界,仓储方法、异常类型和审计方式需按项目实现。

    public ImportRowResult importOne(ImportRow row) {
    AssetKey key = new AssetKey(row.companyId(), row.assetCode().trim());
    String digest = digestOf(row.normalizedFields());
    AssetCard existing = assetRepository.findByKey(key);
    if (existing != null) {
    return digest.equals(existing.getImportDigest())
    ? ImportRowResult.alreadyImported(existing.getId())
    : ImportRowResult.conflict(existing.getId(), diff(existing, row));
    }
    try {
    return ImportRowResult.imported(assetRepository.insert(row, digest));
    } catch (DuplicateKeyException ex) {
    AssetCard concurrent = assetRepository.requireByKey(key);
    return digest.equals(concurrent.getImportDigest())
    ? ImportRowResult.alreadyImported(concurrent.getId())
    : ImportRowResult.conflict(concurrent.getId(), diff(concurrent, row));
    }
    }

    示例刻意没有让Controller拼接SQL,也没有让前端决定最终状态。这样做的价值是:批量任务、后台管理和普通页面调用同一服务时,仍遵循同一套规则。若方法会抛出受检异常,需要显式配置回滚规则;Spring默认回滚语义不应靠猜测。

    九、事务、并发和外部调用的边界

    每一行的正式资产与行结果应在同一事务中提交;批次可以分块执行,但不能先写资产、稍后再补成功标记。两个用户同时提交时,应用层预检都可能通过,因此仍要让数据库唯一约束裁决,并把唯一键异常转换成可读的行结果。

    事务解决的是一个数据库边界内的一致性,不会自动撤销已发送的短信、已上传的对象存储文件或外部财务系统请求。遇到跨系统动作,应保存可重试任务、业务幂等键和最后错误,并让操作人员看见“业务已完成、同步待处理”这类真实状态。长事务还会扩大锁持有时间,因此批量操作要按可恢复的数据块处理。

    十、失败结果要能指导下一步处理

    异常不应全部压成“操作失败”。至少区分输入错误、业务冲突、权限拒绝、并发冲突和外部依赖失败;前四类通常需要用户修改或确认,最后一类才适合自动重试。本文案例建议使用以下结果:

    场景结果码处理方式
    同文件出现两次 FILE_DUPLICATE 两行都标出并让用户确认保留哪一行
    系统已有且内容一致 ALREADY_IMPORTED 返回已有资产ID,不再插入
    系统已有但原值不同 FIELD_CONFLICT 列出差异,禁止静默覆盖
    并发写入撞唯一键 CONCURRENT_DUPLICATE 回查后转成已存在或冲突

    错误回执或接口响应可以显示业务编号和友好说明,详细堆栈只进入受控日志。返回结果还应带request_id,便于把用户看到的失败与服务端日志、批次明细及审计记录关联起来。

    十一、上线前用SQL核对业务结果

    页面数量只能用来快速观察,最终验收还要在明确组织、期间和业务状态的前提下执行只读查询。下面查询用于发现不一致记录,生产环境应先补充分区或公司条件,并确认执行计划,避免在大表上做无范围扫描。

    SELECT company_id, asset_code, COUNT(*) cnt
    FROM asset_card_demo
    GROUP BY company_id, asset_code HAVING COUNT(*) > 1;

    SELECT status, COUNT(*) rows_count
    FROM asset_import_row_demo WHERE batch_id=:batchId
    GROUP BY status;

    若查询返回异常,先保留样本和执行时间,再判断是历史脏数据、迁移遗漏还是程序缺陷。不要为了让验收SQL返回零行而直接改库;修复必须经过与正常业务相同的授权、留痕和复核。

    十二、可直接使用的技术验收表

    正式发布前固定一组小样本,记录输入、预期、实际结果和证据位置。修复后仍用同一组样本复测,才能知道变化来自代码,而不是换了一批更容易通过的数据。

    验收维度预期结果证据
    正常路径 业务结果、状态和关联记录符合预期 业务页面 + 数据库查询
    重复操作 第二次执行不产生重复主记录 唯一键 + 行结果/业务结果
    越权或非法状态 服务端拒绝,原数据不变化 低权限账号 + 更新前后对照
    执行中断 可识别已完成与未完成范围 批次状态 + 明细状态
    金额或数量 汇总与来源口径完全一致 固定样本 + SQL核对
    追溯 能回到操作者、单据、时间和请求 审计时间轴 + request_id

    验收完成后保存程序版本、数据库版本、样本编号和结论。只写“测试通过”无法回答测试了哪些边界,也无法在下次升级后进行回归比较。

    小结

    固定资产Excel批量导入怎么防重复,核心不是把页面操作做出来,而是让每一步都能说明输入、边界、结果和验证证据。先用小样本走通,再按实际数据量和业务制度扩大范围。

    参考资料

    • MySQL 8.4:SHOW CREATE TABLE
    • Spring Framework:声明式事务管理
    • MySQL 8.4:主键与唯一索引约束
    • MySQL 8.4:Online DDL Operations
    赞(0)
    未经允许不得转载:171主机测评 » 固定资产Excel批量导入怎么防重复?资产编码、重复检查和错误回执
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址