群晖NAS使用Docker部署WordPress

搭建网站的工具和过程降到低门槛和低成本,WordPress使用小型文件数据库SQLite组建Sandbox系统模拟

有什么用

创建工作室/公司网站, 网店; 发布文章, 分享经验等

怎么用

1
2
3
4
5
6
7
8
9
10
11
version: '3'
services:
wordpress:
image: soulteary/sqlite-wordpress
restart: always
ports:
- 8104:80
volumes:
- ./:/var/www/html
#用于对接兰空图床批量上传的图库, CSV中含文件名对应到产品
- /volume2/KingchuxingSSD512G/MacBookPro_Skitch/storage/app/uploads/2026/05:/var/www/html/wp-content/uploads/lskyupload202605

后来验证: 这种方式最后用于sandbox的建设, 因为网店再导入商品和图片后下单会报错, 切换使用官方版本后解决; 用这种轻量级的sandbox好处有: 随便安装插件等, 不怕污染了主数据库(数据的清理干净很麻烦, 耗时, 易出错)

演示/实践 https://w.carlzeng.com

相关内容

WooCommerce导入产品

安装插件: WebToffee Import Export

image-20260511092148886

如何导入产品照片?

需求: 导入所有几千张照片, 然后关联到产出

WP的SQLlite数据库文件在哪里?

WordPress 用 SQLite 时(官方插件 sqlite-database-integration 或 wp-sqlite-db),数据库文件默认在:

1
wp-content/database/.ht.sqlite

最后图片还是要使用WP原始的图库功能;

上传到图库, 但由于兰空图床的目录使用的docker的映射方式, 被我直接映射到docker目录

/volume2/KingchuxingSSD512G/MacBookPro_Skitch/storage/app/uploads/2026/05

这样; Addd From Server Reloaded 就能加载到这些1170+张照片了

导入后: 图库

image-20260512231637885

故障: 很多可以在兰空中看到的照片, 无法访问, 不知为何(是否和docker wordpress映射 并上传后有关? )
https://img.carlzeng.com:3/i/2026/05/O1CN01bBq8Wx1smZl0yPj7B_!!4611686018427385985-53-fleamarket.jpg

O1CN01fP0ICM1smZqe9QMrt_!!4611686018427385985-53-xy_item.jpg

成功链接到使用免费版的WebToffee Import Export; 导入所有产品和相关联的图片(先导入到图库中)

照片可以导入格式:
/wp-content/uploads/O1CN013Dpd011smZsfIaiIb_4611686018427385985-53-xy_item.jpg

可以导入多张照片, 用 , 空格分开

-5:09pm- 用,分隔即可

    修改全部的csv文件, 转化
    | 为 ,
    !! 去除
    格式如上

优化docker-compose 的目录映射 添加:ro, 确保兰空图床中的文件, 不要受影响/删除

导入详细商品目录

用DBeaver, 导入wp_posts表数据, 和 wp_postmeta表数据

绝大部分数据成功/轻松导入.

发现问题:

有的货品的SKU和价格丢失

image-20260520230621810

修复步骤:

  1. 查询到sku 或 price为空的行, 一共60个; 复制出p.ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SELECT 
p.ID as product_id,
p.post_title as product_name,
sku.meta_value as sku,
price.meta_value as price
FROM
wp_posts p
LEFT JOIN
wp_postmeta sku ON p.ID = sku.post_id AND sku.meta_key = '_sku'
LEFT JOIN
wp_postmeta price ON p.ID = price.post_id AND price.meta_key = '_price'
WHERE
p.post_type = 'product'
AND p.post_status = 'publish'
AND (
sku.meta_value IS NULL OR sku.meta_value = ''
OR price.meta_value IS NULL OR price.meta_value = ''
);
  1. 到Sandbox正常的数据库中提取这些wp_postmeta相关的行数据
1
2
3
4
5
6
7
8
SELECT 
post_id,
meta_key,
meta_value
FROM
wp_postmeta
WHERE
post_id IN (9, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338)
  1. 使用DBeaver导出这些数据, 然后目标选择到MariaDB的wp_postmeta 表,

    注意: 不要输出meta_id, 列, 不然会存在PK冲突的问题, 导致数据无法正常导入

    导入最后一步选择 Insert IGNORE

