MoreRSS

site iconSunZhongWei | 孙仲维 修改

博客名「大象笔记」,全干程序员一名,曾在金山,DNSPod,腾讯云,常驻烟台。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

SunZhongWei | 孙仲维 的 RSS 预览

Magento 的批量导入机制,及数据库表 importexport_importdata

2025-09-13 09:59:58

找了半天 Magento 无法批量导入的问题,最后发现是 crontab 里设置了一个定时任务,每半小时 restart php fpm 的 docker 容器。导致 Magento 的批量任务没有执行完,就被 restart 中断了。如此反复,导致没有一个批量任务导入成功的。 我在 Magento 日志中,没有找到任何的相关日志,完全靠翻看数据库表结构,盲猜 magento 的导入机制,才解决这个问题的。

记录一下排查中学到的 Magento 无用知识:

数据库表

有个数据表,名为 importexport_importdata,记录了需要导入的产品信息。

实际数据来源就是,后台上传的 csv 文件。

切割机制

magento 会默认按照 100 个产品为一组,作为一条记录。记录值为一段 json。

  • 前一百个,为一个列表
  • 后面的,为 map。key 为数字编号,从 100 开始顺序编号。

这个切分单位 100 是可以通过配置文件修改的。

表结构

CREATE TABLE `importexport_importdata` (
  `id` int UNSIGNED NOT NULL COMMENT 'ID',
  `entity` varchar(50) NOT NULL COMMENT 'Entity',
  `behavior` varchar(10) NOT NULL DEFAULT 'append' COMMENT 'Behavior',
  `data` longtext COMMENT 'Data',
  `is_processed` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Is Row Processed',
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'timestamp of last update'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Import Data Table';

数据示例

INSERT INTO `importexport_importdata` (`id`, `entity`, `behavior`, `data`, `is_processed`, `updated_at`) VALUES
(868, 'catalog_product', 'append', '{\"0\":{\"sku\":\"M3AA-90LD-4\", ... }', 0, '2025-09-12 21:32:33');

清空上传队列

直接清空这个 importexport_importdata 表即可。

注意,最好不要使用 truncate,最好使用 delete。因为 truncate 会导致自增 id 被重置从 1 开始。不确定是否有其他表关联了这个 id,所以使用 delete 保险一点。

524 超时问题

如果在 magento 后台,批量导入的 csv 中包含数量巨大的产品。例如,超过 1000 个。大概率会遇到一直转圈处理中的提示。从浏览器调试工具看,查询进度的请求报了 524 错误。这是一个 cloudflare 的超时状态码。

其实,只要 csv 的里的产品数据,被存储到了导入队列表,那就可以关闭 magento 后台网页,甚至关闭浏览器。等待处理完成即可。

处理顺序机制

观察了一下 magento 的导入队列处理过程。发现,并不是顺序处理的。即不是按照 id 从小到大逐一处理的。从导入数据看,会同时导入前面几条记录中的产品信息(通过 SKU 搜索来排查)。

而且,导入是否成功的状态,是按照队列记录来的,而不是单个独立产品。所以,我猜测,这个队列处理机制是,定时任务处理队列记录,取出一条记录,解析出来产品列表,逐一保存。如果一条队列记录的处理被打断,例如运行时间过长,下次会从头重新处理。

比较诡异的是,这个处理是否完成,是批量同时更新的。例如,一批 2000 个产品,被分割成了 20 条处理记录。处理完成时间是一样的。。。而不是逐个完成。

magento 批量导入

后台产品列表更新不及时

从产品的创建时间看,写入数据库是实时的,但是不能及时在后台产品列表中看到产品。猜测是索引数据需要时间,有较长的滞后性,不知道创建索引是否也是以 100 为单位批量进行的。

代码质量堪忧

整体感觉 magento 的代码质量不太行,例如这个批量导入的体验极差

  • 导入速度巨慢
  • 前端进度提示跟没有一样。做成百分比进度会直观不少。
  • 批导队列的处理,看起来也存在重复操作的隐患

一个大胆的想法

我可以通过程序直接插入这个 mysql 表,importexport_importdata,就绕过了完全没有头绪的 magento php 接口,来实现批量导入。

得测试一下看看可行性如何。

人事管理系统开发之三,员工编号号批量生成

2025-09-10 11:09:44

需求

人事管理系统中,员工入职的时候,需根据公司前缀生成唯一的员工号。

TODO

  • 找几个知名公司的工卡看看员工号的格式
  • 确保公司信息表中有公司前缀的字段
  • 整理成文档。可以节省其他人收集的时间
  • 按照入职时间顺序生成员工号。参考之前生成设备序列号的规则,复用一下这部分代码。
  • 线上改表

问题的本质

员工号是做什么用的?如果没有员工号,会影响公司运营么?

  • 方便客户投诉时使用。。。因为用名字投诉不方便,也容易重名,用员工编号最合适。
  • 作为员工的唯一标识符
  • 作为员工的登录账号
  • 作为员工的考勤打卡号。作为一些三方考勤设备的标识。
  • 作为员工的薪资发放账号

老员工的问题

按理说应该按照入职时间顺序生成员工号。

头条的做法

为了防止从员工号中推断出入职时间,及职级,改成了随机七位数的员工号。 但是这个方案我不敢用,怕领导打死我。先上线一版最重要,他们觉得不好,就等他们出编号规则就行。

接口

  • 新建员工信息时,自动生成员工号。如果没有指定公司,则无法生成员工号。等 update 记录时,判断如果没有员工号,且有了公司,再生成员工号。
  • 更新员工信息时,如果之前没有员工号,且现在有了公司,则生成员工号。
  • 人才信息转员工信息时,自动生成员工号 (即,之前的简历备案入职,转正时生成员工号)
  • 一个批量生成员工号的接口,给老员工补全员工号用的 (只处理没有员工号的记录)
  • 员工转岗到另外一个子公司怎么办?员工号不变?还是重新生成?

员工编号格式

格式说明:

公司前缀 + 入职年份 + 四位流水号 例如, 公司前缀是 "SZ" ,入职年份是 2023 年,流水号是 0001 ,则员工编号是 "SZ20230001"

本来要求是使用年份的后两位作为年份标识,但是我觉得不严谨,做百年企业怎么能这么粗糙,于是一意孤行,4 位数得了。

继续阅读

📖 人事管理系统开发系列

人事管理系统开发系列

2025-09-08 14:04:58

在使用 Golang Gin 开发一套公司内部使用人事管理系统。至于为何不使用企业微信或者钉钉,飞书之类的现成系统内置的人事管理模块。领导们给出的原因是,公司内部架构太复杂,企业微信内置模块不支持。

但是实际上,我感觉还是人事部门负责人并没有完整了解过这些平台的人事管理模块,其实是足够灵活配置的,满足需求。只是这些人懒得去了解罢了。不过多抱怨这些了,让开发就开发吧。

这个开发的过程非常的枯燥无趣,而且这种成熟的内部系统,开发再多也没有产出和价值。所以,我觉得边开发,边整理遇到的问题。才能不至于写着写着代码,就想去划水。

系列章节

人事管理系统开发之二,员工资料上传

2025-09-08 13:55:45

需求

人事管理系统里设置了一堆员工需要上传的资料清单。当然,包含必须上传的和非必须上传的。

  • 必须上传的,包括毕业证,学位证等
  • 非必须上传的,包括各种资格证书,离职证明等

在编辑员工信息时,会拉取这个清单,显示在界面上。HR 或者员工可以选择上传对应的文件。

接口设计

原有的资料清单接口不做变化,保留,用于资料清单的后台增删改查编辑之用。

需要新增一个资料清单列表接口,可以提交员工 ID,在拉取资料清单的同时,返回:

  • 该员工已经上传了哪些资料
  • 上传时间
  • 上传人 (有可能不是员工自己上传的,而是 HR 或其他管理员上传的)

再就是需要:

  • 上传资料接口。即上传指定员工的某个类型的资料。传递员工 id 和资料类型 ID
  • 删除资料接口。

其实本质上就是一个复杂一点点的 CRUD 接口集。

Model 设计

type StaffDocument struct {
	ID          uint
	CreatedAt   time.Time
	UpdatedAt   time.Time
	StaffId     uint   // 员工 ID。上传文档关联的员工
	DocumentId  uint   // 资料 ID。关联的资料配置表
	CreatorId   uint   // 创建者 ID
	Files       string // 上传的文件 key, 多个逗号分隔。使用七牛云私有空间存储
}

其他

各种嵌套的 api 返回数据结构,不必纠结于现有的 Model 结构,可以根据需要,设计新的返回数据结构。 也方便进行联表操作。否则嵌套过多,会导致返回数据内容过长,影响性能。

TODO

  • ✅ 梳理需求
  • ✅ 模块英文名: StaffDocument
  • ✅ CRUD
  • ✅ 设计字段
  • ✅ 完善 swagger 文档
  • ✅ swagger 测试
  • ✅ 线上建表

继续阅读

📖 人事管理系统开发系列

最近心慌,入手大表盘的小米手环 pro 9 监测心率

2025-09-05 08:47:45

最近看到不少打羽毛球嘎了的兄弟,恰好最近总感觉心慌,担心心率有问题,就下单了一个小米手环,上一个还是五代,没想到九代 pro  就变成了这种大表盘,还挺好看👀

小米手环 pro 9

大表盘的小米手环 pro 9 ,这就是个手表哈😄 界面风格很像苹果手表,当然我只用过苹果手表一代,不知道现在是什么样子。续航号称20天,带了一晚上,早上醒来,电量还是100,夸张了。

我关注的功能

我需要用到的功能其实很少,主要是

  1. 随时关注心率。❤️ 跳太猛了,就躺平歇歇
  2. 闹钟。午休时,可以脱离手机闹钟。而且震动模式也不打扰别人
  3. 久坐提醒。坐久了,提醒我起身出去摸鱼一会,放松一下老腰

其他功能,其实我基本用不到。所以,简单纠结了一下是买小米手表,还是小米手环,感觉手环功能足够了,这个新版的大屏幕也想尝试一下,所以就入手了。

手机设置界面

不太适应很多功能藏的太深,找了半天才找到。不如老版本的 app 直观。

心率过速的原因

其实是本来打算买个 switch 游戏,业余放松一下心情,毕竟平时压力太大鸟。没想到买的这个哈迪斯游戏,一点也不放松,刷起来,比加班都累,但是又很上瘾😮‍💨,控制不住自己的手,也控制不住自己的心率。

switch 哈迪斯

还是缓缓再玩吧。中年人,回家就应该躺在沙发上,看电视,珍爱生命远离游戏。

还是班车上码字快。

golang gin 项目中添加多个 cmd 命令行工具,如何进行目录组织

2025-09-01 14:48:20

需求

之前开发的大赛报名网站终于进入了收尾阶段,比赛已结束,现在需要把参赛选手上传的资料及视频文件导出,做备份。

正好借此机会了解一下如何在 golang gin 项目中添加一堆命令行工具。

为何要引入 cmd 目录

之前把导出数据的功能,都放到了 gin API 接口中,然后通过 swagger 的文档管理界面调用,再保存。

但是,这样搞有个坏处,就是导致 swagger 文档中,会多出很多不需要前端使用的接口。 此外,像视频批量导出这样需要长时间执行的功能,需要更多详情日志的场景,也不适合放到 API 接口中。

所以,我需要将这些功能从 HTTP API 接口中剥离出来,放到单独的命令行工具中。然后代码目录组织就变成了一个问题 🤔

golang gin 中 cmd 命令的目录结构

印象中 Golang 官方是有目录结构组织的推荐结构的。

cmd/ 目录区分

在项目根目录下建立 cmd 文件夹,其中包含多个子目录,每个子目录是一个独立的命令(如 cmd/format-tool, cmd/cli-tool),每个子目录都有自己的 main.go 文件。

例如:

my-gin-app/
├── cmd/          // 存放多个入口命令
│   ├── tool1/
│   │   └── main.go
│   ├── tool2/
│   │   └── main.go
│   └── tool3/
│       └── main.go
├── go.mod
├── go.sum
└── main.go

这样,我只需要在当前的 gin 项目目录中新建一个 cmd/export 目录,在里面放一个 main.go 文件。

mkdir -p cmd/export
touch cmd/export/main.go

我发现多年前,我就测试过多个 main 的情况:

如何组织 Golang 项目目录,使一个项目包含多个 main 入口程序

main.go 代码示例

如何设置 package 呢?是否可以使用 main 作为 package 名称?因为原 gin 的入口文件 main.go 就是 main 作为包名。

测试了一下确实可以的:

package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello world")
}

执行:

> go run main.go
Hello world

然后,我又测试了一下原 gin 项目的编译,并不影响,很好。(๑•̀ㅂ•́)و✧

如何引用 gin 中的 Model 代码

跟其他包一样引用即可:

package main

import (
	"fmt"

	"sunzhongwei.com/my_project/models"
)

func main() {
	fmt.Println("Hello world")

	fmt.Println(models.Account{}.EditableFields())
}

不便的地方

由于之前 gin 项目中把数据库初始化放到了 main.go 中,导致在 cmd 中想直接使用 db 很麻烦。。。 还是把初始化逻辑放到 models 模块中比较合适。

是否需要使用 Cobra

对应简单的使用场景,不需要大量子命令的情况,我感觉可以不使用 Cobra。

等 cmd 命令多了,重合度高了之后,再引入 Cobra 也不迟。