Files
full/docs/feed.md
2026-09-22 18:53:53 +08:00

32 KiB
Raw Blame History

feed社交动态与内容互动代码审计报告

内容
审计对象 module/social/feedmodule 路径 bsm/full/module/social/feed
服务域 社交与内容
审计日期 2026-09-22
代码规模 手写 Go 32 个文件 / 1160 行;pb/ 14 个文件 / 6527 行(含手写兼容文件 pb/blocks_compat.goproto/ 5 个文件 / 433 行;etc/ 6 个 yaml + 1 个 supervisor conf*_test.go 0 个
入口 仅独立入口 cmd/main/main.gogRPC + gateway 单进程,端口 12212service/空目录,未被 pkgs/allpkgs/ecmall 引用;cmd/cli/main.go 只打印配置
对外协议 gRPC4 个 service / 17 个 RPCgateway 路由定义为 POST /feed.{Service}/{Method},但模块内从未注册任何 gateway handler
结论摘要 代码量小且 go vet 通过,但核心读写链路大面积失效:分页与过滤条件因 GORM 链式未回写而全部失效、列表恒不返回标签/附件、标签列表恒为空、删除评论为空实现;删除/修改动态与互动计数接口均无归属校验9 个 RPC 为 TODO 占位;etc/feed_*.yaml 字段名与 SDK 配置结构不匹配,服务启动即退出。

1. 服务定位与职责

承载用户发布动态(feed_post)、动态附件与标签关联、评论(feed_comment)、标签字典(feed_tags)与四类时间线(推荐/好友/关注/热门)的社交内容服务。

它是社交内容流的读写与互动计数服务。时间线排序与分发、好友/关注关系、推荐算法均不在本模块内实现(四类 Timeline 方法全部为占位);模块也不负责用户资料、关系链、消息推送或媒体存储。

2. 代码结构与入口

路径 职责
cmd/main/main.go 独立进程入口:config.New("Feed")impl.NewImpl()server.New(addr)service.Newsrv.Use(models.InitData)srv.Start()
cmd/cli/main.go config.New("feed") + fmt.Println(config.Spec.Databases) 的调试入口
internal/config/config.go SrvConfigBase、Databases、MicroService、Rpc、Gateway、APM、Etcd、WeChatConf、Token、Kyc与校验
internal/impl/impl.go 初始化 Memory、Redis、DB、Etcd 四个全局实例
internal/server/new.go 创建 grpc.Server、注册 4 个 serviceprotoc-gen-slc 生成)
internal/server/{post,tag,timeline,setting}_server.go 17 个 RPC 的 1:1 转发(生成代码)
internal/logic/post/{fetch,create,change,remove,comment_list,add_comment,delete_comment,action}.go 动态 CRUD、评论、互动计数
internal/logic/tag/{list,create,post_list}.go 标签列表/创建/话题下动态
internal/logic/timeline/{recommend,friend,follow,hot}.go 四类时间线(全部占位)
internal/logic/setting/{info,rights}.go 设置(全部占位)
internal/models/{feed_post,feed_comment,feed_tags,feed_relate_tags,feed_relate_attachs,query}.go 5 张表的模型 + 迁移注册 + query.go 中全部数据访问函数
service/ 空目录(无 expose.go,因此无法被聚合宿主接入)
pb/blocks_compat.go 手写类型别名文件(PostListReply = FeedPostListReply 等),非生成代码
proto/*.proto 4 个 service 契约;公共消息放在 proto/const.protopackage blocks,与 service package feed 不同)
etc/{feed_dev,feed_prod,feed_test,passport_dev,passport_prod,passport_test}.yaml 配置;passport_*.yaml 的内容才是与本模块结构匹配的那一份(见 6.4

依赖注入:模块内无 service.Exposeimpl.NewImpl() 是唯一初始化路径;pkgs/allpkgs/ecmallgo.mod 均未引用本模块,全仓库对 module/social/feed 的引用只有 scripts/api-docgen(仅 import pb)与根 README.md

3. 接口清单

HTTP 路径由生成代码固定为 /feed.{服务}/{方法}pb/post.pb.gw.go:600-607pb/tag.pb.gw.go:280-282pb/timeline.pb.gw.go:344-347pb/setting.pb.gw.go:216-217)。注意:这些 handler 在模块内从未被注册到任何 ServeMux(见 6.4)。

方法 路径 功能 鉴权 实现位置
POST /feed.Post/Fetch 推文列表(支持 params.key 按标签过滤) JWT logic/post/fetch.go:14
POST /feed.Post/Create 新建推文(含附件、标签) JWT logic/post/create.go:17
POST /feed.Post/Change 修改推文 JWT无归属校验 logic/post/change.go:17
POST /feed.Post/Remove 删除推文 JWT无归属校验 logic/post/remove.go:16
POST /feed.Post/CommentList 评论列表 JWT 占位 logic/post/comment_list.go:11
POST /feed.Post/AddComment 新建评论 JWT logic/post/add_comment.go:18
POST /feed.Post/DeleteComment 删除评论 JWT无归属校验 logic/post/delete_comment.go:16 + 空实现 models/query.go:122-125
POST /feed.Post/Action 点赞/点踩计数 JWT不校验身份来源 logic/post/action.go:17
POST /feed.Tag/List 标签列表 JWT logic/tag/list.go:13(返回值被丢弃,恒为空)
POST /feed.Tag/Create 新建标签 JWT logic/tag/create.go:15
POST /feed.Tag/PostList 话题相关动态 JWT 占位 logic/tag/post_list.go:11
POST /feed.Timeline/Recommend 推荐时间线 JWT 占位 logic/timeline/recommend.go:11
POST /feed.Timeline/Friend 好友动态 JWT 占位 logic/timeline/friend.go:11
POST /feed.Timeline/Follow 关注动态 JWT 占位 logic/timeline/follow.go:11
POST /feed.Timeline/Hot 热门动态 JWT 占位 logic/timeline/hot.go:11
POST /feed.Setting/Info 修改个人信息 JWT 占位 logic/setting/info.go:11
POST /feed.Setting/Rights 修改权限信息 JWT 占位 logic/setting/rights.go:11

鉴权方式:全部 17 个 logic 入口都调用 service.ParseMetaCtx(ctx, nil)git.apinb.com/bsm-sdk/core/service/meta.go:19-49),要求有效 JWT所有 opts 均为 nil,即不校验角色、不校验私有 IP。多个入口用 _, err = ... 丢弃 claimslogic/post/fetch.go:15),因此拿不到调用者身份。

声明但未实现 / 空转的接口

类别 接口 证据
TODO 占位4 个时间线) Timeline.Recommend / Friend / Follow / Hot internal/logic/timeline/{recommend,friend,follow,hot}.go:26// TODO: add your logic code
TODO 占位 Tag.PostList internal/logic/tag/post_list.go:26
TODO 占位 Post.CommentList internal/logic/post/comment_list.go:26
TODO 占位2 个) Setting.Info / Setting.Rights internal/logic/setting/{info,rights}.go:18-20
有路由有转发但底层空实现 Post.DeleteComment internal/models/query.go:122-125 函数体只有 return,永远返回 nil
有实现但返回值被丢弃 Tag.List internal/logic/tag/list.go:32 返回新建的空 TagListReply,忽略了上面组装好的 res

9 个 TODO 占位函数统一写成 func(...) (reply *XxxReply, err error) { ...; return },即返回 (nil, nil)gRPC 侧回空消息gateway 侧对 nil 响应序列化会失败(表现为 500/空响应)。

4. 数据模型与表

模型统一在 init() 中通过 database.AppendMigrate 注册(internal/models/feed_post.go:20 等为 TableName 方法;迁移注册在各文件内)。公共字段来自 SDKtypes.Std_IICUDS = id(PK) / identity varchar(36) uniqueIndex / created_at / updated_at / deleted_at(index) / status int8 indextypes.Std_IDIdentity = id(PK) / identity(uniqueIndex)types.Std_Passport = passport_id(index) / passport_identity varchar(36)(index)。

feed_postinternal/models/feed_post.go:8-22

字段 类型 键/约束 说明
id uint PK 自增主键
identity varchar(36) uniqueIndex 动态唯一码
created_at/updated_at/deleted_at timestamp index(deleted_at) 软删除
status int8 index, 默认 0 状态(-1 禁止 / 1 正常),查询中未使用
passport_id uint index 发布者 ID
passport_identity varchar(36) index 发布者唯一标识
content text 默认 '' 正文
is_open bool 默认 true true 公开 / false 不公开,查询中未过滤
cnt_like int64 默认 0 点赞数
cnt_unlike int64 默认 0 点踩数
cnt_comment int64 默认 0 评论数
Attachs []FeedRelateAttach gorm:"-" 非持久化,不会自动迁移,也永远不会被 GORM 加载
Tags []FeedRelateTags gorm:"-" 同上

feed_commentinternal/models/feed_comment.go:6-20

字段 类型 键/约束 说明
id / identity uint / varchar(36) PK / uniqueIndex 主键与唯一码
created_at/updated_at/deleted_at timestamp index(deleted_at) 软删除
status int8 index 状态,未使用
passport_id / passport_identity uint / varchar(36) index 评论作者;新增评论时从不写入(见 6.2
post_identity varchar(36) index 所属动态唯一码
parent_identity varchar(36) index 上级评论唯一码
content text 默认 '' 评论正文
SubComment []FeedComment foreignkey:parent_identity 子评论关联,无任何查询使用
cnt_like/cnt_unlike/cnt_comment int64 默认 0 互动计数

feed_tagsinternal/models/feed_tags.go:8-16

字段 类型 键/约束 说明
id uint PK 自增主键
created_at/updated_at/deleted_at timestamp index(deleted_at) 软删除
key varchar(36) not null无唯一索引 标签键;由 tag/create.go:26 写入随机 UUID
content varchar(255) not null 标签名称

该表没有 identity,但 models/query.go:81:89 使用 Where("identity = ?", ...)(见 6.2 与 6.3)。

feed_relate_tagsinternal/models/feed_relate_tags.go:6-15

字段 类型 键/约束 说明
id / identity uint / varchar(36) PK / uniqueIndex 主键与唯一码
post_identity varchar(36) index 动态唯一码
key varchar(36) index 标签键
Content string gorm:"-" 非持久化create.go:46 写入的标签名会被静默丢弃

feed_relate_attachinternal/models/feed_relate_attachs.go:6-16,表名单数)

字段 类型 键/约束 说明
id uint PK内嵌 Std_IDIdentity 自增主键
identity varchar(36) uniqueIndex 唯一码(内嵌字段与显式字段同名,见 6.4
post_identity varchar(36) index 动态唯一码
attach_type varchar(36) index 附件类型
url varchar(255) index 附件地址

5. 核心流程

5.1 发布动态 → 落库 → 列表读取

flowchart TD
    A["POST /feed.Post/Create<br/>service.ParseMetaCtx JWT"] --> B["校验 content 非空"]
    B --> C["models.CreatePost 事务"]
    C --> D["tx.Create feed_post"]
    D --> E["tx.Create feed_relate_attach"]
    E --> F["tx.Create feed_relate_tags<br/>Content 字段 gorm 忽略 标签名丢失"]
    F --> G["返回 identity"]
    H["POST /feed.Post/Fetch<br/>service.ParseMetaCtx 丢弃 claims"] --> I["page_no 小于等于 0 置 1<br/>page_size 小于等于 0 置 10"]
    I --> J["models.PostList<br/>Order created_at desc"]
    J --> K["tx.Limit(page).Offset(...) 未回写<br/>分页整体失效"]
    K --> L["Find 到 FeedPost 切片<br/>Tags 与 Attachs 为 gorm 忽略字段"]
    L --> M["AssembleList 中两个循环永不执行<br/>标签与附件恒为空"]
    M --> N["fmt.Print 把整页数据打到 stdout"]
    N --> O["无 is_open 无 status 过滤<br/>含私密与禁用用户的动态"]

5.2 评论与互动计数

flowchart TD
    A["POST /feed.Post/AddComment"] --> B["models.AddComment 事务"]
    B --> C["Create feed_comment<br/>passport_identity 未写入 作者为空"]
    C --> D["Table feed_post 后 UpdateColumn<br/>cnt_comment 加 1"]
    D --> E{"ParentIdentity 非空"}
    E -->|是| F["未指定 Model 的 UpdateColumn<br/>目标表只能靠 GORM 内部状态推断"]
    G["POST /feed.Post/Action"] --> H["models.LikeAction"]
    H --> I{"action_type"}
    I -->|post| J["Model FeedPost"]
    I -->|comment| K["Model FeedComment"]
    I -->|其他| L["未设置 Model 目标表不确定"]
    J --> M{"action_op"}
    K --> M
    M -->|ilike| N["column 为 cnt_like 计数加 1"]
    M -->|unlike| O["条件误写成 action_type 等于 unlike<br/>column 保持空串"]
    O --> P["UpdateColumn 空列名 SQL 错误"]
    Q["POST /feed.Post/DeleteComment"] --> R["models.DeleteComment 空函数<br/>永远返回 nil"]
    R --> S["接口返回成功但无任何删除"]

6. 审计发现

6.1 安全

级别 位置 问题
internal/logic/post/remove.go:17-29 + internal/models/query.go:34-37 越权删除动态Remove 拿到 JWT 后直接用 in.IdentityDeletePost,模型层是 Where("identity = ?", identity).Delete(&FeedPost{})没有任何 passport_identity 归属条件 → 任意登录用户可删除他人动态(软删除)。
internal/logic/post/change.go:17-40 + internal/models/query.go:30-33 越权修改动态Change 同样只校验 in.Identity != ""ChargePostWhere("identity = ?", data.Identity).Updates(&data) → 可篡改任意用户的动态正文与公开状态。
internal/logic/post/fetch.go:15-36 + internal/models/query.go:60-71 私密动态泄露Fetch 丢弃 claimsPostList 的查询没有 is_open 条件、没有 status 条件、没有作者条件is_open=false 的"不公开"动态、status=-1 的被禁用用户动态都会被返回(internal/models/feed_post.go:12 明确 false-不公开)。
internal/logic/post/action.go:17-28 + internal/models/query.go:40-57 互动计数可无限刷量Action 只校验三个字符串非空,不校验调用者身份、不落库点赞人、无去重 → 任意登录用户对任意 identity 循环调用即可把 cnt_like 刷到任意值;且 action_type 可传任意值绕过模型绑定。
internal/logic/post/delete_comment.go:16-28 删除评论接口只校验 in.Identity 非空,不校验评论归属(底层当前是空实现,但设计上即越权入口)。
internal/logic/post/add_comment.go:23-34 + internal/models/query.go:103-120 评论不记录作者:data 只设了 Identity/PostIdentity/Content/ParentIdentityStd_Passport 全空,且模型函数签名里的 authorIdentity 形参在函数体内从未被使用query.go:103-120)→ 无法做归属校验、无法展示/追溯评论人。同时不校验 PostIdentity 是否存在或可见,可对任意/不存在的动态灌评论。
internal/logic/tag/create.go:20-36 任何登录用户可批量创建标签,无权限校验、无去重、无长度限制;Keyutils.UUID()create.go:26)与标签内容无关联,标签字典的键不可读。
internal/logic/post/create.go:22-24 只校验 content != "",无长度上限、无发布频率限制、无敏感内容校验;附件 urlattach_type 由请求方直接写入(create.go:32-41)。

6.2 正确性与逻辑缺陷

级别 位置 问题
internal/models/query.go:67 分页完全失效,且页码被当成条数tx.Limit(int(page)).Offset(int((page - 1) * size)) 中第一个参数应为 size 却传了 page;更关键的是 tx.Limit(...).Offset(...) 的返回值未回写 tx,后续 tx.Find 用的是没有 Limit/Offset 的语句 → 实际返回全表(并受第 68 行 Count 影响)。logic/post/fetch.go:26 传入 (in.PageNo, in.PageSize) 的顺序本身是对的,缺陷在模型层。
internal/models/query.go:122-125 DeleteComment 是空实现,函数体只有 return,永远返回 nil → Post.DeleteComment 永远返回"成功",但没有任何删除动作;logic/post/delete_comment.go:24err 判断因此永远不会触发。
internal/models/query.go:60-71 + internal/logic/post/fetch.go:51-63 列表恒不返回标签与附件PostListtx.Find(&list),没有 Preload("Tags")/Preload("Attachs");而 FeedPost.Tags/Attachs 的标签是 gorm:"-"internal/models/feed_post.go:16-17GORM 永远不会加载它们 → AssembleList 里的两个 for 循环永不执行,post.Tags/post.Attachs 恒为空切片。
internal/models/query.go:40-57 点踩功能不可用。第 50 行把判断写成了 } else if action_type == "unlike" {(应为 action_op == "unlike")→ 点踩时 column 保持空串,第 53 行执行 UpdateColumn("", gorm.Expr(" + ?", 1)),列名非法导致 SQL 错误、接口报错。另:action_type"post"/"comment" 时第 43-47 行不会调用 .Model(...)tx 仍是裸 impl.DBService,目标表不确定。
internal/logic/post/change.go:25-40 + internal/models/query.go:30-33 修改动态无法改为不公开,附件被静默丢弃ChargePost 用结构体 Updates(&data)GORM 结构体更新会忽略零值 → is_open=false 永远写不进去;change.go:31-39 组装的 postData.Attachs 只放在 gorm:"-" 字段上,ChargePost 完全不处理 → 附件修改无效(且是"改了但没报错")。
internal/models/query.go:81,89 ModifyTags/DeleteTags 使用 Where("identity = ?", ...),而 FeedTags 没有 identityinternal/models/feed_tags.go:8-12 只有 gorm.Model + key + content)→ SQL 会因列不存在而报错。两函数当前无调用点(死代码),一旦接入即报错。
internal/logic/tag/list.go:20-32 标签列表恒为空。第 25-31 行已经把 tags 组装进 res,第 32 行却 return &pb.TagListReply{}, nil 新建了一个空对象返回 → tagstotal 全部丢失。
internal/models/query.go:68,99 err = tx.Find(&list).Count(&cnt).ErrorFindCount 共用同一链,Count 会重新构建并执行一次统计查询,Find 的错误可能被 Count 的结果覆盖,RowsAffected/总数语义不清;TagsListquery.go:96-101)同样写法,且具名返回值 list 未被赋值。
internal/models/query.go:63-64 标签过滤构造不可靠:tx.Joins("join feed_relate_tags on ...", impl.DBService.Where(&FeedRelateTags{Key: tag})) 把一个 *gorm.DB 当作 Joins 的参数值传入(应为 ? 参数);紧接着第 64 行又用未限定表名的 Where("key = ?", tag) 二次过滤 → 生成的 SQL 与预期语义不一致(该分支同时因分页失效而难以验证)。
internal/models/query.go:103-120 AddComment 事务内链式顺序颠倒:第 106 行 tx.Create(comment).Table("feed_post").Where(...).UpdateColumn(...) 先插入评论、再切换目标表去更新帖子计数,与注释"添加评论,更新文章评论量"的书写顺序相反;第 112 行对"被评论的评论"更新时未指定 Model/Table,目标表只能由 GORM 内部语句状态决定(可能落到 feed_comment 自身的 cnt_comment),无法从代码确证。
internal/logic/post/fetch.go:19-24internal/logic/timeline/*.go:19-24internal/logic/tag/post_list.go:19-24internal/logic/post/comment_list.go:19-24 page_size 只做了下限兜底、没有上限if in.GetPageSize() <= 0 { in.PageSize = 10 }fetchif in.GetPageSize() < 10 { in.PageSize = 50 }(其余)两种语义还不一致(后者把传 5 放大成 50传 20 却保持 20配合分页失效可一次要求全表。
internal/logic/post/add_comment.go:23-30 允许 in.PostIdentity 为空串(无任何校验)→ 可写入 post_identity 为空的孤儿评论;同时 in.Identity 被当作 ParentIdentity,但未校验该父评论是否属于同一 post_identity
internal/logic/post/fetch.go:26 标签过滤参数取自 in.Params["key"],键名是硬编码的魔法字符串,与 TagItem.Key 语义耦合;传其他键(如 tag)会被静默忽略。
internal/models/query.go:11 InitData() 直接 return nil,而 cmd/main/main.go:38 仍以 srv.Use(models.InitData) 注册(空初始化)。
internal/models/feed_relate_attachs.go:6-12 FeedRelateAttach 内嵌 types.Std_IDIdentity(已含 identity varchar(36) uniqueIndex)后又显式声明 Identity string同名列遮蔽;实际生效的是显式字段,但列定义重复声明,依赖 GORM 的字段冲突消解规则。表名 feed_relate_attach 为单数,与 feed_relate_tagsfeed_post 命名风格不一致。

6.3 未完成实现

类别 内容 证据
TODO 占位 RPC9 个) Timeline.Recommend/Friend/Follow/HotTag.PostListPost.CommentListSetting.InfoSetting.Rights internal/logic/timeline/{recommend,friend,follow,hot}.go:26internal/logic/tag/post_list.go:26internal/logic/post/comment_list.go:26internal/logic/setting/{info,rights}.go:18-20
空实现函数 models.DeleteComment(只有 return internal/models/query.go:122-125
空实现函数 models.InitData(只有 return nil internal/models/query.go:10-12
无调用点的死函数 models.ModifyTagsmodels.DeleteTags(且 SQL 列名不存在,见 6.2 internal/models/query.go:80-94
未接入聚合 service/ 为空目录,无 Expose/Dependenciespkgs/allpkgs/ecmallgo.mod 均未引用本模块 全仓库对 module/social/feed 的引用只有 scripts/api-docgen/go.mod:18,47 与根 README.md:218
gateway 未接线 模块内无任何 RegisterPostHandlerServer 等调用,Server.Mux 也从未被赋值 internal/server/new.go:16-36grep RegisterPostHandlerServer module/social/feed 无匹配)
配置文件名错位 etc/passport_dev.yaml/passport_prod.yaml/passport_test.yaml 的内容(Service: feed、含 Databases/MicroService/WeChatConf/Kyc/Token)才是与本模块匹配的配置,但 conf.Newfeed_<mode>.yaml 取名SDK conf/new.go:35-36)→ 这三个文件永远不会被加载 etc/passport_prod.yaml:1-2internal/config/config.go:15-26、SDK git.apinb.com/bsm-sdk/core/conf/new.go:35-42
README 为空壳 module/social/feed/README.md 全文只有标题 # content README.md:1

6.4 健壮性与可维护性

级别 位置 问题
etc/feed_dev.yaml:1-4etc/feed_prod.yaml:1-4etc/feed_test.yaml:1-4 三个 feed 配置都用 Name/ListenOn/Dsn,而 SrvConfig 只识别 Service/Port/Databasesinternal/config/config.go:17-18。SDK 在 YAML 解析前先检查文件是否含子串 "Service:"git.apinb.com/bsm-sdk/core/conf/new.go:58-60),这三个文件只含 ServiceName: → 直接 log.Fatalln("ERROR: Service Not Nil") 退出;即使能加载,Spec.Service 仍为空,conf.NotNilconfig.go:56)同样致命。
internal/impl/impl.go:24 + etc/feed_*.yaml 三个 feed 配置均无 Databases 段(用的是 Dsnwith.Databases(nil, nil) 会直接 panic("No Database Source Found !")git.apinb.com/bsm-sdk/core/with/databases.go:13-15)。
etc/feed_dev.yaml:16-19feed_prod.yaml:15-18feed_test.yaml:16-19 匿名清单写成顶层 Anonymous: {Key, Urls},而 SDK 的匿名清单在 conf.MicroServiceConf.Anonymousconf/types.go:25)→ Anonymous 整块被忽略;三个配置也都没有 MicroService 段,MsConf 为 nil → service.Start 跳过 etcd 注册SDK service/service.go:66)。passport_prod.yaml:15 的匿名项还写成 feed.ping.hello(模块内不存在此方法)。
internal/server/new.go:16-36cmd/main/main.go:33 既没有 gwRuntime.NewServeMux(),也没有 pb.RegisterXxxHandlerServer 调用,Server.Mux 恒为 nil → cmd/main/main.go:33 把 nil 传给 GatewayMuxservice.Starthttp.ListenAndServe(addr, nil)DefaultServeMux,所有 /feed.* 路径 404。模块自身也无法像 wallet 那样通过 service.Expose 被聚合宿主接线。
etc/feed_prod.yaml:4etc/feed_dev.yaml:4 数据库连接串明文提交入库,含生产库账号与口令(postgres://prod:MakeW2023~PROD@192.168.0.224:5432/scf)、开发库地址(47.108.57.74:5432/milu)。
etc/supervisor.bsm-apps-passport.conf:1 文件名仍是 passport内容 program 名却是 bsm-apps-feed;与 etc/passport_*.yaml 一并说明该目录是从 passport 服务复制而来,未清理。
etc/passport_prod.yaml:31-35 同一份配置里同时存在 Kyc:KycConf: 两个段,而 config.SrvConfig 只映射 Kycinternal/config/config.go:25)→ KycConf 整块为死配置。
internal/impl/impl.go:21-24 MemorySericeRedisService 被初始化,但全模块无任何使用点(死代码);EtcdService 仅在微服务注册路径使用,而 MsConf 恰好为 nil。
internal/models/query.go:69 残留调试语句 fmt.Print("posts", list),会把整页动态实体(含 content打印到进程标准输出。
cmd/cli/main.go:9-12 CLI 入口只做 config.New("feed")fmt.Println(config.Spec.Databases),调试残留;且服务键传小写 "feed",与主入口的 ServiceKey = "Feed" 不一致(conf.New 拼文件名时会 ToLower,但 vars.ServiceKey 保留原样,影响日志/注册键大小写)。
全模块 0 个 *_test.gotest/ 下只有一个空的 lint/ 目录,无任何请求样例。
internal/logic/post/fetch.go:39-67 AssembleList 是包级导出函数但只在本文件使用,且内部对 res 使用具名返回追加(res = append(res, post)),在 data 为空时返回 nil 切片而非空切片,前端需要额外判空。

7. 风险汇总

编号 级别 问题 影响面
F1 删除动态无归属校验,可删他人动态 数据完整性、越权
F2 修改动态无归属校验,可篡改他人动态 数据完整性、越权
F3 列表无 is_open/status 过滤,私密动态泄露 隐私泄露
F4 互动计数无去重、无身份绑定,可无限刷量 数据可信性
F5 分页 Limit(page) + 未回写 → 分页失效并全表返回 功能不可用 + 性能风险
F6 DeleteComment 空实现,删除接口永远"成功" 功能不可用
F7 列表恒不返回标签与附件(gorm:"-" + 无 Preload 功能不可用
F8 点踩计数条件写错导致 SQL 列名为空 功能不可用
F9 修改动态无法改为不公开,附件修改被静默丢弃 功能不正确
F10 feed_*.yaml 字段名不匹配 → 启动即退出;无 Databases → panic 部署可用性
F11 gateway handler 从未注册、Mux 为 nil、无 service.Expose 接口不可用、无法聚合
F12 feed_tagsidentity 列却被 Where("identity") 使用 接入即报错
F13 评论不记录作者、不校验所属动态 越权、数据不可追溯
F14 标签列表返回值被丢弃,恒为空 功能不可用
F15 AddComment 事务内 Table 用法与未指定 Model 的更新 结果不确定、可能整事务回滚
F16 9 个 RPC 为 TODO 占位,返回 (nil, nil) 功能缺失、网关异常
F17 生产/开发数据库连接串明文入库 凭据泄露
F18 page_size 无上限、上下限语义不一致 资源滥用
F19 死代码、调试 fmt.Print、无测试、README 空壳 可维护性

8. 修复建议(务实项)

  1. 补归属校验F1/F2/F13Remove/Change 在 logic 层先按 identity + passport_identity 查一次动态,查不到即返回"无权操作";或把模型函数改为 DeletePost(identity, passportIdentity)ChargePost(data, passportIdentity) 并加上 Where("passport_identity = ?", ...)。评论同理,把 AddCommentauthorIdentity 真正写进 FeedComment.Std_Passport,删除评论按 identity + passport_identity 定位。
  2. 修可见性过滤F3PostList 增加 is_open = true 与作者本人可见的 OR 条件,并按需过滤 status <> -1Fetch 不要丢弃 claims用调用者身份决定是否可见私密动态。
  3. 修分页F5/F18query.go:67Limit(int(page)) 改为 Limit(int(size)),并把 tx = tx.Limit(...).Offset(...) 回写;给 page_size 设上限(如 50并把 fetch.gocomment_list.go、四个 timeline、tag/post_list.go 的兜底逻辑统一为 <= 0 则取默认值
  4. 修删除与计数F6/F8DeleteComment 补实现(软删除 + 同事务把对应 feed_post.cnt_comment 减 1并做下限保护LikeAction 的第 50 行改为 action_op == "unlike"action_type 用白名单(post/comment)校验并显式设置 Modelcolumn 为空时直接返回参数错误。
  5. 修标签与附件读写F7/F9/F12/F14PostListPreload("Tags")/Preload("Attachs") 之前,需先把 FeedPost.Tags/Attachsgorm:"-" 去掉或改写为显式二次查询(按 post_identity IN (...) 批量取关联表再回填);FeedRelateTags.Content 去掉 gorm:"-" 才能真正存标签名;ChangeSelect("content","is_open").Updates(...) 或 map 更新以写入 is_open=false,并显式处理附件的增删;FeedTagsWhere("identity = ?") 改为按主键或 key 定位;tag/list.go:32 改为 return res, nil
  6. 配置F10etc/feed_dev.yaml/feed_prod.yaml/feed_test.yamlSrvConfig 重写(Service/Port/Databases/Cache/MicroService.Anonymous/Gateway),或直接把已正确的 etc/passport_*.yaml 重命名为 feed_*.yaml 并删除冗余文件;config.New 里补 conf.NotNil(Spec.Databases);把 Dsn 中的口令改为环境变量注入并轮换已泄露的生产口令。
  7. 接线与启动F11internal/server/new.go 中给 MuxgwRuntime.NewServeMux() 并注册 4 组 handler或在 service/ 下补一份与 wallet 同构的 expose.go/dependencies.go 以便接入 pkgs/all;否则 Gateway.Enable=true 只会得到一个 404 网关。
  8. 补初始化与占位反馈F16models.InitData 要么实现必要的基础数据,要么在 cmd/main/main.go:38 去掉 srv.Use(models.InitData)9 个 TODO RPC 在实现前应返回明确的 Unimplemented 错误,而不是 (nil, nil)
  9. 清理F19删除 internal/models/query.go:69fmt.Printinternal/models/query.go:80-94ModifyTags/DeleteTags(无调用点且 SQL 错误)、internal/impl/impl.go 中未使用的 Redis/Memory 初始化、etc/passport_prod.yaml:28-33KycConf 死配置;README.md 至少补一段服务说明。
  10. 测试:为 PostList(分页与可见性过滤)、LikeAction(点赞/点踩各一次)、DeleteCommentAddComment(作者落库与计数一致)各写一条正例 + 一条边界用例。

本报告只列出与现有实现直接相关的修复项。为 feed 引入"统一内容框架""时间线抽象层""互动事件总线""DTO/VO 分层"一类改造不在建议范围内——上述 10 条都是对现有函数与配置的最小修正。