至此60个有显示故障的商品wp_postmeta被重新导入修复完毕!

下一步: 继续设置商品的分类和品牌, 标签等

解锁媒体库目前上传限制2MB

媒体库目前上传限制时2MB, 如何改成4M?

docker-compose中添加

1
2
3
4
WORDPRESS_CONFIG_EXTRA: |
@ini_set( 'upload_max_filesize', '4M' );
@ini_set( 'post_max_size', '4M' );
@ini_set( 'max_execution_time', '300' );

无法解决问题, 媒体上传限制还是2M

1
2
@ini_set( 'upload_max_size' , '4M' );
@ini_set( 'post_max_size', '4M');

解决办法: 新建一个插件

1
2
3
4
5
6
7
8
9
<?php
/* Plugin Name: Increase Upload Limit */

add_filter( 'upload_size_limit', 'b5f_increase_upload' );

function b5f_increase_upload( $bytes )
{
return 4194304; // 4m; 5m=5*1024*1024
}

把这个文件保存, 然后压缩成.zip文件; 比如: increase-upload-limit.zip

然后在安装插件页面上传, 启用, 解决!

而且编辑是动态的, 也就是说 想要修改这个限制; 直接ssh过去服务器中, 修改这个值; 立即生效, 简单直接.

image-20260531103911290

在wordpress 根目录 创建一个新的php.ini文件

使用代码或文本编辑器,将以下代码添加到现有或新的php.ini文件中:

1
2
3
upload_max_filesize = 5M 
post_max_size = 5M
memory_limit = 128M

本小节感谢: WordPress上传限制,最大上传文件大小,最大上传限制 文字教程

如何批量导入MD博客文章

插件: Import Markdown 似乎只能单独, 单个文件的导入?

各种研究, 最后在一个博主的站点上找到了解决方案, 用一个python脚本 一键批量上传目录中441篇文章到WordPress

简直太高效了, 分享源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import os  # 用来遍历文件路径
import sys
import ssl
ssl._create_default_https_context = ssl._create_unverified_context


"""
需要安装如下pip包
pip3 install python-frontmatter
pip3 install markdown
pip install 'Markdown'
pip3 install python-wordpress-xmlrpc
"""
# 1 导入frontmatter模块
import frontmatter

# 2 导入markdown模块
import markdown

# 3 导入wordpress_xmlrpc模块
from wordpress_xmlrpc import Client, WordPressPost
from wordpress_xmlrpc.methods.posts import NewPost


def make_post(filepath, metadata):
"""
make a WordPressPost for Client call
:param filepath: 要发布的文件路径
:param metadata: 字典类型
包括 metadata['category']: 文章分类
metadata['tag']: 文章标签
metadata['status']: 有publish发布、draft草稿、private隐私状态可选
:return WordPressPost: if success
None: if failure
"""
filename = os.path.basename(filepath) # 例如:test(2021.11.19).md
filename_suffix = filename.split('.')[-1] # 例如:md
filename_prefix = filename.replace('.' + filename_suffix, '') # 例如:test(2021.11.19);注意:这种替换方法要求文件名中只有一个".md"

# 目前只支持 .md 后缀的文件
if filename_suffix != 'md':
return None

# 1 通过frontmatter.load函数加载读取文档里的信息,包括元数据
post_from_file = frontmatter.load(filepath)

# 2 markdown库导入内容
post_content_html = markdown.markdown(post_from_file.content, extensions=['markdown.extensions.fenced_code'])
post_content_html = post_content_html.encode("utf-8")

# 3 将本地post的元数据暂存到metadata中
metadata['title'] = filename_prefix # 将文件名去掉.md后缀,作为标题
# metadata['slug'] = metadata['title'] # 别名
metadata_keys = metadata.keys()
# 如果post_from_file.metadata中的属性key存在,那么就将metadata[key]替换为它
for key in metadata_keys:
if key in post_from_file.metadata: # 若md文件中没有元数据'category',则无法调用post.metadata['category']
metadata[key] = post_from_file.metadata[key]

