ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

批量乐高套装的数字化入库:CSV与SQLite实现清点对账

批量乐高套装的数字化入库:CSV与SQLite实现清点对账 从石家庄集中收到一批乐高总数量在70余套这个体量已经不能按“先拆几盒拍个日常vlog再说”的方式处理。装箱、运输、中途翻找和多个批次的纸箱混在一起之后任何一次没有记录的挪动都可能让一套盒装品变成一堆无法区分的散件。真正的整理工作不是把每个盒子叠放整齐而是先把这批套装当成一组待入库资产清点、编码、登记、存放最后对账。这套方法可以用于个人收藏也可以借给工作室做库存盘点如果整理过程本身要拍成日常vlog它还能直接充当分集脚本和素材检索表。下面按一次在本地落地过的完整流程展开所有脚本和模板都可以直接改成自己的数据。1. 批量套装入库前先想清楚信息要流向哪里1.1 为什么“数量多”是分水岭两三套乐高到货随手放在桌面、拼完再收通常不会出大问题。但一旦数量到了70余套信息丢失的风险就开始叠加。丢失的往往不是零件本身而是“这套是什么型号、拆到什么程度、放在哪个箱子、是否缺件”这些记录。一个常见场景是为了拍日常vlog把所有箱子都打开先拍外箱再拍零件袋然后为了镜头好看把多个套装的零件摊在一起。结果是素材看起来很丰富但三天后再想拼其中一套面对的是混在一起的散件已经无法确定哪些是原装、哪些缺件。这个规模下整理的本质不是“把东西收整齐”而是要让四个问题的答案随时可查一共收到多少套和记录里的数字是否一致。每一套处于什么状态是全新未拆还是拆袋未拼。每一套放在哪里能否在十分钟内找出来。有没有异常比如缺件、包装破损、说明书中英文版本不符、需要补袋。1.2 收货第一步箱级登记而不是逐个拆盒很多人的第一反应是按主题拆盒城市系列放一起科技系列放一起。但在第一批箱子来源不一、运输过程中可能翻动过的情况下直接拆盒会让“箱子里原本装了什么”这个信息永久消失。推荐先做一次箱级登记。所谓箱级登记是指不打开内部包装先给每个外箱一个临时编号并记录能从外部直接观察到的信息。这样即便后续发现某个箱子里缺货也能倒查是哪个批次、哪个入口出了问题。外箱临时编号的规则尽量简单用B01、B02这种格式。先不要直接写乐高型号因为外箱很多时候不是原装盒只是运输用的纸箱直接写型号容易误导后续核对。箱级登记表的内容如下字段填写说明示例box_no本批纸箱唯一编号B01source来源渠道或批次说明石家庄自提label_info外箱或内盒上肉眼可见的型号/名称盒面货号待确认package_damage外箱是否压损、受潮角部压痕needs_urgent是否需要立即处理压损严重需验盒media_prefix对应视频或照片的前缀b01这个阶段不要追求把每一个型号都读出来。很多外箱上只有快递面单内盒上才有真正货号。能登记多少登记多少目的是建立箱子和内容物之间的第一层索引。1.3 为 vlog 素材预留第二套编号如果这次整理要拍成日常vlog视频素材也要从一开始就进入同一套编号体系。最稳妥的命名不是“开箱视频1”“盒子特写”而是“时间_地点逻辑_箱号_拍摄内容”的结构。示例命名YYYYMMDD_shijiazhuang_b01_exterior.mp4 YYYYMMDD_shijiazhuang_b01_bags.mp4 YYYYMMDD_shijiazhuang_b01_manual.mp4这样命名的好处是后期剪辑时不用打开视频也能判断内容。箱号是实物通道里的编号media_prefix 是素材通道里的编号两条通道通过同一个B01关联起来。这里最常见的坑是拍摄时很有热情素材拍完后以默认文件名堆在存储卡里等到剪辑时只能靠缩略图猜测内容。对几十个套装来说这种方式基本不可维护。所以在拆第一箱之前应该先建好素材文件夹并确定命名规则而不是边拍边改。2. 台账字段设计能回答查询而不是为了“记了”2.1 先设计字段再开始填表整理过程中最忌讳拿着一支笔、看到什么写什么。因为写到后面同一个概念可能被表达成不同说法。比如“未拆”“没开”“还没拼”在统计时很难被当成同一类状态。批量场景下应该先用一个固定结构约束每一次记录。一份个人收藏台账至少需要这些字段字段含义是否必填填法建议seq每行数据的唯一序号是1、2、3递增box_no所属外箱编号是B01set_number套装官方货号或自定义编号是尽量填写官方货号set_name套装名称建议填官方名称便于人工识别theme主题系列否城市、科技、城堡等release_year发行年份或版本年份否用于区分再版piece_count官方颗粒数否未知时留空不要填0package_status外盒和包装状态是使用枚举见下文parts_status内袋或零件状态是使用枚举见下文manual_status说明书状态是有、无、待核对storage_box最终存放箱号否A01、A02slot_no箱内位置或层号否01、02risk_flag异常标记否缺件待查、压损等remark备注否只写无法枚举的信息media_prefix对应素材前缀否b01_001set_number是唯一锚点。名称可能写错主题可能有争议但官方货号在多数情况下是稳定的。如果某些套装没有官方货号才使用自定义编号并且要在remark里说明。2.2 状态字段使用枚举不要自由填写状态字段是最容易被“自由发挥”破坏的地方。记录“盒装开封”“开的盒”“已开”“盒子烂了”时整理者自己当时明白一周后就会开始犹豫。为了防止这种情况应该先约定一组状态值。包装状态建议使用这组值package_status含义全新未拆外盒塑封或封口仍完整盒装开封外盒正常已被打开过外盒压损外盒存在明显压痕或破损无外盒只有内袋或散件没有原装盒待拍照包装状态无法判断需要进一步查看零件状态建议分开记录不要和外盒状态混在同一个字段parts_status含义内袋完整每个零件袋没有开封内袋已拆未拼零件袋已打开但还没开始搭建已拼完整成品完整可以整件保存已拼拆散拼好后又拆散状态不明散件待核实以散装零件形式存在需要清点缺件待查发现缺袋或缺关键零件这样设计的核心原因是外盒完好不等于零件袋完整盒装开封也不代表零件一定缺失。拆得越早信息越模糊所以应该在拆箱前或拆箱当下尽量把状态记录下来。2.3 一份可以直接开始填写的 CSV 模板使用办公软件或纯文本编辑器都可以维护台账。个人项目中 CSV 是最低门槛的格式也方便后续被 Python、SQLite 读取。模板如下seq,box_no,set_number,set_name,theme,release_year,piece_count,package_status,parts_status,manual_status,storage_box,slot_no,risk_flag,remark,media_prefix 1,B01,SET-001,示例套装A,城市,2021,350,全新未拆,内袋完整,有,待分配,,,,本行数据仅用于演示,b01 2,B01,SET-002,示例套装B,城堡,2023,,盒装开封,内袋已拆未拼,有,A01,01,缺件待查,袋口有破损,b01_002 3,B02,SET-003,示例套装C,科技,2022,,无外盒,散件待核实,待核对,待分配,,,需要清点,b02示例中的SET-001、SET-002是故意使用的占位编号。实际整理时如果知道官方货号应该把这一列替换成真实货号如果暂时不知道也不要整行留空至少给一个临时编号避免“没有编号的套件”之后无法引用。在 CSV 里留意几个细节piece_count未知时不要填 0。填 0 在后期统计总颗粒数时会被误认为“这套没有颗粒”。每一行只记录一个物理实体。如果同一个货号有两套应该占两行通过seq区分。列名不要用空格和中文避免后续脚本处理时出现编码或转义问题。2.4 同一型号出现多套或再版时怎么办批量到货后同一官方货号出现两套甚至多套的情况并不少见。不要因为“编号重复”就删除其中一行。正确的做法是给每套一个独立seq并在release_year或remark中标记差异。例如seq,box_no,set_number,set_name,release_year,remark 9,B05,SET-010,示例套装X,2019,初版外盒 10,B05,SET-010,示例套装X,2023,再版外盒虽然set_number相同但两套外盒印刷、说明书版本可能不同存放位置也可能不同。保留两行才能在将来回答“编号 SET-010 到底有哪两套”的问题。3. 从 CSV、Python 到 SQLite 的最小核对闭环3.1 环境准备不引入额外依赖也能完成核对个人收藏台账不需要一开始就使用重量级数据库。常见环境下Python 自带csv和sqlite3模块已经足够完成校验、入库和查询。主流的 Python 3 环境都可以直接运行下面的脚本不需要额外安装第三方库。如果本地还没有安装 Python可以先在命令行确认python --version如果命令提示找不到再尝试python3 --version这里不强制规定版本能运行 Python 3 即可。3.2 写一个数据校验脚本尽早发现笔误台账录入几十行后肉眼很难发现重复编号、空缺字段或异常状态。可以在归档前用一段脚本做基础检查。下面的脚本读取sets.csv输出三类问题必填字段为空、seq重复、set_number相同且没有说明信息。脚本本身只是检查数据质量不会修改原始 CSV。import csv from collections import defaultdict INPUT_FILE sets.csv REQUIRED [seq, box_no, set_number, package_status, parts_status] seen_seq set() seen_number defaultdict(list) issues [] with open(INPUT_FILE, r, encodingutf-8-sig, newline) as f: reader csv.DictReader(f) for row_number, row in enumerate(reader, start2): seq row.get(seq, ).strip() set_number row.get(set_number, ).strip() # 检查必填字段 for field in REQUIRED: value row.get(field, ).strip() if not value: issues.append(f第{row_number}行字段 {field} 为空) # 检查 seq 重复 if seq and seq in seen_seq: issues.append(f第{row_number}行seq 重复 - {seq}) seen_seq.add(seq) # 检查相同 set_number 是否出现多次 if set_number: seen_number[set_number].append(row_number) for number, lines in seen_number.items(): if len(lines) 1 and number.startswith(SET-): issues.append(f编号 {number} 出现了 {len(lines)} 次请确认是否为多套实物) if issues: print(发现潜在问题) for issue in issues: print(-, issue) else: print(基础校验通过。)CSV 文件建议使用utf-8-sig编码打开。在 Windows 上用 Excel 另存的 CSV 经常是带 BOM 的utf-8格式直接读取可能会在第一列字段名里混入不可见字符。utf-8-sig是一种兼容处理方式。运行方式python check_sets.py脚本输出“基础校验通过”不代表数据完全正确只代表结构层面没有明显问题。数量是否对得上最终仍要和实物对账。3.3 把台账导入 SQLite用查询代替翻表格当数据行数超过几十行后用 CSV 自带的筛选还能应付但要做跨字段统计不如导入 SQLite。SQLite 不需要单独启动服务库文件就是一个本地文件适合个人收藏数据长期保存。先准备建表 SQLCREATE TABLE sets ( seq INTEGER PRIMARY KEY, box_no TEXT NOT NULL, set_number TEXT NOT NULL, set_name TEXT, theme TEXT, release_year TEXT, piece_count INTEGER, package_status TEXT, parts_status TEXT, manual_status TEXT, storage_box TEXT, slot_no TEXT, risk_flag TEXT, remark TEXT, media_prefix TEXT );SQLite 对类型要求并不严格字段类型更多是给维护者看的规范。seq是主键避免同一行被重复导入。release_year使用 TEXT 而不是 INTEGER是因为某些老套装年份信息不明确可能需要填写作“1980s”这类模糊值。导入方式可以直接执行一个 Python 脚本import csv import sqlite3 con sqlite3.connect(lego.db) cur con.cursor() with open(sets.csv, r, encodingutf-8-sig, newline) as f: reader csv.DictReader(f) for row in reader: cur.execute( INSERT OR REPLACE INTO sets ( seq, box_no, set_number, set_name, theme, release_year, piece_count, package_status, parts_status, manual_status, storage_box, slot_no, risk_flag, remark, media_prefix ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) , ( row.get(seq), row.get(box_no), row.get(set_number), row.get(set_name), row.get(theme), row.get(release_year), row.get(piece_count) or None, row.get(package_status), row.get(parts_status), row.get(manual_status), row.get(storage_box), row.get(slot_no), row.get(risk_flag), row.get(remark), row.get(media_prefix), ), ) con.commit() con.close()这段代码把 CSV 的每一行写入 SQLite。INSERT OR REPLACE的意思是如果seq已经存在就覆盖旧数据。这样在后续修改台账后重新导入不会因为主键冲突而中断。3.4 三条最常用的盘点查询把数据导入 SQLite 后日常盘点只需要几条 SQL按主题统计数量SELECT theme, COUNT(*) AS count FROM sets GROUP BY theme ORDER BY count DESC;查找某个外箱里放了哪些套装SELECT seq, set_number, set_name, package_status, parts_status, risk_flag FROM sets WHERE box_no B01;查看所有带有异常标记的套装SELECT seq, set_number, set_name, box_no, risk_flag FROM sets WHERE risk_flag IS NOT NULL AND risk_flag ! ;统计已知颗粒数的套装总数SELECT COUNT(*) AS known_piece_sets FROM sets WHERE piece_count IS NOT NULL AND piece_count 0;需要说明的是SQL 查到的只是数据库里的情况数据库记录不等于实物情况。每次实物移动后都应该同步更新数据库否则查询结果会逐渐失真。4. 物理存放先保护外盒再保护零件状态4.1 存放顺序不要先拆盒先处理高风险纸箱70余套盒子如果全部堆在地面很容易发生底层纸箱受压、倒塌和混箱。物理上先处理的不是“看起来最好看的”而是风险最高的部分。建议按下面顺序处理外箱有明显压损或受潮的第一轮拆看确认内部零件袋是否受影响。盒装开封、袋口已经松动的第二轮处理重新封口并放置防潮剂。无外盒的散件第三轮处理逐个用自封袋缓冲。全新未拆、包装完好的套装最后集中存放减少开合次数。每一轮处理都只做一件事。比如第一轮只负责拆看压损箱不要在中间顺手去拼一套感兴趣的套装否则实物进度和台账进度会迅速脱节。4.2 拆袋之后零件状态就进入“模糊区”有些整理者为了节省空间喜欢把所有套装的内袋全部拆开然后按零件颜色或种类混放。这个做法对 MOC 玩家可能有用但对于已经拥有具体图纸说明书、想要随时拼回原套装的人是灾难性的选择。一套乐高套装的零件袋通常已经按搭建顺序分袋。比如 A 袋包含前半部分零件B 袋包含后半部分零件。一旦把A袋和B袋倒在一起虽然零件总数没变但后续拼装时找件成本大幅上升。所以处理策略应当是原盒原袋完整的套装不拆袋保持原样。已成散件的套装如果已经无法按原袋区分至少按照颜色或零件大类装进独立自封袋并在袋外写上set_number。绝不要为了省一个自封袋而把两套装进同一个袋中。对于需要拍日常vlog的整理场景拍摄完零件袋后立刻按原来的袋号放回去不要因为画面需要把所有零件倒进同一个盘中展示。4.3 给每一层存放容器做标签批量套装存放时最常见的问题是“箱里有货但标签只写了数字或主题”。比如一个大纸箱上写“城市”里面可能混了好几个型号当需要找某一套时仍然必须翻箱。推荐在存储箱上贴一个箱内物清单。如果不想反复手写可以在数据库台账里按storage_box分组生成一个纯文本清单后打印sqlite3 lego.db .headers on .mode column SELECT seq, set_number, set_name, slot_no FROM sets WHERE storage_box A01 ORDER BY slot_no;把查询结果贴在对应箱体外面或者拍照存进手机相册。找货时先看箱外清单找到slot_no再开箱取对应区域不用把整箱倒开。4.4 横跨多个箱子时必须在台账里写明大型套装可能无法放进常规存储箱或者为了减少搬动会把包装盒放在一个箱子、把成品放在另一个箱子。这个场景可以存在但必须像处理跨页表格一样处理主记录放在主存放箱辅助记录放在remark字段。示例 remark 写法外盒在 A03拆散零件在同箱下层备用补件袋独立存放不要只用脑子记“盒子和零件分开放了”。套数少时没问题到70余套后一定会忘记。5. 数量复查让清单、实物、素材三张账对得上5.1 第一轮对账清单数量 vs 外箱队列整理完成后不能只确认“东西都放进去了”。要从数量到位置再做一轮对账。对账步骤在数据库里按box_no汇总拿到每组箱号对应的套装数。回到存储区逐个核对物理外箱上的标签。打开纸箱检查最外侧的存储箱确认箱号与清单一致。对异常项重新更新风险状态。如果数据库有 72 条记录但外箱标签加起来只有 71 个箱子说明要么有箱子标签漏贴要么数据库多写了一行。这时候不要先改数据库先找到那个“隐身”的箱子确认是哪一侧的错误。5.2 第二轮对账抽查风险套装对全部套装逐套开箱检查不现实也没有必要。第二轮对账重点抽查三类risk_flag不为空的套装。box_no是压损箱的套装。同一set_number出现多行的套装。抽查时从外箱中取出核对检查点通过标准盒体货号与台账set_number一致说明书说明书货号与该套装一致零件袋数量与现场拍摄的袋数一致风险标记已补齐说明没有“似乎缺件”的模糊描述如果发现零件袋数量对不上应该把该套装标记为“缺件待查”并记录缺的是哪一袋例如“缺少编号为 4 的零件袋共三袋只看到两袋”。5.3 第三轮对账素材和台账对齐日常vlog场景比单纯收藏多一层素材账。素材如果和实物编号不对应整理完成后剪辑仍会陷入混乱。建议在每次拍摄后用“批量重命名”工具或文件管理的重命名功能把素材按media_prefix归整。例如b01对应的照片可能有一批b01_exterior_01.jpg b01_exterior_02.jpg b01_bags_01.jpg b01_bags_02.jpg b01_manual_01.jpg归整完毕后在台账的media_prefix字段填入 b01。这样以后想找某套的说明书照片只要按编号搜索就能直接跳到对应文件夹。5.4 定义一个“整理完成”的验收标准整理是否完成需要有一个可验证的标准而不是“感觉弄完了”。建议以这几条为完成标志数据库总行数与实到套件数一致。每一行都有box_no和storage_box。所有能识别的套装都有set_number。risk_flag为非空的套装都写明原因和下一步计划。视频素材按media_prefix归档不再存在无编号的视频文件。缺件的套装单独放在“待补件区域”没有混在待拼区域里。这几个条件同时满足后才算真正完成入库。6. 常见坑、排错顺序与后续增量入库清单6.1 现象与排查表先看现象再改数据整理过程中最怕看到问题就怀疑“是不是系统坏了”。多数情况下问题出在数据录入和实物状态不一致。下表列出几个高频问题问题现象常见原因检查方式处理建议清单总数比实际多一套同一套件被重复记录在数据库按set_number分组查看是否有疑似重复行确认为重复后删除错误行不要用“同名就删”的方式清单总数比实际少一套漏登记或先拆后记核对箱级登记表和最终存储箱清单找到漏登记套装补录一行数据找某套装时不在标记箱里移动后没有更新位置用media_prefix找现场照片确定最后一次记录位置更新storage_box以后移动时先改台账再搬箱同一货号两套被误删看到编号重复就直接合并检查release_year和remark是否有版本差异每个物理实体一行用seq区分零件袋状态不清楚表单里 allowed 自由填写查看parts_status字段是否有未归一化的值统一改成枚举值SQL 查询结果中有表头行CSV 的表头被当成数据导入重新运行导入脚本检查seq字段是否包含英文字段名使用 Python 导入并跳过 DictReader 表头如果对不上账不要急着重录。先确定是哪一层出现偏差是源码 CSV 和实物不一致还是 CSV 和数据库不一致又或是素材命名和台账不一致。顺序是从实物反推再到 CSV再到 SQLite。具体排查顺序实物清点 - 与箱级登记表对比 - 与 sets.csv 对比 - 与 SQLite 查询结果对比 - 与素材文件命名对比每对比一层就停下来说清楚差异点不要一次跨过三层直接改数据库。6.2 入库前检查清单下一次再收到批量套装时可以直接参考这份清单是否已经建好素材文件夹并且定好命名规则。是否已经创建 CSV 表头状态字段使用枚举值。是否完成箱级登记每个纸箱都有box_no。是否优先处理了压损、受潮、无外盒的高风险箱。是否对每一套都记录了package_status和parts_status。是否给缺件或待补件设置了risk_flag。是否打印了按storage_box分组的箱内清单。是否完成三轮对账数量、风险套件、素材编号。是否把缺件区、待拼区、长期存放区分开。这个清单看起来琐碎但在 70 余套的体量里每一条都能避免一次几小时的返工。6.3 学习环境与大批量入库场景的做法差异单套或少量套装阅读这篇流程时会觉得步骤过重。如果只是两三套确实不需要建数据库、写 Python 脚本。一个简单的电子表格就能覆盖。但一旦出现以下信号就值得切换到完整流程同一批次超过 20 套。同一货号出现多套。运输箱和原装盒混在一起无法一眼分清。需要为后续 vlog 剪辑提供素材检索。收藏未来可能增长到上百套。个人整理不一定需要搭建完整应用使用 CSV 加 SQLite 已经是学习成本和维护成本都比较低的组合。如果以后需要多人协作或移动端实时查询再把 CSV 迁移到更完整的数据库系统这套字段设计仍然成立。6.4 后续增购时的最小增量维护整理完第一批 70 余套之后后续每次购入都要走一次增量流程避免几年后又堆成一大摊。最小增量流程是新套装到货后先编一个新seq。拍外箱照片文件前缀用日期和箱号。更新 CSV录入set_number、状态和存放位置。执行一次check_sets.py校验。重新导入 SQLite。把新套装的资料追加到对应存储箱箱外清单。这个流程依赖的仍然是一张 CSV、一个数据库文件和一个 Python 校验脚本。对个人收藏而言能坚持这套小流程比寻找更花哨的软件更重要。这套流程跑通之后下一批到货时应该先做的事不是继续拆箱而是先把纸箱码放整齐再打开同一个 CSV补上第一行新记录。
返回列表