数据字典
共 19 张表,表前缀统一 tw_(安装时可改)。所有时间字段都是 int 时间戳,不是 datetime;所有"软状态"用 tinyint 存 1/0。
一、表关系总览
二、tw_sort — 栏目
树形结构,pid=0 为一级。同一语言下 slug 唯一。
| 字段 | 类型 | 说明 |
|---|---|---|
id | int unsigned | 主键 |
lang | varchar(10) | 语言代码,如 en |
pid | int unsigned | 上级栏目 ID,0 = 一级 |
type | varchar(10) | list 列表 / page 单页 / link 外链 |
name / subname | varchar | 栏目名称 / 副标题 |
slug | varchar(100) | URL 别名,与 lang 组成唯一键 |
ico / pic | varchar(255) | 栏目缩略图 / 栏目大图(相对路径) |
description | text | 栏目简介,列表页顶部展示 |
seo_title / seo_keywords / seo_description | varchar | SEO 三要素 |
canonical | varchar(255) | 自定义 canonical,留空则自指 |
trans_key | varchar(64) | 跨语言配对键(hreflang 用) |
template_list | varchar(50) | 列表页模板名,默认 lists |
template_detail | varchar(50) | 详情页模板名,默认 detail |
template_page | varchar(50) | 单页模板名,默认 page |
link | varchar(255) | 外链地址(type=link 时使用) |
status | tinyint(1) | 1 显示 / 0 隐藏 |
sort | int | 排序,越小越前 |
createtime / updatetime | int | 创建 / 更新时间戳 |
三、tw_content — 内容
产品、新闻、案例、单页正文都用这一张表,靠 sort_id 区分。
| 字段 | 类型 | 说明 |
|---|---|---|
id | int unsigned | 主键 |
lang | varchar(10) | 语言代码 |
sort_id | int unsigned | 所属栏目 |
title / subtitle | varchar | 标题 / 副标题(产品常用作一句话卖点) |
slug | varchar(120) | URL 别名,与 lang 组成唯一键 |
ico | varchar(255) | 缩略图(列表卡片用) |
images | text | 图集,JSON 数组字符串,如 ["/uploads/a.jpg"] |
summary | varchar(500) | 摘要,列表卡片与 meta description 兜底 |
content | mediumtext | 正文,原始 HTML(模板输出需 |html_entity_decode|raw) |
tags | varchar(255) | 标签,逗号分隔,如 cnc,aluminum |
seo_title / seo_keywords / seo_description | varchar | SEO 三要素 |
canonical | varchar(255) | 自定义 canonical |
noindex | tinyint(1) | 1 = 输出 noindex,follow |
attr | varchar(50) | 推荐属性,逗号分隔:recommend / hot / new |
author / source | varchar | 作者 / 来源(新闻用) |
meta | varchar(1000) | 扩展属性(v1.0.43 起)。栏目没有自定义字段时 = 后台内容表单「扩展」文本框的原文(如价格卡里的 ¥4800);栏目定义了自定义字段时 = JSON {"字段键":"值"},后台「扩展」框自动隐藏。模板取 {$list.meta} 拿原文,取 {$list.字段键} 拿解析后的单项 |
views | int unsigned | 浏览量,可用于"热门"排序 |
price / unit | decimal(12,2) / varchar | 参考价 / 单位(如 pcs) |
model_no / moq | varchar | 产品型号 / 最小起订量 |
trans_key | varchar(64) | 跨语言配对键 |
status | tinyint(1) | 1 发布 / 0 草稿 |
sort | int | 排序 |
publish_time | int unsigned | 发布时间,列表默认按它倒序 |
createtime / updatetime | int | 创建 / 更新 |
先走栏目自定义字段:栏目管理 → 字段,定义几个字段键,值会自动存进 meta(JSON),
模板用 {$list.字段键} 就能取到,全程不用改表、不用改程序。详见
栏目自定义字段。
只有两种情况才需要真的加列:① 要在 {tw:list} 里做排序 / 筛选 / 统计(meta 里的 JSON 不方便查);
② 值会很长(超过 meta 的 1000 字上限)。加列后列表标签仍拿不到——需要把字段加进
Tags::lists() 的 field() 白名单,详情页 $content 则是自动全字段。
四、tw_language — 语言
| 字段 | 类型 | 说明 |
|---|---|---|
code | varchar(10) | 语言代码,决定 URL 前缀:en(默认语言无前缀)、zh-cn |
name / native_name | varchar(50) | 显示名 / 本地名(如 中文) |
region | varchar(20) | hreflang 区域码,如 en-US、zh-CN,同时输出到 <html lang> |
dir | varchar(3) | ltr 或 rtl(阿拉伯语/希伯来语) |
is_default | tinyint(1) | 默认语言:URL 不带前缀,且是 hreflang 的 x-default |
sort / status | int / tinyint | 排序 / 启用状态 |
新增语言时注意把其他语言的 is_default 置 0,否则 URL 前缀逻辑会混乱。
五、tw_inquiry — 询盘
| 字段 | 类型 | 说明 |
|---|---|---|
no | varchar(32) | 询盘编号,如 TWQ20260101001,唯一 |
lang | varchar(10) | 提交时的语言 |
name / email / phone / whatsapp | varchar | 客户联系方式 |
company / country | varchar | 公司 / 国家 |
subject / message | varchar / text | 主题 / 正文 |
product_id / product_name | int / varchar | 关联产品(名称是快照,产品改名不影响历史记录) |
items | text | 询价车明细 JSON |
amount / currency | decimal / varchar | 合计金额 / 币种 |
source_url / ip / ua | varchar | 来源页、IP、浏览器 UA(反查刷单用) |
status | varchar(20) | new / read / quoted / replied / won / closed |
priority | tinyint | 1 高 / 2 中 / 3 低 |
remark | text | 内部跟进备注(客户端看不到) |
admin_id | int | 最后处理人 |
createtime / updatetime | int | 提交 / 更新 |
六、tw_cart — 询价车
不是购物车(不付款),是"客户攒一批产品,一起发询盘"。
| 字段 | 类型 | 说明 |
|---|---|---|
sid | varchar(64) | 浏览器会话 ID(cookie 里的标识,非会员体系) |
lang | varchar(10) | 加购时的语言 |
product_id / product_name / product_url | int / varchar | 产品信息快照 |
price / qty / currency | decimal / int / varchar | 参考价、数量、币种 |
七、tw_price — 阶梯价
| 字段 | 类型 | 说明 |
|---|---|---|
product_id | int | 产品 ID |
lang | varchar(10) | 语言(不同语言可独立定价) |
min_qty | int unsigned | 起订量(该档从这里开始) |
price | decimal(12,2) | 单价 |
unit | varchar(20) | 单位 |
八、tw_attachment — 附件
| 字段 | 类型 | 说明 |
|---|---|---|
name | varchar(255) | 原始文件名 |
path / url | varchar | 相对路径 / 完整访问地址 |
ext / mime / size | varchar / int | 扩展名 / MIME / 字节数 |
width / height | int | 图片宽高(用于输出 width/height 防 CLS) |
webp | varchar(255) | WebP 版本路径 |
hash | varchar(64) | 文件哈希,用于查重 |
admin_id | int | 上传者 |
九、tw_setting — 设置
极简键值表,skey 唯一。sgroup 只用于后台分组展示:base / seo / contact / mail / system。
| 字段 | 类型 | 说明 |
|---|---|---|
skey | varchar(64) | 键,如 site_name、phone |
svalue | text | 值(统一存字符串,布尔用 1/0) |
sgroup | varchar(30) | 分组 |
代码里读:tw_setting('phone') 或 Settings::get('phone');写:Settings::set('phone', '...')(会自动清缓存)。
十、tw_admin / tw_admin_group / tw_admin_rule — 权限
tw_admin 管理员
| 字段 | 说明 |
|---|---|
group_id | 所属角色组 |
username / password / salt | 账号 / 密码哈希 / 盐(不要手工改这三列,用后台改密码) |
nickname / email / avatar | 资料 |
login_ip / login_time | 最后登录信息 |
login_fail / lock_until | 连续失败次数 / 锁定到期时间(防爆破) |
status | 1 启用 / 0 禁用 |
tw_admin_group 角色
| 字段 | 说明 |
|---|---|
name | 角色名,如"运营" |
rules | 权限节点 ID 列表(逗号分隔),* 表示全部权限 |
tw_admin_rule 权限节点
| 字段 | 说明 |
|---|---|
pid | 上级节点,0 = 一级菜单 |
type | menu 菜单 / button 按钮级权限 / link 外链 |
name | 权限标识,如 content/index |
path | URL 路径(控制器/方法) |
plugin | 所属插件名,空表示核心功能 |
菜单定义在 app/admin/menu.php,进入后台时按版本号自动同步到这张表。
十一、tw_redirect — 跳转
| 字段 | 说明 |
|---|---|
from_path | 旧地址路径,如 /old-page.html(不带域名) |
to_path | 新地址,站内路径或完整外链 |
code | 301 永久 / 302 临时 |
hits | 命中次数,用来判断哪些老链接还有流量 |
status | 1 启用 / 0 停用 |
十二、tw_plugin — 插件状态
| 字段 | 说明 |
|---|---|
name | 插件目录名,唯一 |
title / version / author / description | 从 plugin.json 同步 |
status | 1 启用 / 0 停用 |
config | 插件配置 JSON |
十三、tw_currency — 货币
多货币展示用,前台按 rate 换算,基准货币的 rate 固定 1.000000。
| 字段 | 说明 |
|---|---|
code | 货币代码,如 USD / EUR |
name / symbol | 名称与符号,如 US Dollar / $ |
rate | 对基准货币的汇率,decimal(12,6) |
status / sort | 启用状态与排序 |
十四、tw_visit / tw_visit_page — 访客统计
两张表配合使用:tw_visit 一个会话一行(聚合数据,后台列表读它);
tw_visit_page 一次页面浏览一行(明细,后台「访问详情」的浏览轨迹读它)。写入全部由
Visit.php 完成,sid 唯一键保证同一会话只更新不新建。
tw_visit — 访客会话
| 字段 | 类型 | 说明 |
|---|---|---|
sid | varchar(32) | 会话 ID(唯一键),前端 sessionStorage 存 30 分钟 |
vid | varchar(32) | 访客 ID,前端 cookie 存 1 年,用于识别回访 |
ip | varchar(45) | 兼容 IPv6 |
ua | varchar(500) | 原始 User-Agent |
device / browser / os | varchar | 由 UA 解析:desktop / mobile / tablet |
lang | varchar(10) | 访问时的语言站点,如 en |
referer / ref_host | varchar | 来源页与来源域名(Google / Facebook 判断依据) |
landing / last_url | varchar(500) | 落地页与最后停留页 |
pages / duration | int | 浏览页数、累计停留秒数 |
first_time / last_time | int | 本次会话开始 / 最后活动时间 |
tw_visit_page — 页面浏览明细
| 字段 | 类型 | 说明 |
|---|---|---|
visit_id | int | 关联 tw_visit.id |
sid / vid | varchar(32) | 冗余存一份,便于按会话/访客直查 |
url / title | varchar | 页面路径与标题(前台传什么存什么) |
sort_id / content_id | int | 能对应到栏目/内容时填入,用于页面热度聚合 |
referer | varchar(500) | 进入本页时的来源 |
enter_time / leave_time / duration | int | 进入、离开时间与停留秒数 |
默认保留 90 天(站点设置 visit_keep_days),过期记录在写入时按概率触发清理。
数据量大时可把保留天数调小,或定期 DELETE FROM tw_visit WHERE first_time < UNIX_TIMESTAMP(NOW() - INTERVAL 30 DAY),
再执行 OPTIMIZE TABLE 回收空间。
十五、tw_sort_field — 栏目自定义字段定义
v1.0.44 新增。一条记录 = 栏目表单上的一个输入框。值不存在这张表,存在 tw_content.meta 的 JSON 里;本表只定义"有哪些字段、叫什么、什么类型"。
| 字段 | 类型 | 说明 |
|---|---|---|
sort_id | int unsigned | 字段挂在哪个栏目上(栏目删了要一起清) |
name | varchar(50) | 字段名称,后台表单上显示的中文标签,如「主按钮链接」 |
fkey | varchar(50) | 字段键,模板里用的变量名,如 cta1_url。同栏目内唯一,只允许小写字母/数字/下划线,留空时后台按中文名自动生成 cf_拼音 |
type | varchar(10) | text 单行 / textarea 多行 / select 下拉 / switch 开关 |
options | varchar(500) | 下拉选项,一行一个(只对 select 有效)。存的是选项文字本身,不是序号 |
defval | varchar(200) | 默认值,新增内容时预填 |
sort / status | int / tinyint(1) | 排序 / 1 启用 0 停用(停用后表单不再渲染该输入框,已存的值保留) |
createtime / updatetime | int | 时间戳 |
同栏目 fkey 不重复是靠后台保存时校验的(Sort::fieldSave()),手工 INSERT 绕过去就会出现两个同名键、后者覆盖前者。
另外系统保留名(title / summary / content / meta / url / lines / cf 等)会被直接拒绝。
十六、tw_slide_group — 幻灯片分组
一个分组 = 前台一个位置的图集(首页轮播、内页 banner、优势配图…)。靠 code 而不是 id 调用,换站迁移时 id 会变、code 不会。
| 字段 | 类型 | 说明 |
|---|---|---|
name | varchar(80) | 分组名称,后台列表显示用 |
code | varchar(60) | 调用标识(唯一键),模板里写 {tw:slide group="home"} |
remark | varchar(255) | 用途说明 / 尺寸建议,只给编辑看 |
width / height | int unsigned | 建议尺寸(像素),后台提示用,程序不做校验 |
status / sort | tinyint(1) / int | 1 启用 / 0 停用;排序 |
十七、tw_slide — 幻灯片条目
一张图一条记录。多语言用 lang 区分;当前语言下该分组没有启用条目时,整组回退到默认语言——所以只做一个语言的站不用为每种语言重复上传同一张图。
| 字段 | 类型 | 说明 |
|---|---|---|
group_id | int unsigned | 所属分组 |
lang | varchar(10) | 语言代码 |
title / subtitle / summary | varchar | 主标题 / 副标题 / 描述 |
image / image_mobile | varchar(500) | 电脑图 / 手机图(手机图留空则用电脑图) |
video | varchar(500) | 视频地址(mp4),可空;有值时模板可用 {$slide.has_video} 判断 |
url / target / btn_text | varchar | 跳转链接 / _self·_blank / 按钮文字 |
start_time / end_time | int unsigned | 定时上下线窗口,0 = 不限;查询时按当前时间过滤 |
status / sort | tinyint(1) / int | 1 启用 / 0 停用;排序(同 sort 按 id 升序) |
标签用法见 标签与函数 → slide / slidegroup;栏目还可以绑一个默认分组(tw_sort.slide_group 存 code),这样同一套内页模板能按栏目出不同 banner。
十八、常用 SQL
看某个栏目下有多少条内容
SELECT s.name, COUNT(c.id) AS total
FROM tw_sort s
LEFT JOIN tw_content c ON c.sort_id = s.id AND c.status = 1
WHERE s.lang = 'en'
GROUP BY s.id
ORDER BY s.sort;
本月询盘按国家统计
SELECT country, COUNT(*) AS cnt
FROM tw_inquiry
WHERE createtime >= UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-01'))
GROUP BY country
ORDER BY cnt DESC;
查没填 SEO 描述的内容
SELECT id, title FROM tw_content
WHERE status = 1 AND (seo_description = '' OR seo_description IS NULL);
批量补 trans_key(按 slug 配对)
UPDATE tw_content SET trans_key = CONCAT('c-', slug)
WHERE trans_key = '' AND slug != '';
看今天来了多少人、从哪些渠道来
SELECT IF(ref_host = '', 'direct', ref_host) AS channel,
COUNT(*) AS sessions,
SUM(pages) AS pv,
ROUND(AVG(duration)) AS avg_sec
FROM tw_visit
WHERE first_time >= UNIX_TIMESTAMP(CURDATE())
GROUP BY channel
ORDER BY sessions DESC;
哪几页留人最久(近 7 天)
SELECT url, COUNT(*) AS pv, ROUND(AVG(duration)) AS avg_sec
FROM tw_visit_page
WHERE enter_time >= UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY)
GROUP BY url
HAVING pv >= 5
ORDER BY avg_sec DESC
LIMIT 20;
清理 30 天前的访客数据
DELETE p FROM tw_visit_page p
JOIN tw_visit v ON v.id = p.visit_id
WHERE v.first_time < UNIX_TIMESTAMP(NOW() - INTERVAL 30 DAY);
DELETE FROM tw_visit WHERE first_time < UNIX_TIMESTAMP(NOW() - INTERVAL 30 DAY);
查某栏目定义了哪些自定义字段
SELECT f.name, f.fkey, f.type, f.options, f.defval
FROM tw_sort_field f
JOIN tw_sort s ON s.id = f.sort_id
WHERE s.slug = 'home-hero' AND f.status = 1
ORDER BY f.sort;
取内容里某个自定义字段的值(meta 是 JSON)
SELECT id, title,
JSON_UNQUOTE(JSON_EXTRACT(meta, '$.badge')) AS badge
FROM tw_content
WHERE sort_id = 20006 AND status = 1;
任何 UPDATE / DELETE 前先 mysqldump 单表;批量操作先用 SELECT 查一遍影响范围(把 UPDATE 改成 SELECT COUNT(*) 试跑)。