# 4 将metadata中的属性赋值给post的对应属性
post = WordPressPost() # 要返回的post
post.content = post_content_html
post.title = metadata['title']
# post.slug = metadata['slug']
post.post_status = metadata['status']
post.terms_names = {
'category': metadata['category'],
'post_tag': metadata['tag']
}
post.comment_status = 'open' # 开启评论
return post


def push_post(post, client):
"""
上传post到WordPress网站
:param post: 要发布的文章(WordPressPost类型),由make_post函数得到
:param client: 客户端
:return True: if success
"""
return client.call(NewPost(post))


def get_filepaths(path):
"""
如果path是目录路径,递归遍历path目录下的所有文件,将所有文件路径存入filepaths
如果path是文件路径,直接将单个文件路径存入filepaths
:param path: 你要上传的目录路径或文件路径(绝对路径)
:return filepaths: 该目录下的所有子文件或单个文件的绝对路径
None: wrong path
"""
filepaths = []
if os.path.isdir(path): # 当前路径是目录
for now_dirpath, child_dirnames, child_filenames in os.walk(path):
# ========== 新增:排除包含 secret 的目录 ==========
if 'secret' in now_dirpath.lower(): # 不区分大小写:Secret / SECRET 都排除
continue

for filename in child_filenames:
filepath = os.path.join(now_dirpath, filename)
filepaths.append(filepath)
filepaths.sort() # <-- 按英文字母 / 数字顺序排序(就加这一行)
return filepaths
elif os.path.isfile(path): # 当前路径是文件
return [path]
else: # wrong path
return None


if __name__ == '__main__':
# User Configuration
path = '/Users/carlzeng/App/blog/source/_posts' # e.g. D:/PythonCode/post-wordpress-with-markdown/doc
domain_list = [
'https://wp.carlzeng.com:3/'
] # e.g. https://example.com(配置了SSL证书就用https,否则用http)
username = '*******'
password = '*******'

for domain in domain_list:
# Optional Configuration
post_metadata = {
'category': ['未分类'], # 文章分类
'tag': ['手柄一修哥'], # 文章标签
'status': 'publish' # 可选publish发布、draft草稿、private隐私状态
}

# Start Work
print('----------------------------------------------START----------------------------------------------')
filepaths = get_filepaths(path)
if filepaths is None:
print('FAILURE: wrong path')
sys.exit(1)

client = Client(domain + '/xmlrpc.php', username, password) # 客户端

md_cnt = 0
all_cnt = len(filepaths)
process_number = 0
failpaths = [] # 存储上传失败的文件路径
for filepath in filepaths:
process_number = process_number + 1
filename = os.path.basename(filepath)
try:
# 把可能报错的逻辑全部放进 try
post = make_post(filepath, post_metadata)

if post is not None:
push_post(post, client)
md_cnt = md_cnt + 1
print('Process number: %d/%d SUCCESS: Push "%s" completed!' % (process_number, all_cnt, filename))
else:
failpaths.append(filepath)
print('Process number: %d/%d WARNING: Can\'t push "%s" because it\'s not Markdown file.' % (
process_number, all_cnt, filename))
except Exception as e:
# 捕获解析/发布失败,不中断程序
failpaths.append(filepath)
print('Process number: %d/%d ERROR: Failed to push "%s" | Reason: %s' % (process_number, all_cnt, filename, str(e)))

print('-----------------------------------------------END-----------------------------------------------')
print('SUCCESS: %d files have been pushed to {0}.'.format(domain) % md_cnt)

if len(failpaths) > 0:
print('WARNING: %d files haven\'t been pushed to {0}.'.format(domain) % len(failpaths))
print('\nFailure to push these file paths:')
for failpath in failpaths:
print(failpath)

感谢作者: 一键上传markdown 格式文章到wordpress

