尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ECSHOP v3.0数据字典解读:商品、订单、会员表结构与会话查询

ECSHOP v3.0数据字典解读:商品、订单、会员表结构与会话查询 简介ECSHOP v3.0 数据库字典以 Word 文档形式整理了电商系统核心表结构面向 ECSHOP 二次开发人员、PHP 电商项目维护者及数据库初学者。文档围绕商品分类表、商品数据表、商品货品表、关联文章表、商品相册表等核心业务表展开逐一说明字段名、字段类型、默认值、索引及备注含义并给出库存预警、促销价格不参与会员折扣、虚拟商品标志等关键设计规则整体采用表格化呈现结构清晰便于快速定位字段定义。资源包共 1 个文件为 324KB 的 docx 文档方便检索、标注和打印目前已有 233 位学习者下载使用。借助这份数据字典读者可以快速掌握各模块字段用途减少查阅源码或数据库的时间成本在二次开发或数据迁移时也能据此设计表结构、校验字段类型并理解 ECSHOP 的存储约定。对商品模型扩展、分类层级维护、促销与库存逻辑梳理等任务均可提供直接参考适合作为团队内部资料或自学笔记。1. 一份数据字典对二次开发意味着什么接手过 ECSHOP 老项目的人都有这种经历线上跑了两三年商品、订单、会员数据全在库里但没人说得清每个字段到底是干嘛的。想加一个“分类页价格区间筛选”翻后台配置找不到入口想统计供应商商品审核通过率DBA 给了张表却不知道is_check和suppliers_id怎么联动最头疼的是对接 ERPdiscount_fee和goods_discount_fee一个没搞明白对账就差出几万块。这份 ECSHOP v3.0 数据字典的价值就在这——它不是用来学的是用来在改造、迁移、对账时当依据的。本文基于这份完整数据字典把商品、订单、会员三大域的字段设计和隐含的业务规则拆开讲最后给出基于字典核对线上库结构的具体方法。不管你是接盘维护、做数据迁移还是准备从 v3.0 升级到 v3.6这篇文章都按可落地的标准来写。2. 商品域表结构从 category 到 product 的建模脉络商品域是 ECSHOP 里表最多、字段最杂的一块。数据字典里从category开始到goods、product再到goods_attr、goods_cat、volume_price覆盖了分类、商品主档、SKU、属性和阶梯价五个层次。理解这一块的关键是先分清“分类”和“类型”是两个完全不同的概念再搞清楚goods表里那些is_*标记怎么协同工作。2.1 category分类表里藏着的导航与前台筛选逻辑category表用cat_id做自增主键parent_id指向上级分类顶级分类默认为 0这就是 ECSHOP 分类树的基础。字段不多但有三个值得注意show_in_nav控制分类是否出现在前台导航栏is_show控制分类在前台分类列表中是否可见二者是独立控制的——一个分类可以隐藏但仍在导航栏出现反之亦然。grade是价格区间个数配合filter_attr实现前台按价格区间筛选。很多二次开发改分类页时只动is_show忘了grade为 0 会导致价格区间筛选不出现。复现这张表的建表语句重点看类型选择CREATE TABLE category ( cat_id smallint(5) unsigned NOT NULL AUTO_INCREMENT COMMENT 分类编号, cat_name varchar(90) NOT NULL DEFAULT COMMENT 类别名称, keywords varchar(255) NOT NULL DEFAULT COMMENT 分类关键词, cat_desc varchar(255) NOT NULL DEFAULT COMMENT 分类描述, parent_id smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 上级分类, sort_order tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 排序序号, template_file varchar(50) NOT NULL DEFAULT COMMENT 模板文件, measure_unit varchar(15) NOT NULL DEFAULT COMMENT 数量单位, show_in_nav tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 是否显示在导航栏, style varchar(150) NOT NULL DEFAULT COMMENT 分类样式表, is_show tinyint(1) unsigned NOT NULL DEFAULT 1 COMMENT 是否显示, grade tinyint(4) unsigned NOT NULL DEFAULT 0 COMMENT 价格区间个数, filter_attr smallint(6) unsigned NOT NULL DEFAULT 0 COMMENT 筛选属性, PRIMARY KEY (cat_id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT商品分类表;字段类型的选择值得注意分类编号用smallint(5) unsigned最大 65535对绝大多数电商站足够sort_order用tinyint(1)而不是常见的int是因为排序值只需要 0-255。show_in_nav和is_show都是tinyint(1) unsignedMySQL 里tinyint(1)常被 ORM 映射为布尔值但 ECSHOP 的原生 SQL 是直接比较 0/1 的二次开发时不要依赖 ORM 的布尔转换。2.2 goods一张表完成商品主档、营销与库存预警goods表是整个 EC SHOP 商品域的核心字段从goods_id到rank_integral共 40 多个。这里不逐字段罗列按职责拆成几组来看基础信息组goods_name、goods_sn、goods_name_style、brand_id、provider_name价格与促销组market_price、shop_price、promote_price、promote_start_date、promote_end_date库存组goods_number、warn_number标记位组is_on_sale、is_alone_sale、is_best、is_new、is_hot、is_promote、is_delete、is_check积分组integral、give_integral、rank_integral其中最容易踩坑的是promote_price的语义。数据字典备注明确有促销价格时按促销价销售且该价格不再参与会员折扣计算。也就是说user_rank.discount的折扣只作用于shop_price不会叠加到promote_price上。这在做价格展示时非常关键——后台配了会员折扣用户看到促销价却没打折不是 bug是设计。库存预警的逻辑也写得很清楚WHERE goods_number warn_number。warn_number默认是 1意味着库存低于 1 时预警也就是缺货预警。这里要特别提醒ECSHOP 对goods_number的类型定义是smallint(5) unsigned最大 65535而货品表product.product_number已经从smallint(5)修正为mediumint(8)。如果你在 v3.0 上做库存同步单商品库存超过 6 万就会溢出升级或修改结构时建议一并把goods.goods_number改为mediumint(8) unsigned。2.3 product、goods_attr 与 goods_catSKU、属性与多分类的协作product表是 ECSHOP 的 SKU 层product_id自增goods_id关联商品goods_attr保存规格组合product_sn是货号product_number是货品库存。注意goods_attr在这里是varchar(50)存的是商品属性表中goods_attr_id的组合比如198,199不是属性值的文本。查询某个 SKU 的具体规格时需要把这段 ID 拆开再去goods_attr表里取attr_value。实际开发中常见的做法是在应用层拆解而不是在 SQL 里用FIND_IN_SET因为规格 ID 的顺序会影响匹配结果。goods_cat表是商品与分类的多对多关系表一个商品可以挂在多个分类下goods_id和cat_id联合主键。这解释了为什么goods表里的cat_id字段被标注为“所属主分类”——ECSHOP 用goods.cat_id做主分类提升查询性能用goods_cat做扩展归属。关联查询示例SELECT g.goods_id, g.goods_name, g.goods_sn, c.cat_name AS main_cat, b.brand_name, g.shop_price, g.promote_price, g.goods_number, g.warn_number FROM goods g LEFT JOIN category c ON c.cat_id g.cat_id LEFT JOIN brand b ON b.brand_id g.brand_id WHERE g.is_delete 0 AND g.is_on_sale 1 AND g.cat_id IN (SELECT cat_id FROM goods_cat WHERE goods_id g.goods_id);这个查询把商品主档、主分类、品牌一次取出。is_delete 0过滤回收站商品is_on_sale 1只查上架商品。IN子查询用来处理一个商品挂在多个分类下的场景——如果只按goods.cat_id过滤会漏掉通过goods_cat挂到其他分类的商品。实际业务中如果分类页商品缺失优先检查goods_cat的数据是否完整。3. 订单与会员域状态机、资金流水和冗余设计订单域和会员域的表结构思路和商品域完全不同。商品表是一张主表加若干辅助表而订单域是围绕order_info这张主表展开的order_goods存商品快照order_action存操作日志delivery_order和back_order分别对应发货与退货。会员域则通过users主表联动user_account、account_log、user_address和collect_goods。理解这些表的关键不在于记住字段而在于搞懂三个问题订单状态怎么变迁、金额字段为什么冗余、会员资金和积分为什么分表记录。3.1 order_info三状态联动与金额字段的完整拆解order_info是 ECSHOP 里字段最多的一张表核心状态机由三个字段协同控制order_status、shipping_status、pay_status。数据字典的备注给出了每个字段的枚举值状态字段值含义order_status0未确认order_status1已确认order_status2已合并order_status3已取消order_status4无效order_status5退货shipping_status0未发货shipping_status1已发货shipping_status2确认收货shipping_status3备货中shipping_status4已发货部分商品pay_status0未付款pay_status1付款中pay_status2已付款这三个字段必须组合理解。一个典型的已完成订单是order_status 1, shipping_status 2, pay_status 2而一个已取消的订单可能是order_status 3, shipping_status 0, pay_status 0。做订单列表筛选时不能只看单一状态字段否则会出现“已取消但已付款”这类脏数据。很多 ERP 对接失败就是只同步了order_status没管另外两个字段。金额字段的设计也很有代表性。goods_amount、shipping_fee、pay_fee、pack_fee、card_fee、insure_fee、integral_money、bonus、surplus分开存储最后汇总为order_amount。注意order_amount不是存出来的而是计算出来的——goods_amount shipping_fee pay_fee pack_fee card_fee insure_fee - integral_money - bonus - surplus。订单列表展示金额时不能直接取order_amount要检查money_paid已付款金额与order_amount是否一致差额就是待支付或已退款部分。3.2 order_goods 与 order_action交易快照和审计日志order_goods表存的是下单那一刻的商品快照goods_name、goods_price、goods_attr都是冗余存储。这意味着商品后来改了名、调了价订单里的记录不会跟着变。这个设计对财务对账很重要——你要的对账单依据永远是order_goods.goods_price而不是goods.shop_price。另外goods_attr字段存的是规格文本比如“颜色:红色 \n 尺寸:M”直接展示用不需要再关联查询。order_action表是订单操作日志记录谁在什么时间把订单从什么状态改成了什么状态。action_user可能是管理员也可能是系统。这张表的作用主要是审计和纠纷定位。常见的坑是日志只记录order_status变化而忽略了shipping_status和pay_status的变化导致查“谁改了发货状态”时无从下手。二次开发建议在写入order_action时把三个状态字段全部冗余进去与数据字典的结构对齐。3.3 users 与 account_log密码安全、资金流水和积分体系users表的设计比其他电商系统复杂。password是 MD5 值salt是密码种子组合方式是md5(md5(password) salt)这是 ECSHOP v3.0 的密码生成逻辑。flag字段用于用户重名处理1 表示未处理2 表示改为alias记录的名字3 表示删除4 表示重名但不处理。这个机制是配合第三方系统整合用的自己开发登录功能时容易忽略flag 0的用户需要特殊处理。资金和积分分表记录是另一个关键设计。users.user_money是账户可用资金frozen_money是冻结资金pay_points是消费积分rank_points是等级积分。account_log表记录每一次变动change_type枚举0 充值1 提款2 调节账户99 其他。对比之下user_account表是用户提交的充值/提款申请process_type区分 0 充值、1 提款、2 购买商品、3 取消订单。两表配合才能完整追踪一笔钱从申请到入账的全过程。查询用户资金流水的典型 SQLSELECT al.change_time, al.user_money, al.frozen_money, al.rank_points, al.pay_points, al.change_desc, CASE al.change_type WHEN 0 THEN 充值 WHEN 1 THEN 提款 WHEN 2 THEN 调节账户 ELSE 其他 END AS change_type_name FROM account_log al WHERE al.user_id 1001 ORDER BY al.change_time DESC LIMIT 50;change_time是int(10) unsigned的时间戳不是datetime。查询结果要在应用层用date(Y-m-d H:i:s, $change_time)转换。很多从其他系统转过来的开发容易在这里出错——直接用FROM_UNIXTIME(change_time)没问题但做时间范围筛选时要记得比较对象也必须是时间戳不能直接传字符串日期。4. 对照数据字典写查询从字段反推业务模型把前两章的表结构串起来就能完成绝大部分业务查询。这一章挑三个典型场景促销商品筛选、库存预警、订单异常筛查。每个场景都给出可执行的 SQL并说明字段之间容易忽略的边界条件。4.1 促销、库存预警和虚拟商品的查询边界促销商品的判断条件不是is_promote 1就够了还要校验促销时间窗口。promote_start_date和promote_end_date都是int(10)时间戳且promote_end_date在数据字典中标注为“此价格不再参与会员折扣计算”。正确的促销商品查询SELECT goods_id, goods_name, shop_price, promote_price, promote_start_date, promote_end_date FROM goods WHERE is_promote 1 AND is_on_sale 1 AND is_delete 0 AND promote_start_date UNIX_TIMESTAMP() AND promote_end_date UNIX_TIMESTAMP();这里有一个容易犯的错有些项目在后台设置了is_promote 1但忘记设置promote_start_date默认值为 0导致promote_start_date UNIX_TIMESTAMP()永远成立促销提前生效。反过来如果只设了开始时间而promote_end_date为 0促销永远不会结束。这两种情况都要在写入时做默认值校验。库存预警的查询逻辑是goods_number warn_number。注意warn_number的默认值是 1所以库存为 0 的商品会触发预警。但这里有个边界goods_number是smallint(5) unsigned最大 65535超卖时如果库存减成负数虽然 ECSHOP 通常拦截unsigned会导致数值回绕成 65535。做库存同步时一定要先确认goods_number的类型是否已升级。虚拟商品的判断不看is_real而看extension_code。is_real为 0 表示虚拟商品但具体是哪种虚拟商品由extension_code区分比如virtual_card代表虚拟卡密。查询虚拟商品时要关联virtual_card表SELECT g.goods_id, g.goods_name, vc.card_sn, vc.card_password, vc.is_saled, vc.end_date FROM goods g JOIN virtual_card vc ON vc.goods_id g.goods_id WHERE g.extension_code virtual_card AND vc.is_saled 0;is_saled 0表示未售出前端下单后系统从这张表取卡密发给用户。如果end_date小于当前时间即使is_saled 0也不能再卖——这个过期校验在 v3.0 原版里存在但不够严格二次开发时建议在售出逻辑里加上。4.2 订单生命周期与异常订单筛查订单状态三字段联动是 ECSHOP 最容易被误用的设计。做“已完成订单”统计时如果只写order_status 1会把待发货的订单也统计进去。正确的状态判断SELECT order_id, order_sn, consignee, order_amount, money_paid, order_status, shipping_status, pay_status FROM order_info WHERE order_status 1 AND shipping_status 2 AND pay_status 2 AND add_time BETWEEN UNIX_TIMESTAMP(2024-01-01) AND UNIX_TIMESTAMP(2024-01-31);异常订单筛查是数据字典最有价值的应用场景。比如“已付款但未发货”的订单是pay_status 2 AND shipping_status 0而“已发货但未确认收货”的是shipping_status 1 AND order_status 1。更进一步money_paid和order_amount不一致的订单意味着支付金额与订单金额不匹配可能是部分退款或者支付回调异常SELECT order_sn, consignee, order_amount, money_paid, pay_status, shipping_status FROM order_info WHERE money_paid order_amount AND order_status NOT IN (3, 4) AND pay_status 2;order_status NOT IN (3, 4)排除已取消和无效订单。这里暴露了一个字段设计上的坑money_paid是decimal(10,2) unsigned如果发生全额退款退款不会在order_info里留痕而是在order_action里记录操作。所以做退款统计必须以order_action为准不能只看money_paid。4.3 销售统计的多表联查字段冗余的意义月度销售统计需要联查order_info、order_goods和category。这里体现order_goods冗余存储商品名称和价格的价值——查询不需要回溯goods表SELECT c.cat_name, COUNT(DISTINCT og.order_id) AS order_count, SUM(og.goods_number) AS goods_count, SUM(og.goods_price * og.goods_number) AS sales_amount FROM order_goods og JOIN order_info oi ON oi.order_id og.order_id JOIN goods g ON g.goods_id og.goods_id JOIN category c ON c.cat_id g.cat_id WHERE oi.pay_status 2 AND oi.order_status ! 3 AND oi.add_time UNIX_TIMESTAMP(CURRENT_DATE - INTERVAL 30 DAY) GROUP BY c.cat_id ORDER BY sales_amount DESC;UNIX_TIMESTAMP(CURRENT_DATE - INTERVAL 30 DAY)直接生成 30 天前零点的时间戳避免在应用层做日期转换。GROUP BY c.cat_id而不是GROUP BY c.cat_name是因为分类可能重名。这个联查返回的是主分类维度的销售数据如果商品挂在多个goods_cat分类下这里会全部归到主分类需要业务方确认口径。5. 用数据字典反向核对线上库information_schema 与 ALTER TABLE 实战数据字典不只是用来查字段含义的更实用的场景是拿它反向核对线上数据库结构。ECSHOP v3.0 到 v3.6 之间发生了不少类型修正其中product.product_number由smallint(5)修正为mediumint(8)这是库存量大的站点必须关注的结构变更。核对线上库最有效的手段是用information_schema直接对比。先写一段核对脚本把线上库中的表结构与数据字典里记录的字段类型做差异比对SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA ecshop AND TABLE_NAME IN (category, goods, product, order_info, users) ORDER BY TABLE_NAME, ORDINAL_POSITION;执行结果会列出每张表当前线上实际的字段类型、是否允许 NULL、默认值。拿着这份结果和 docx 里记录的字段类型做差值比对重点关注三类差异一是unsigned是否保留二是varchar长度是否被改短三是默认值是否被改动。字段类型被改动最常见的原因是为了迁库或同步比如开发环境用 SQLite 导入导出tinyint(1)会被转成bool再导回 MySQL 就变成tinyint(1)但丢了unsigned。对比发现差异后用ALTER TABLE修正。以product.product_number为例ALTER TABLE product MODIFY COLUMN product_number mediumint(8) unsigned NOT NULL DEFAULT 0 COMMENT 货品库存数量;MODIFY COLUMN会重建该字段所以必须完整写出字段类型、是否允许 NULL、默认值和注释。这里有个关键点ALTER TABLE执行时会对表加元数据锁大表会在高峰期造成阻塞。推荐的做法是先在从库上执行pt-online-schema-change或者 MySQL 8.0 的ALGORITHMINPLACE在线变更。另一个容易被忽略的地方是goods_weight类型为decimal(10,3) unsigned如果迁移时改成decimal(10,2)重量精度会丢失导致运费计算偏差。核对字典时对于decimal类型的精度要特别敏感。除了字段类型索引也是核对重点。数据字典里标注了Y的字段通常是查询高频字段比如goods.cat_id、order_info.user_id、order_goods.order_id。迁移后常见的问题是索引丢失用SHOW INDEX FROM order_goods;检查order_id是否有索引。没有索引会导致后台订单详情页在数据量大时慢查询。最后一类容易出问题的字典差异是默认值。give_integral和rank_integral在 v3.0 中的默认值是-1表示不单独设置积分则按商品价格自动计算。如果迁移后默认值变成了0会导致所有新商品不送积分。核对字典时对带特殊默认值的字段-1、0、1逐个对照这类小差异往往不报错但直接影响业务结果。本文还有配套的精品资源点击获取
返回列表