# -*- coding: utf-8 -*- """批量生成论坛演示内容:用户、帖子、评论、点赞、收藏。""" from datetime import datetime, timedelta, timezone from app import app, db from models import User, ForumPost, ForumComment, ForumCategory, ForumPostLike, ForumPostBookmark DEFAULT_PASSWORD = "Forum123456" USERS = [ {"username": "ops_alan", "password": DEFAULT_PASSWORD}, {"username": "dev_yuki", "password": DEFAULT_PASSWORD}, {"username": "cloud_nana", "password": DEFAULT_PASSWORD}, {"username": "linux_mason", "password": DEFAULT_PASSWORD}, {"username": "sec_neo", "password": DEFAULT_PASSWORD}, {"username": "ai_rookie", "password": DEFAULT_PASSWORD}, ] POSTS = [ { "title": "Ubuntu 22.04 安装宝塔面板后的 10 项安全加固(实战清单)", "author": "ops_alan", "category": "运维经验", "days_ago": 7, "is_featured": True, "content": """背景:很多同学装完宝塔就直接上线,结果 2-3 天就被扫端口、爆破后台。 我自己在生产环境里执行的加固顺序如下,基本可以在 30 分钟内做完: 1. 安装后第一时间修改宝塔面板入口和端口,不使用默认路径。 2. 宝塔后台开启登录限制(失败次数锁定 + 验证码 + 仅允许白名单 IP)。 3. SSH 禁止 root 密码登录,只保留密钥登录;同时改 SSH 端口并记录在资产台账。 4. 开启 UFW / firewalld,只放行必须端口(22/80/443),其余全部拒绝。 5. 云厂商安全组与系统防火墙双层控制,避免“安全组放行过宽”。 6. 删除不必要的默认站点,避免目录遍历和弱口令后台暴露。 7. 数据库不要对公网开放 3306,应用与数据库尽量走内网。 8. 配置定时备份(站点、数据库、配置文件),并异地存储。 9. 安装 fail2ban,对 SSH 和 Web 登录做自动封禁。 10. 上线后立即做一次端口扫描与弱口令自检,确认暴露面。 建议:宝塔是效率工具,不是安全工具。上线前至少做一次“攻击面最小化”检查。""", "comments": [ ("sec_neo", "这个清单很实用,尤其是 SSH 密钥登录 + fail2ban,很多人会忽略。"), ("cloud_nana", "补充一点:最好把宝塔登录入口放到仅内网访问,再通过跳板机管理。"), ], "likes": ["dev_yuki", "cloud_nana", "linux_mason", "sec_neo"], "bookmarks": ["dev_yuki", "ai_rookie", "linux_mason"], }, { "title": "服务器开放端口的正确姿势:安全组、防火墙、服务监听三层排查", "author": "sec_neo", "category": "运维经验", "days_ago": 6, "content": """很多“端口开了但访问不到”的问题,本质是三层中有一层没打通。 排查顺序建议固定为: 第一层:云安全组 - 入站规则是否允许目标端口(如 80/443); - 来源 CIDR 是否过于严格(如只放了办公网); - 是否误把规则加在了错误的网卡或实例组。 第二层:系统防火墙 - `ufw status` / `firewall-cmd --list-all` 检查是否放行; - 是否有默认拒绝策略导致端口被拦; - 改完记得 reload。 第三层:服务监听 - `ss -lntp | grep 80` 检查进程是否监听; - 注意监听地址是 `0.0.0.0` 还是 `127.0.0.1`; - 应用容器端口映射是否正确(Docker 常见问题)。 最后一步: - 本机 `curl 127.0.0.1:端口`; - 内网 `curl 内网IP:端口`; - 外网 `curl 公网IP:端口`。 这样基本 10-15 分钟能定位到具体环节。""", "comments": [ ("ops_alan", "三层模型非常清晰,新手排障直接照这个顺序来就行。"), ("ai_rookie", "我之前就是服务只监听 127.0.0.1,难怪外网怎么都不通。"), ], "likes": ["ops_alan", "dev_yuki", "ai_rookie"], "bookmarks": ["ops_alan", "cloud_nana", "ai_rookie"], }, { "title": "Nginx 绑定域名 + HTTPS 全流程(含 301、HSTS、自动续期)", "author": "cloud_nana", "category": "运维经验", "days_ago": 5, "is_featured": True, "content": """这篇给一个可直接落地的域名绑定流程,适合新站上线: 步骤 1:DNS 解析 - A 记录指向服务器公网 IP; - 等待生效(通常 1-10 分钟,部分服务商更久)。 步骤 2:Nginx server block - `server_name` 填主域名 + www; - 先用 HTTP 跑通站点,不要一开始就上证书。 步骤 3:申请证书(Let's Encrypt) - 推荐 certbot; - 申请成功后检查证书路径和 Nginx 引用是否一致。 步骤 4:强制 HTTPS - 80 端口统一做 301 跳转到 https; - 保留 ACME 验证路径例外(如果你用 webroot 方式)。 步骤 5:安全头 - 开启 HSTS(先短时间,再逐步拉长); - 可补充 `X-Content-Type-Options`、`X-Frame-Options`。 步骤 6:自动续期 - `certbot renew --dry-run` 先验证; - 用 systemd timer / crontab 定期续期并 reload Nginx。 上线后建议在 SSL Labs 跑一遍,确保协议和套件配置达标。""", "comments": [ ("linux_mason", "赞同先 HTTP 再 HTTPS,很多人一上来配证书容易定位困难。"), ("sec_neo", "HSTS 建议分阶段,别一开始就 preload,回滚会很麻烦。"), ], "likes": ["ops_alan", "linux_mason", "sec_neo", "dev_yuki"], "bookmarks": ["ops_alan", "linux_mason", "ai_rookie"], }, { "title": "OpenClaw 在 Ubuntu 服务器安装与 systemd 守护(可维护版)", "author": "dev_yuki", "category": "运维经验", "days_ago": 4, "content": """分享我在 Ubuntu 22.04 上部署 OpenClaw 的方式,重点是“稳定运行 + 易于维护”。 推荐目录结构: - `/opt/openclaw`:程序目录 - `/etc/openclaw/`:配置目录 - `/var/log/openclaw/`:日志目录 部署建议: 1. 用独立系统用户运行(不要用 root)。 2. 配置文件与程序文件分离,便于升级回滚。 3. 写 systemd 服务: - `Restart=always` - `RestartSec=5` - 限制权限(`NoNewPrivileges=true` 等) 4. 日志统一走 journald 或文件滚动,避免磁盘被打满。 5. 升级流程采用“解压新版本 -> 切换软链 -> 重启服务”。 上线前 checklist: - `systemctl status openclaw` - `journalctl -u openclaw -n 200` - 健康检查接口可用 - 端口仅对需要的来源开放 这样做的好处是:故障定位快、升级可回滚、不会因为单次异常导致服务长期不可用。""", "comments": [ ("ops_alan", "目录分层 + 软链切换这个方案很稳,适合长期维护。"), ("cloud_nana", "建议再加个健康检查脚本,配合告警一起用效果更好。"), ], "likes": ["ops_alan", "cloud_nana", "linux_mason"], "bookmarks": ["ops_alan", "sec_neo", "ai_rookie"], }, { "title": "宝塔部署 Flask:Gunicorn + Nginx + Supervisor 一次跑通", "author": "linux_mason", "category": "运维经验", "days_ago": 3, "content": """很多同学卡在“本地能跑,宝塔上线 502”。我这边给一个稳定组合: 架构: Nginx (80/443) -> Gunicorn (127.0.0.1:8000) -> Flask App 关键点: 1. Flask 不直接对外暴露,Gunicorn 只监听本机回环地址。 2. Nginx `proxy_pass` 指向 Gunicorn,注意 `proxy_set_header` 要完整。 3. Gunicorn worker 数量按 CPU 计算,不要盲目拉满。 4. 用 Supervisor/systemd 托管 Gunicorn,防止进程意外退出。 5. 目录权限统一,避免静态文件 403。 常见坑: - 虚拟环境没激活导致依赖缺失; - Nginx 与 Gunicorn socket/端口不一致; - 项目根目录配置错误导致模块导入失败。 建议每次发布后执行: - `nginx -t` - 访问健康检查 URL - 查看 Nginx 和应用日志是否有 5xx。""", "comments": [ ("dev_yuki", "Gunicorn 只监听 127.0.0.1 这点很重要,安全收益很高。"), ("ai_rookie", "终于理解 502 的定位方式了,之前一直只看应用日志。"), ], "likes": ["ops_alan", "dev_yuki", "cloud_nana", "ai_rookie"], "bookmarks": ["cloud_nana", "ai_rookie", "sec_neo"], }, { "title": "为什么放行了 80/443 还是打不开网站?15 分钟排障流程", "author": "ai_rookie", "category": "新手提问", "days_ago": 2, "is_pinned": True, "content": """我把安全组 80/443 放开了,但浏览器还是超时。请大家帮忙看看排障顺序是否正确: 我目前做了: 1. 云安全组入站已放行 80/443(来源 0.0.0.0/0)。 2. Ubuntu 上 `ufw allow 80,443/tcp`。 3. Nginx 已启动,`systemctl status nginx` 显示 active。 4. 域名 A 记录已指向服务器公网 IP。 我准备进一步排查: - `ss -lntp | grep :80` 确认监听地址; - 本机 curl 127.0.0.1; - 外网 curl 公网 IP; - 检查 Nginx default server 是否误拦截。 如果还有常见遗漏项,请大家补充一下,我整理后回帖反馈结果。""", "comments": [ ("sec_neo", "再查一下运营商端口封禁和实例是否有公网带宽,这两个也常见。"), ("cloud_nana", "域名解析生效可以用 dig/nslookup 验证,避免本地 DNS 缓存干扰。"), ("ops_alan", "可以先不用域名,直接公网 IP 打通后再回到域名层。"), ], "likes": ["ops_alan", "cloud_nana", "dev_yuki", "linux_mason"], "bookmarks": ["ops_alan", "dev_yuki"], }, { "title": "新服务器首日初始化 SOP:账号、时间、日志、监控一步到位", "author": "ops_alan", "category": "运维经验", "days_ago": 1, "content": """为了避免“上线后再补安全”这种被动局面,我把新机初始化收敛成一个 SOP: 基础项: - 创建普通运维账号 + sudo; - 配置 SSH 密钥登录; - 关闭 root 密码登录; - 设置时区、NTP 同步。 系统项: - 安装常用诊断工具(curl, wget, vim, htop, lsof, ss); - 配置日志轮转; - 开启防火墙并最小放行。 可观测项: - 主机监控(CPU、内存、磁盘、负载); - 进程可用性检查; - 磁盘与证书过期告警。 交付项: - 资产信息登记(IP、用途、负责人、到期时间); - 变更记录模板; - 回滚方案。 这个流程固定下来后,服务器上线质量会稳定很多。""", "comments": [ ("linux_mason", "强烈建议把资产台账自动化,不然机器一多很容易混乱。"), ("sec_neo", "同意,首日就把监控和告警接好,能省很多夜间故障时间。"), ], "likes": ["dev_yuki", "cloud_nana", "linux_mason", "sec_neo", "ai_rookie"], "bookmarks": ["dev_yuki", "cloud_nana", "ai_rookie"], }, { "title": "服务器端口规划建议:Web、数据库、SSH 与内网隔离实践", "author": "sec_neo", "category": "综合讨论", "days_ago": 1, "is_featured": True, "content": """分享一个适合中小团队的端口规划思路,核心目标是“暴露最小化”: 公网可见: - 80/443:Web 流量入口(建议统一走 Nginx/网关) - 22:仅白名单 IP,最好配合堡垒机 仅内网可见: - 3306/5432:数据库 - 6379:缓存 - 9200:搜索服务(若有) 管理面: - 面板、监控、日志平台尽量不直接暴露公网; - 必要时使用 VPN / 跳板机访问。 执行原则: 1. 先拒绝,后放行(default deny)。 2. 安全组与主机防火墙同时配置。 3. 定期做“端口盘点”和“僵尸规则清理”。 端口规划不是一次性工作,建议每月复盘一次,防止规则持续膨胀。""", "comments": [ ("ops_alan", "我们团队也是按这个思路做,尤其是数据库绝不出公网。"), ("dev_yuki", "建议再加一条:高危端口变更必须走审批和审计。"), ], "likes": ["ops_alan", "cloud_nana", "dev_yuki", "linux_mason"], "bookmarks": ["ops_alan", "linux_mason", "ai_rookie", "cloud_nana"], }, { "title": "宝塔里 Nginx 显示运行中,但网站一直 502,应该按什么顺序排查?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "is_pinned": True, "content": """我在宝塔上部署了 Flask,Nginx 状态是绿色“运行中”,但是访问域名一直 502。 目前我确认过: 1. 域名解析已经指向服务器 IP; 2. 80/443 在安全组里放行; 3. 应用进程偶尔会起来,但不稳定。 请问排查顺序是不是这样更高效? - 看 Nginx 错误日志(确认是 upstream 超时还是连接拒绝); - 用 `ss -lntp` 看 Gunicorn 是否在监听; - 本机 curl `127.0.0.1:应用端口`; - 检查宝塔反向代理的目标端口是否写错; - 查看应用日志是否有导入错误或环境变量缺失。 如果还有常见坑,请大家补充一下,我整理成 checklist。""", "comments": [ ("linux_mason", "顺序正确。先从 Nginx error.log 判断是 connect refused 还是 timeout,再决定下一步。"), ("dev_yuki", "补充:检查 Python 虚拟环境路径,宝塔里最容易因为路径错导致 Gunicorn 启动失败。"), ("ops_alan", "再确认一下 systemd/supervisor 是否开启自动拉起,不然进程崩了就一直 502。"), ], "likes": ["ops_alan", "linux_mason", "dev_yuki", "cloud_nana"], "bookmarks": ["ops_alan", "dev_yuki", "sec_neo"], }, { "title": "域名解析已经改了,为什么访问还是旧服务器?DNS 缓存怎么判断?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "content": """我把 A 记录改到新 VPS 后,自己电脑访问还是旧站点,但手机 4G 打开有时是新站。 我怀疑是 DNS 缓存问题,想确认排查步骤: 1. `dig 域名 +short` 看当前解析结果; 2. 对比不同 DNS(8.8.8.8 / 1.1.1.1 / 本地运营商); 3. 清本地 DNS 缓存 + 浏览器缓存; 4. 检查是否还有 AAAA 记录指向旧机器; 5. 观察 TTL 到期时间。 有没有更稳妥的切换方案,避免业务迁移时出现“部分用户命中旧站”?""", "comments": [ ("cloud_nana", "迁移前建议先把 TTL 从 600 调到 60,等全网生效后再切 IP。"), ("sec_neo", "你提到 AAAA 很关键,很多人只改 A 记录,IPv6 用户会继续走旧站。"), ], "likes": ["cloud_nana", "sec_neo", "ops_alan"], "bookmarks": ["ops_alan", "linux_mason", "dev_yuki"], }, { "title": "Ubuntu 放行了 8080 还是连不上,安全组和防火墙到底谁优先?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "content": """我在服务器里执行了 `ufw allow 8080/tcp`,应用也监听 `0.0.0.0:8080`,但外网还是连不上。 我想搞清楚优先关系: - 云安全组没放行时,系统防火墙放行有没有意义? - 安全组放行了但 UFW 拒绝,会是什么表现? - 能否用一个最简流程快速判断问题在哪一层? 现在新人常被“服务正常、端口不通”卡住,求一个固定排障模板。""", "comments": [ ("sec_neo", "先看安全组再看系统防火墙,任意一层拒绝都不通。两层都要放行才行。"), ("ops_alan", "建议固定三步:安全组 -> 防火墙 -> 服务监听,避免来回猜。"), ], "likes": ["sec_neo", "ops_alan", "dev_yuki"], "bookmarks": ["ai_rookie", "cloud_nana", "linux_mason"], }, { "title": "绑定域名后总跳到 Nginx Welcome Page,server_name 应该怎么配?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "content": """我已经把域名解析到了服务器,也有自己的站点配置,但访问域名总是落到默认欢迎页。 我检查到: 1. `/etc/nginx/sites-enabled` 里同时存在 default 和我的站点; 2. 我的 server_name 只写了主域名,没写 www; 3. 有多个配置都监听 80。 请问正确做法是不是: - 删除/禁用默认站点; - server_name 同时写 `example.com` 和 `www.example.com`; - 用 `nginx -t` 检查冲突,再 reload。""", "comments": [ ("linux_mason", "是的,默认站点优先命中非常常见。建议明确一个 default_server,其他按域名精确匹配。"), ("cloud_nana", "别忘了 HTTPS 的 server block 也要同步配置,不然 443 还是会走错。"), ], "likes": ["linux_mason", "cloud_nana", "ops_alan"], "bookmarks": ["dev_yuki", "ops_alan", "ai_rookie"], }, { "title": "OpenClaw 服务启动成功但页面空白,日志里没有明显报错怎么办?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "content": """我按教程装了 OpenClaw,systemd 状态是 active,但前端页面空白或一直 loading。 我目前想到的排查: 1. 检查配置文件里 API 地址是否写错(内网/公网); 2. 浏览器开发者工具看网络请求是否 4xx/5xx; 3. 检查反向代理路径和 WebSocket 升级头; 4. 看 openclaw 日志级别是否太低,临时改为 debug; 5. 校验数据库连接和初始化状态。 有没有人踩过类似坑,最后是哪里的问题?""", "comments": [ ("dev_yuki", "优先看浏览器 network 面板,空白页大概率是静态资源 404 或 API 跨域。"), ("sec_neo", "如果走反代,确认 `Upgrade`/`Connection` 头,WebSocket 缺这个会卡 loading。"), ], "likes": ["dev_yuki", "sec_neo", "cloud_nana"], "bookmarks": ["ops_alan", "linux_mason", "ai_rookie"], }, { "title": "申请 Let's Encrypt 一直失败(too many failed authorizations),还能怎么处理?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "content": """我在同一个域名上连续尝试了很多次证书申请,现在提示 `too many failed authorizations`。 我理解是被限流了,但不确定下一步: 1. 是不是要先等一段时间再重试? 2. 失败期间怎么保证业务可访问(临时 HTTP / 自签证书)? 3. 下次重试前要先验证哪些条件,避免再次失败? 我现在的域名解析已经正常,80 端口也放开了。""", "comments": [ ("cloud_nana", "先等限流窗口过去,再用 `--dry-run` 或 staging 环境验证流程,别直接打生产接口。"), ("ops_alan", "重试前先用 HTTP 明确能访问 `/.well-known/acme-challenge/`,这一步最关键。"), ], "likes": ["cloud_nana", "ops_alan", "linux_mason"], "bookmarks": ["dev_yuki", "ops_alan", "ai_rookie"], }, { "title": "宝塔用几天后磁盘爆满,新手该先清哪些目录?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "content": """2C4G 小机器,刚装宝塔一周磁盘就 90% 了,怕直接删错文件。 目前怀疑的占用来源: - Nginx / 应用日志; - 宝塔自动备份; - Docker 镜像与容器日志; - 数据库 binlog。 有没有一个“安全清理顺序”? 我希望先释放空间,后面再补定期清理策略。""", "comments": [ ("ops_alan", "先 `du -sh /*` 定位大头,再按日志->备份->镜像顺序清理,不要盲删系统目录。"), ("linux_mason", "Docker 场景补一句:`docker system df` 先看占用,再按策略 prune。"), ], "likes": ["ops_alan", "linux_mason", "sec_neo"], "bookmarks": ["cloud_nana", "dev_yuki", "ai_rookie"], }, { "title": "新手配置选型:2核4G 能不能同时跑 Flask + MySQL + OpenClaw?", "author": "ai_rookie", "category": "新手提问", "days_ago": 0, "content": """预算有限,先买了 2核4G 机器,想跑: 1. Flask 网站 2. MySQL 3. OpenClaw 担心点: - 高峰时内存不够导致 OOM; - MySQL 占用太大拖慢接口; - 后续扩容迁移复杂。 有没有比较稳妥的起步建议?例如哪些服务先拆、哪些参数先限制?""", "comments": [ ("dev_yuki", "2核4G 能跑,但建议先把 MySQL 参数收紧,Gunicorn worker 不要开太多。"), ("cloud_nana", "如果流量上来,优先把数据库拆到独立实例,应用层水平扩展更简单。"), ("sec_neo", "别忘了开启 swap 兜底,但它只是缓冲,不能替代真正扩容。"), ], "likes": ["dev_yuki", "cloud_nana", "sec_neo", "ops_alan"], "bookmarks": ["ops_alan", "linux_mason", "ai_rookie", "cloud_nana"], }, { "title": "OpenClaw 安装实战:从 0 到可用(官方安装页版)", "author": "dev_yuki", "category": "运维经验", "days_ago": 0, "is_featured": True, "content": """这篇按官方安装页(https://openclaw.im/#install)整理,目标是:新手 10-20 分钟跑通。 一、安装方式(推荐一键脚本) 1. 服务器执行: `curl -fsSL https://openclaw.im/install.sh | bash` 2. 安装后检查: `openclaw --version` 如果你更习惯包管理器,也可以: - npm: `npm install -g openclaw@latest` - pnpm: `pnpm add -g openclaw@latest` 二、初始化(关键步骤) 1. 执行引导: `openclaw onboard --install-daemon` 2. 按提示完成 Provider、模型、存储等配置。 3. 安装完成后,建议先跑健康检查: `openclaw gateway status` 三、Web 控制台与连接 1. 默认控制台地址: `http://127.0.0.1:18789/` 2. 如果你是远程服务器,建议用 Nginx 反向代理并开启 HTTPS。 3. 需要连接频道时,使用: `openclaw channels login` 四、Docker 部署(可选) 官方也提供 Docker 工作流,常见顺序: 1. `./docker-setup.sh` 2. `docker compose run --rm openclaw-cli onboard` 3. `docker compose up -d openclaw-gateway` 五、排错清单(最常见) 1. 命令执行失败:先检查 Node 版本(官方建议 Node >= 22)。 2. 页面打不开:确认 18789 端口监听与防火墙/安全组放行。 3. 网关状态异常:先看 `openclaw gateway status`,再复查 onboard 配置。 4. 远程访问不稳定:优先通过反向代理统一入口,不直接暴露高风险端口。 六、上线建议 1. 把配置与日志目录分离,方便升级和回滚。 2. 使用守护方式运行(onboard 的 daemon 选项),避免进程意外退出。 3. 做最小暴露:仅开放必要端口,后台入口加访问控制。""", "comments": [ ("ops_alan", "这篇很适合新手,先一键安装再做反代是比较稳的路径。"), ("sec_neo", "建议补一句:公网部署务必加 HTTPS 和访问控制,别裸奔。"), ("ai_rookie", "我刚按这个流程跑通了,`openclaw gateway status` 这个检查很有用。"), ], "likes": ["ops_alan", "cloud_nana", "linux_mason", "sec_neo", "ai_rookie"], "bookmarks": ["ops_alan", "cloud_nana", "ai_rookie"], }, ] def _utcnow_naive(): """兼容 Python 3.13:返回无时区 UTC 时间。""" return datetime.now(timezone.utc).replace(tzinfo=None) def _resolve_category(name): if not name: return "综合讨论" row = ForumCategory.query.filter_by(name=name).first() if row: return row.name active = ForumCategory.query.filter_by(is_active=True).order_by(ForumCategory.sort_order.asc()).first() if active: return active.name any_row = ForumCategory.query.order_by(ForumCategory.sort_order.asc()).first() if any_row: return any_row.name return "综合讨论" def _get_or_create_user(username, password): user = User.query.filter_by(username=username).first() created = False if user is None: user = User(username=username) user.set_password(password) user.last_login_at = _utcnow_naive() db.session.add(user) db.session.flush() created = True return user, created def main(): created_users = 0 skipped_users = 0 created_posts = 0 skipped_posts = 0 created_comments = 0 created_likes = 0 created_bookmarks = 0 with app.app_context(): user_map = {} for u in USERS: user, created = _get_or_create_user(u["username"], u["password"]) user_map[u["username"]] = user if created: created_users += 1 else: skipped_users += 1 db.session.flush() for idx, spec in enumerate(POSTS): title = spec["title"].strip() author = user_map.get(spec["author"]) if not author: continue exists = ForumPost.query.filter_by(title=title).first() if exists: skipped_posts += 1 continue created_at = _utcnow_naive() - timedelta(days=int(spec.get("days_ago", 0)), hours=max(0, idx)) post = ForumPost( user_id=author.id, category=_resolve_category(spec.get("category")), title=title, content=spec["content"].strip(), is_pinned=bool(spec.get("is_pinned")), is_featured=bool(spec.get("is_featured")), is_locked=bool(spec.get("is_locked")), view_count=120 + idx * 19, created_at=created_at, updated_at=created_at, ) db.session.add(post) db.session.flush() created_posts += 1 for c_idx, (comment_user, comment_text) in enumerate(spec.get("comments", [])): c_user = user_map.get(comment_user) if not c_user: continue comment_at = created_at + timedelta(hours=2 + c_idx * 3) db.session.add(ForumComment( post_id=post.id, user_id=c_user.id, content=comment_text.strip(), created_at=comment_at, updated_at=comment_at, )) created_comments += 1 for like_user in spec.get("likes", []): l_user = user_map.get(like_user) if not l_user: continue db.session.add(ForumPostLike(post_id=post.id, user_id=l_user.id)) created_likes += 1 for bookmark_user in spec.get("bookmarks", []): b_user = user_map.get(bookmark_user) if not b_user: continue db.session.add(ForumPostBookmark(post_id=post.id, user_id=b_user.id)) created_bookmarks += 1 db.session.commit() print("用户:新增 {},已存在 {}".format(created_users, skipped_users)) print("帖子:新增 {},已存在 {}".format(created_posts, skipped_posts)) print("评论:新增 {}".format(created_comments)) print("点赞:新增 {}".format(created_likes)) print("收藏:新增 {}".format(created_bookmarks)) print("默认测试密码(新用户):{}".format(DEFAULT_PASSWORD)) if __name__ == "__main__": main()