修改的点有:

  1. 第137行 post = make_post(filepath, post_metadata) 这个需要try catch
  2. 添加排序功能: 目前加载目录下的文件没有排序, 请按文件的英文字母顺序排序
  3. 请排除目录名中包含secret的目录

运行后输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
----------------------------------------------START----------------------------------------------
Process number: 8/418 SUCCESS: Push "1099-MISC是什么-CarlZeng.md" completed!
Process number: 9/418 SUCCESS: Push "A-feature-in-Netsuite-Reports-Financial-Balance-Sheet-CarlZeng.md" completed!
Process number: 10/418 SUCCESS: Push "AI脸部识别,人脸比对,人脸识别-CarlZeng.md" completed!
Process number: 11/418 SUCCESS: Push "AJAX提交数据时-中文处理-以及js-url-中文处理-CarlZeng.md" completed!
Process number: 12/418 SUCCESS: Push "Bar-codes-in-NetSuite-Saved-Searches-transport-reprint-活用NetSuite搜索页面显示可扫描条码-CarlZeng.md" completed!

...

Process number: 417/418 SUCCESS: Push "高手写出的是天使,而新手写的,可能是魔鬼!-Javascript这样的脚本语言,由于太灵活-CarlZeng.md" completed!
Process number: 418/418 SUCCESS: Push "高效的服务端生成QRCODE二维码方案-Docker搭建.md" completed!
-----------------------------------------------END-----------------------------------------------
SUCCESS: 411 files have been pushed to https://wp.carlzeng.com:3/.
WARNING: 7 files haven't been pushed to https://wp.carlzeng.com:3/.

Failure to push these file paths:
/Users/carlzeng/App/blog/source/_posts/.DS_Store

遇到的bug/enhancement:

  1. 手动完成文章所有分类,

  2. 标签和照片无法导入; (用python, 等待更新, 提取这些信息到WP)

  3. 下一步:

Woocommerce下单错误

点击下单报错:

image-20260514101102522

1
Uncaught Error: Call to a member function get_total() on false in /var/www/html/wp-content/plugins/woocommerce/includes/gateways/cod/class-wc-gateway-cod.php:314\nStack trace:\n#0 /var/www/html/wp-content/plugins/woocommerce/src/StoreApi/Legacy.php(56): WC_Gateway_COD->process_payment(4131)\n#1 /var/www/html/wp-includes/class-wp-hook.php(341): Automattic\\WooCommerce\\StoreApi\\Legacy->process_legacy_payment(Object(Automattic\\WooCommerce\\StoreApi\\Payments\\PaymentContext), Object(Automattic\\WooCommerce…wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters(NULL, Array)\n

image-20260514101017933

wp_woocommerce_order_items 等等, 这些表都有的

image-20260514153126661

切换到官方Docker Image

Production生成环境

1
2
mkdir wordpresshtml && mkdir db 
vi docker-compose.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
services:

wordpress:
image: wordpress
restart: always
ports:
- 8106:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: example user
WORDPRESS_DB_PASSWORD: example pass
WORDPRESS_DB_NAME: wordpress
volumes:
- ./wordpresshtml:/var/www/html

db:
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: example user
MYSQL_PASSWORD: example pass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- ./db:/var/lib/mysql

volumes:
wordpress:
db:

chown mysql /volume2/KingchuxingSSD512G/docker/compose/wordpress2/db -R

chmod +777 /volume2/KingchuxingSSD512G/docker/compose/wordpress2/db -R

chown mysql /volume2/KingchuxingSSD512G/docker/compose/wordpress2/wordpresshtml -R

chmod +777 /volume2/KingchuxingSSD512G/docker/compose/wordpress2/wordpresshtml -R

必须要运行这个目录权限, 否者就是各种报错.

woocommerce Connection Information

upload a WooCommerce .zip file and install the plugin on the local host. Every time I click install button, WordPress is asking for connection for FTP

Add this line in wp-config.php

1
define( 'FS_METHOD', 'direct' );
1
2
3
4
//define('WP_HOME','https://w.carlzeng.com');  
define('WP_SITEURL','https://w.carlzeng.com');

define('WP_CONTENT_URL', '/wp-content');

