Files
rootandClaude Opus 5 218ad98519 [AI] docs: dramacmt 补上部署与 run_cmt.sh 的用法
其余四站的连接串只在同步服务器上,照 contentsync 的办法交叉编译 scp 过去,
用服务器自己的 env.sh,凭据不出那台机器。每次 contentsync 带进新短剧之后
要跑一遍 run_cmt.sh。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-03 11:47:02 +08:00

192 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# dramacmt
给在线短剧铺机器人评论。一次性运营工具,不进 cron。
## 为什么不用后台已有的评论导入
`web/api/commentctrl.AddContentExcel` 有三个限制:
1. 只认视频(按 `vidmod.GetVideoInfo` 查对象),短剧的 `objID` 是 `media._id`,走不通
2. 要上传 Excel
3. 它的 `userID` 取自 `static.json` 的 `sysUser`——**那些 uid 在 `user` 表里并不存在**
第 3 点是实际踩到的坑:评论列表填充用户信息时(`app/service/commentser/cmtcoverter.go`)
查不到用户**不会丢弃评论,只是把昵称和头像留空**。线上已经有 5962 条这样的
「无名无头像」评论(抽查 400 个引用 uid,0 命中)。
所以本工具自己建一批机器人用户,评论挂在它们身上。
## 机器人用户
- uid 从 **1001** 开始往上发,`--users` 控制池子大小。该区间原本完全空闲
(`uid 1~17` 是库里已有的 17 个机器人,1000 以下整段不碰)
- **池子要够大**:同一部剧内用户不重复,所以 `--users` 必须大于 `--max`;
而且池子太小的话同一个昵称头像会在整个评论区刷屏。实测取值:
tangguo 5,000(和 `scripts/vidcmt` 共用),其余四站 3,000
- uid 必须 `<= 111999`(`common/constant.RobotUIDLimit`)——
`commentser.getTrackInfo` 靠这个阈值判定 `isRobot`,后台可按机器人筛选和撤回
- uid 池远低于 `auto_id` 的 uid 计数器(当时是 3111989,只增不减),
不会和后续真实注册撞号
- 昵称按「姓 + 名」随机拼,风格对齐库里现有那 17 个机器人(郏哲、溪焱这种)
- 头像**从库里现有用户采样**,不写死路径(各站 `static.json` 里是同一批 90 张)
`user` 表的唯一索引要当心:
| 索引 | 类型 | 处理 |
|---|---|---|
| `devID` | **非 sparse 唯一** | 必须给唯一非空值,用 `ROBOT%07d` |
| `token` / `promotionCode` / `mobile` | 部分唯一(`{$gt: ""}`) | 留空串即豁免 |
已存在的 uid **不覆盖**(昵称头像可能被运营改过),所以可以重复跑。
## 评论
要能在 App 里显示,必须满足 `models/v/cmtmod.GetParentCmtList` 的全部条件:
```
level = 1 status = 1 isGodComment = false
isDelete = false isAdvertiser 非 true
createdAt <= now
```
注意 `status`:App 发评论走的 `getTrackInfo` 写的是 `status: 2`(未审核),
而列表只认 `1`。所以铺数据必须直接写 `1`,否则一条都不显示。
其余字段整体对齐线上真实短剧评论的形状,两个容易写错的地方:
- `type` 是 `"VIDEO"`,不是 `"drama"`——`objType` 才是 `"drama"`。
`getTrackInfo` 对所有 objType 都传 `cmtmod.CmtTypeVideo`
- `tousername` 落库键名是**小写**的:结构体那个字段只有 `form`/`json` tag,
没写 `bson` tag,driver 默认把字段名小写
- `city` 必须写 `"-"`:发评论路径里 `GetLocationByIP` 的调用是注释掉的
(`app/service/commentser/cmtsend.go:114-116`),线上真实评论**没有一条**带
归属地。`City` 会原样出参给 App(`cmtcoverter.go:46`),给真城市等于在评论区
挂个「我是机器人」的标。第一版写了随机城市,已改掉,线上那 19,697 条也已回刷
**`media.countComment` 必须同步 `$inc`**。App 的「总评论条数」在第一页直接读这个
字段(`app/api/commentctrl`),不补的话前端显示 0 条、但列表里有评论。
### 点赞数与时间
- `createdAt` 随机铺在该剧 `latestPublishedAt`(没有就退 `createdAt`)到现在之间,
再按时间排序写入
- `likeCount` 走长尾:55% 落 0~5、30% 落 6~30、12% 落 31~120、3% 落 121~400。
热门评论区要求 `likeCount > 0` 且按它倒序,所以不能全给 0
- 再按文案长短压上限:≤4 字的短评最多 30 赞,≤8 字最多 120。
不压的话会出现「可以」两个字拿 164 赞这种明显假的数据
- **不写 `like` 集合的记录**。`likeCount` 是展示值,`isLike` 只反映当前请求用户,
不建 like 文档不影响功能,也省掉几万条垃圾数据
文案池在 `content.go`,**637 条**互不重复。单剧内文案和用户都不重复,
所以 `--max` 不能超过池子容量(`main.go` 里有硬校验)。
容量还要留足余量,不能刚好够:铺 200 条时池子只有 220 条的话,每部剧都会抽到
九成文案,几千部剧的评论区长得一模一样。现在抽 200 条约占池子三成。
## 用法
```bash
go build -o /tmp/dramacmt ./scripts/dramacmt/
# 干跑,看会写多少
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo --dry-run
# 只给评论数为 0 的剧铺,每部 20~50 条(默认)
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo
# 所有在线短剧都加铺 50~200 条(其余四站 2026-10-30 实际用的参数)
/tmp/dramacmt --dst "$DST_HUANGGUO" --db huangguo --users 3000 \
--min 50 --max 200 --only-empty=false
# 补齐模式:把每部剧补到 50~200 条总量(tangguo 2026-10-30 实际用的参数)
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo --users 5000 \
--min 50 --max 200 --only-empty=false --top-up
# 只重算 countComment,不铺评论(每次 contentsync 带进新剧之后都要跑)
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo --recount
# 调试:只跑 3 部,固定种子便于复现
/tmp/dramacmt --dst "$DST_TANGGUO" --db tangguo --limit 3 --seed 42
```
`--db` 必须显式传:URI 里的 `authSource` 通常是 `admin`,推不出业务库名。
### `--top-up`
默认模式下 `--min~--max` 是**新增**条数,`--top-up` 把它当成**目标总条数**,
按差额补,已经够的跳过。
区别在于底子不空的站:tangguo 的短剧先前已经铺过 20~50 条,照新增算会冲到
250 条,和其余四站的 50~200 对不上。代价是每部剧多一次 `CountDocuments`
往返(773 部约两分钟,远端副本集的网络延迟占大头)。
现有条数按 App 列表第 1 页的口径数,不读 `countComment`——那个字段可能是飘的。
### `--recount`
把每部短剧的 `countComment` 重算成 App 列表口径的实际可见条数。铺完会自动跑一次,
`--no-recount` 可以跳过,`--recount` 则是只重算不铺。
**这个字段只会飘,不会自己回来**,有两个来源:
1. 发评论时 `incCommentCount` 无条件 `+1`(`app/service/commentser/cmtsend.go:224`),
审核不通过和删除都不回减。后端唯一的重算函数
`web/service/commentser.rebuildSingleObjCommentCount` **明确跳过 drama**
(`commentAgent.go:268` 只认 video 和 cartoon),所以线上没有任何一条路会修它
2. `contentsync` 用 `$setOnInsert` 给新文档的本地字段赋初值,`countComment` 在
那份清单里(`scripts/contentsync/spec.go`),于是**源库新剧带着源站的评论数
进来,本地却一条评论都没有**
第 2 条意味着每次 contentsync 带进新短剧之后都要重算一次,否则新剧在 App 里显示
「N 条评论」但列表是空的。2026-11-03 实测:一次增量同步带进 16 部新短剧,
全都带着源站的 `countComment`。
## 部署
tangguo 的连接串在本仓库 `app/config/app.json` 的 `mongo.videoDbUrl`,本地直接跑。
**其余四站的连接串只在同步服务器上**(`/server/sync/env.sh`、`hgmj_env.sh`,权限 600),
所以照 `scripts/contentsync` 的办法交叉编译静态二进制 scp 过去,让它用服务器自己的
连接串,凭据不出那台机器:
```bash
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o dramacmt ./scripts/dramacmt/
scp dramacmt root@172.104.39.32:/server/sync/
```
服务器上的 `/server/sync/run_cmt.sh` 负责 source 连接串并按站点循环,日志
`/var/log/dramacmt.log`。**不进 cron**,手动跑:
```bash
/server/sync/run_cmt.sh # 默认 huangguo 91fans hgmj 51dj
SITES="hgmj 51dj" /server/sync/run_cmt.sh # 指定站点
DRY=--dry-run /server/sync/run_cmt.sh # 干跑
```
`--top-up` 把 50~200 当成目标总量、已达标的不动,所以可以反复跑。**每次
contentsync 带进新短剧之后跑一遍**:新剧既没有评论,又带着源站的 `countComment`,
不补的话在 App 里显示「有 N 条评论但列表是空的」。
2026-11-03 实测一次增量同步之后:四站各有 43 / 43 / 39 / 39 部新剧待补,
共写入 19,300 条,重算修正 229 部,耗时 9 秒。
## 撤回
机器人用户和评论都带标记,撤回是两条命令:
```js
// 撤回评论(软删,保守做法)。uid 上限按当站实际的 --users 取
db.comment.updateMany({isRobot: true, objType: "drama", userID: {$gte: 1001, $lte: 5000}},
{$set: {isDelete: true}})
```
硬删同理,把 `$set` 换成 `deleteMany`。两种都做完后 `countComment` 要重算,
口径和 App 列表第 1 页保持一致(`app/service/commentser.GetTotalComments`):
```js
countDocuments({objID, objType: "drama", level: 1, status: 1,
isDelete: false, isAdvertiser: {$in: [null, false]}})
```
短剧和影视不一样:**第 1 页的总条数直接读 `media.countComment`**,第 2 页之后才
实时查,所以这个字段不重算前端就会出现「显示 N 条但列表是空的」。