还是决定切换到MariaDB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
services:

wordpress:
image: wordpress
restart: always
ports:
- 8106:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: example user
WORDPRESS_DB_PASSWORD: example pass
WORDPRESS_DB_NAME: wordpress
volumes:
- ./wordpresshtml:/var/www/html

db:
image: mariadb:jammy
restart: always
ports:
- 8107:3306
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: example user
MYSQL_PASSWORD: example pass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- ./db:/var/lib/mysql

volumes:
wordpress:
db:

初始化安装好wordpress, 只需要这些表:

image-20260515081045699

  1. 手工导入wp_posts (直接从数据库到数据库)

  2. 然后用csv导入wp_postmeta

裁剪您的图片时发生错误。

原来是操作不了PNG; JPG可以的, 无所谓, 无伤大雅

SQL修复文章分类

步骤一: 插入wp_term_relationships

1
2
3
INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order)

select ID, 22, 0 from wp_posts where post_type = 'post' and post_content like '%netsuite%' or post_title like '%netsuite'

note: 用IGNORE支持多次执行, 会避开重复的行, 输出

1
2
3
4
5
6
7
Duplicate entry '205-22' for key 'PRIMARY'
Duplicate entry '3298-22' for key 'PRIMARY'
Duplicate entry '3306-22' for key 'PRIMARY'
Duplicate entry '3312-22' for key 'PRIMARY'
Duplicate entry '3640-22' for key 'PRIMARY'
Duplicate entry '3662-22' for key 'PRIMARY'

步骤二: 插入wp_term_taxonomy

1
2
3
4
5
6
7
8
#关键后续:更新 WordPress 分类计数器
UPDATE wp_term_taxonomy
SET count = (
SELECT COUNT(*)
FROM wp_term_relationships
WHERE wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
)
WHERE term_taxonomy_id = 22

添加剩余的文章都分类到: 一修哥工作室

1
2
3
4
5
6
7
8
9
10
11
12
INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order)
select ID, 23, 0 from wp_posts where post_type = 'post' and ID not in (
select object_id from wp_term_relationships
)

UPDATE wp_term_taxonomy
SET count = (
SELECT COUNT(*)
FROM wp_term_relationships
WHERE wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
)
WHERE term_taxonomy_id = 23

导入部分硬件工作记录/成功案例

以准备好成功案例的md聊天记录导出, 导入展现. 此md来源为: 闲鱼APP中聊天记录导出

批量, 加载目录下md文件时, 需要按md文件的创建时间顺序来加载

Sanbox测试得知: 待修复 视频,如何支持在线点击查看?
md文件中如何显示视频播放器?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import os
import re

# 匹配 Markdown 格式的 [视频](url地址)
pattern = r'\[\s*视频\s*\]\(\s*(https?://[^\s\)]+|[^\s\)]+\.mp4|[^\s\)]+)\s*\)'


def get_file_creation_time(file_path):
"""兼容多系统的获取文件创建时间函数"""
stat = os.stat(file_path)
try:
# macOS / FreeBSD 优先使用 birthtime
return stat.st_birthtime
except AttributeError:
# Windows 上 ctime 即为创建时间;Linux 上为元数据修改时间
return stat.st_ctime


def convert_md_files_by_order():
current_dir = os.getcwd()

# 1. 过滤出当前目录下所有的 .md 文件
md_files = [f for f in os.listdir(current_dir) if f.endswith(".md")]

# 2. 【核心修改】将文件列表按照创建时间进行“升序排序”(从旧到新)
# 如果想改为降序(最新创建的先处理),可以加上 reverse=True
md_files.sort(key=lambda x: get_file_creation_time(os.path.join(current_dir, x)))

success_count = 0
print("开始按创建时间【升序】处理文件...\n" + "-" * 40)

# 3. 按排序后的顺序遍历文件
for filename in md_files:
file_path = os.path.join(current_dir, filename)

with open(file_path, "r", encoding="utf-8") as f:
content = f.read()

if re.search(pattern, content):
# 替换并强制添加前后空行激活 HTML 渲染
replaced_content = re.sub(
pattern,
r"\n\n<video src='\1' controls width='100%' height='auto' loop='loop'></video>\n\n",
content,
)

with open(file_path, "w", encoding="utf-8") as f:
f.write(replaced_content)

# 获取文件创建时间用于终端打印展示
c_time = get_file_creation_time(file_path)
from datetime import datetime

time_str = datetime.fromtimestamp(c_time).strftime(
"%Y-%m-%d %H:%M:%S"
)

print(f"[{time_str}] 成功转换: {filename}")
success_count += 1

print("-" * 40 + f"\n处理完成!共依序转换了 {success_count} 个文件。")


if __name__ == "__main__":
convert_md_files_by_order()

1
2
3
----------------------------------------------START----------------------------------          
Process number: 1/30 SUCCESS: Push "聊天记录_tbNick_n9c42_2026-05-17.md" completed!
....

对隐私/敏感信息的屏蔽保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SELECT ID, post_title, post_date 
FROM wp_posts
WHERE post_content LIKE '%13261977480%';

-- 1. 替换文章正文内容(最核心)
UPDATE wp_posts
SET post_content = REPLACE(post_content, '13261977480', '1326****480')
WHERE post_content LIKE '%13261977480%';

UPDATE wp_posts
SET post_content = REPLACE(post_content, '后北营家园一区10号楼3-130', '后北营家园一区10号(大运河果蔬便民店外)')
WHERE post_content LIKE '%后北营家园一区10号楼3-130%';

UPDATE wp_posts
SET post_content = REPLACE(post_content, 'VPN', 'V*P*N')
WHERE post_content LIKE '%VPN%';

-- 替换文章标题
UPDATE wp_posts
SET post_title = REPLACE(post_title, '_2026-05-17', '_手柄一修哥')
WHERE post_title LIKE '%_2026-05-17%';

维护商品分类

1
2
3
4
5
6
7
8
INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order)
SELECT ID, 17, 0
FROM wp_posts
WHERE post_type = 'product'
AND post_title like '%PS5%'

and ID not in (
select object_id from wp_term_relationships)

image-20260526164407478

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order)
SELECT ID, 27, 0
FROM wp_posts
WHERE post_type = 'product'
AND post_title like '%switch%'


INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id, term_order)
SELECT ID, 27, 0
FROM wp_posts
WHERE post_type = 'product'
AND post_title like '%joycon%'

#totally 43 row, success 23, duplicated

Updated rows 23;
Duplicate entry '242-27' for key 'PRIMARY'
Duplicate entry '432-27' for key 'PRIMARY'
Duplicate entry '443-27' for key 'PRIMARY'
Duplicate entry '455-27' for key 'PRIMARY'
Duplicate entry '487-27' for key 'PRIMARY'
Duplicate entry '506-27' for key 'PRIMARY'
Duplicate entry '259-27' for key 'PRIMARY'
Duplicate entry '293-27' for key 'PRIMARY'
Duplicate entry '323-27' for key 'PRIMARY'
Duplicate entry '578-27' for key 'PRIMARY'
Duplicate entry '580-27' for key 'PRIMARY'
Duplicate entry '606-27' for key 'PRIMARY'
Duplicate entry '608-27' for key 'PRIMARY'
Duplicate entry '615-27' for key 'PRIMARY'
Duplicate entry '631-27' for key 'PRIMARY'
Duplicate entry '652-27' for key 'PRIMARY'
Duplicate entry '659-27' for key 'PRIMARY'
Duplicate entry '682-27' for key 'PRIMARY'
Duplicate entry '684-27' for key 'PRIMARY'
Duplicate entry '695-27' for key 'PRIMARY'

​ 17 ps5 93
​ 27 Switch 133 joycon; duplicate lite, oled, 大气层

​ 28 Xbox 35
​ 29 SteamDeck 27
​ 30 Quest3 6
​ 31 PS4 6
​ 32 IT支持 10 远程 10 ; 上门 4

​ 33 NetSuite 1
​ 34 掌机 3

未分类: 24

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT ID, 27, 0
FROM wp_posts
WHERE post_type = 'product'
and wp_posts.ID not in (
select object_id from wp_term_relationships
)


#更新分类的统计表
UPDATE wp_term_taxonomy
SET count = (
SELECT COUNT(*)
FROM wp_term_relationships
WHERE wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
)
WHERE term_taxonomy_id = 34

Next 搭建: 网站客服系统 docker

比如: https://zhuanlan.zhihu.com/p/23677870488

用于新增一个潜在客户直接语言沟通的机会, 直接在网站中聊天

快速上线的,但是无拓展功能(只能去付费拓展的)

https://www.tawk.to/software/live-chat/

https://www.twt.com/en/chat/pricing

https://github.com/chaskiq/chaskiq

https://github.com/chatwoot/chatwoot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: '3.8'

services:
# 内部轻量缓存(不可省略)
chaskiq-redis:
image: redis:7-alpine
container_name: chaskiq-redis
restart: always

# Chaskiq 客服核心服务
chaskiq-app:
image: chaskiq/chaskiq:latest
container_name: chaskiq-app
restart: always
ports:
- "3000:3000"
environment:
- HOST=http://localhost:3000
- SECRET_KEY_BASE=Another_very_long_random_string_64_chars
- REDIS_URL=redis://chaskiq-redis:6379/1 DATABASE_URL=postgresql://umami:umami20260109@192.168.6.117:5434/chaskiq_prod

# 首次运行前需手动进入容器执行 bundle exec rails db:schema:load 完成建表

方案一:正确配置 Chatwoot Community 纯开源版(解除限制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
version: '3.8'

services:
cskefu-redis:
image: redis:7-alpine
container_name: cskefu-redis
restart: always

chatwoot-web:
image: chatwoot/chatwoot:latest
container_name: chatwoot-web
restart: always
ports:
- "3000:3000"
environment:
- FRONTEND_URL=http://localhost:3000
- SECRET_KEY_BASE=a_very_long_random_string_here_32_chars

# ⚠️ 关键配置:关闭企业版限制,强制切换为无限制社区版
- ENABLE_ACCOUNT_LIMITS=false
- INSTALLATION_ENV=docker

# 直连您已有的 Postgres 15 数据库
- POSTGRES_HOST=172.17.0.1 # 宿主机桥接IP
- POSTGRES_PORT=5432
- POSTGRES_DATABASE=chatwoot_prod
- POSTGRES_USERNAME=postgres
- POSTGRES_PASSWORD=your_existing_postgres_password
- REDIS_URL=redis://cskefu-redis:6379/0
command: bundle exec rails s -p 3000 -b '0.0.0.0'
depends_on:
- cskefu-redis

chatwoot-worker:
image: chatwoot/chatwoot:latest
container_name: chatwoot-worker
restart: always
environment:
- ENABLE_ACCOUNT_LIMITS=false
- INSTALLATION_ENV=docker
- POSTGRES_HOST=172.17.0.1
- POSTGRES_PORT=5432
- POSTGRES_DATABASE=chatwoot_prod
- POSTGRES_USERNAME=postgres
- POSTGRES_PASSWORD=your_existing_postgres_password
- REDIS_URL=redis://cskefu-redis:6379/0
command: bundle exec sidekiq -g chatwoot -C config/sidekiq.yml
depends_on:
- cskefu-redis

SEO优化, 尝试提交Google

下载 wordpress sitemap generator 用于生成网站地图

下载zip, 上传安装后, 自动生成了http://w.carlzeng.com/sitemap.xml

image-20260529155841154

image-20260529160127424

状态: Processing data, please check again in a day or so

待下周重新查阅一下Google收录情况

  1. https://ziyuan.baidu.com/linksubmit/url

  2. Bing

    image-20260529163203355

提交了网站地图 http://w.carlzeng.com/sitemap.xml

灵感来源与项目计划

WordPress 告别 MySQL:Docker SQLite WordPress

我写了个从 Obsidian 自动发布文章到微信公众号的工具

个性化需求沟通 扫客服加V加群: