Files
36Wisdom/CLAUDE.md
T
2026-08-13 11:45:45 +08:00

10 KiB

CLAUDE.md

目录结构与职责(硬性约束)

biz/ 是泛化占位名,不是固定目录命名。表格中 biz/ 代表「业务模块目录」,各项目必须按自身业务命名替换(本项目为 kb/),禁止新项目照抄 biz/;ui-src/data/workspace/ 亦为本项目目录名,各项目按自身命名。

目录 职责 强约束
common/ 通用层:HTTP 服务与鉴权中间件、文件解析(parser + pdf/docx/html/text)、中文分词、向量 JSON、DAO 基类、查询缓存、协程池封装(pool.go) 不得依赖业务模块包(仅 pool.go 依赖 biz/consts 取池默认值,既成事实);新增跨模块通用能力放这里
biz/consts/ 常量集中地:表名(table_name.go)、状态(status.go)、内容类型、默认参数与各协程池默认大小(consts.go) 业务常量一律在此集中,禁止散落 magic number;新增池默认大小在此定义
biz/model/ entity(表结构,与 DAO 一一对应)、dto(请求/响应结构,g.Meta 内嵌定义路由)、domain(领域模型,如 RiskSummary / Citation) entity 只做表映射,不带业务逻辑;dto 是 controller 与 HTTP 的唯一出入口
biz/dao/ 单表数据访问,每表一个文件 无业务逻辑;查询经 base_dao 缓存
biz/service/ 业务逻辑:规则校验、文件读写、事务、跨表组装、调用 dao、LLM 编排 不直接写 HTTP 响应(例外见下);并行任务走 common 协程池
biz/controller/ 接口层:接收参数、调用 service、组装返回值 见「分层职责规范」;禁止调用 dao
前端目录 前台 ui-src/ 为 uni-app (Vue 3) 多端工程(Android/iOS/平板/微信小程序/H5);后台 admin-src/ 为 Vue 3 + Element Plus Web 工程 前台/后台开发用 vite 代理,生产构建产物 dist 由后端托管;多端工程差异见技术设计.md
运行时数据目录 SQLite 库、上传/解析文件(本项目 data/ workspace/) 不提交 git;删除即丢失数据,改动前先确认

分层职责规范(硬性要求)

严格分层 controller → service → dao,禁止跨层调用(controller 禁止直接调 dao)。

目录 职责 禁止
controller biz/controller 接收参数(依赖 DTO v tag 自动校验)、调用 service、组装返回值 直接调用 dao;手写业务规则校验(库表依赖/跨字段,应下沉 service);文件 IO;状态流转;跨表数据组装
service biz/service 业务逻辑:规则校验、文件读写、事务、跨表组装、调用 dao 直接写 HTTP 响应(例外见下)
dao biz/dao 单表数据访问,每表一个文件 业务逻辑

例外:SSE 流式响应、HTML/文件导出等"直接写响应体"的场景由 controller 完成——这是"值返回"的流式形式,事件序列化、心跳属 HTTP 协议职责,保留在 controller。

分层文件对齐与代码模式(硬性要求)

  • 每张业务表对应一组 entity / dao / service / controller / dto 文件,数量严格对齐;虚拟表(向量 vec0 / FTS5)不建独立分层文件,由主表 dao 统一管理
  • 不建 parser/rag 等技术目录:纯技术能力(文档解析、中文分词、向量序列化)平铺在 common/;业务编排(分块、检索、工作流)归入对应 service 文件
  • entity:每文件一张表,orm 标签与列名一致,时间字段用 *gtime.Time,只做表映射
  • dao:单例 var Xxx = &xxxDao{},init()CREATE TABLE IF NOT EXISTS + 索引 + 迁移;通用 CRUD 复用 common/base_dao.go(InsertAndReturnId / GetOneByPk / UpdateByPk / DeleteByPk)
  • controller:结构体名决定路由前缀(如 dataset/dataset),接口定义在 dto(g.Meta 携带 path/method/summary)
  • 接口只允许 GET / POST:写操作传 JSON body(或 multipart),读操作走 query params;无 PUT/DELETE
  • dao 查询缓存:查询用 gdb.CacheOption(TTL 来自配置),写操作后必须清对应缓存,否则出现"库里已改、查询还是旧值"

并发规范(grpool 协程池)

  • 可并行的场景:纯 IO 任务——读查询、LLM/Embedding 调用、文件读取。SQLite 写一律回主 goroutine 串行(无 WAL 时并发写会 database is locked,锁定风险归零,并发只赢在 IO 等待上)
  • 新增并行点的固定三处:common/pool.go 加池变量(grpool 封装)→ biz/consts 加默认大小 → config.ymlpool 段加 key: 并发度(缺失或非法时回退默认值)
  • 禁止直接用裸 go 启动并行工作负载,一律走 common 的池(池清单与默认值见 README 配置说明)
  • 防死锁:等待链单向「主 → A池 → B池」,被等待池的任务内不得再等待任何池(会饿死 worker);池无 Wait 方法,等待用调用方 sync.WaitGroup,任务结果经 buffered channel 回主 goroutine
  • 共享状态安全(内存):池内任务并发执行,共享实例(service 单例、model 句柄等)只允许只读访问;可变字段必须在提交池之前由主 goroutine 一次性预置,任务内禁止写共享字段——Go map 并发写直接 fatal error: concurrent map writes,无锁、无降级、不可恢复,只能崩溃重启。需要可变共享状态时按优先级:① 无共享(任务内新建、buffered channel 传递结果)② 锁(sync.Mutex/RWMutex,锁内只做内存操作,LLM/DB 等 IO 放锁外)③ sync/atomic(仅限 int 类标量计数/标志,如 atomic.AddInt64,并发计数禁用普通 ++;复合结构不要用 atomic,指针 CAS 属例外)
  • 锁的使用:互斥场景唯一入口是 common.WithLock[T any](ctx, key, expire, retries, retryInterval, fn func() (T, error)) (T, error)——泛型回调,业务返回值经 T 原样透出给下游;内部按 config.yml 自动选择锁实现(配置了 redis 节点 → redis 锁,跨实例互斥,SET NX EX + token 对比删除防误删他人锁;未配置 → gcache 内存锁,单实例互斥),禁止直接用 gcache/gredis 自己实现加锁。拿不到锁(被占用,ErrLockHeld)最多重试 retries 次、每次间隔 retryInterval(retries=0 立即失败;ctx 取消/超时同样终止等待);中间件故障不重试直接返回。锁自动释放:无论 fn 成功、失败还是 panic,defer 释放。expire 必须 > 0(进程崩溃兜底不死锁),fn 耗时必须在 expire 前完成,fn 内禁止长耗时 IO(LLM/DB 调用);锁粒度按业务唯一键尽量小
  • go 允许的例外:go func(){ wg.Wait(); close(ch) }() 收尾惯用法、SSE 心跳、流式管道(Stream 读写)等长生命周期/非工作负载协程

文档职责(三文档体系)

文档 职责 何时补充/更新
CLAUDE.md(本文件) 公司通用开发规范:分层职责、代码模式、并发/事务/缓存约束、流程 规范变化时
README.md 项目功能介绍:架构、数据流、表清单、功能模块、API 清单、使用说明 功能增减时
技术设计.md 实现细节与技术决策:DDL、检索参数、风险与备选方案 关键技术决策/参数变化时

开发流程(文档驱动,硬性要求)

永远以文档驱动开发:用户提出开发需求 → 先给出实现方案(技术选型、影响面、改动清单) → 用户确认后先补充文档再动手写代码。补充哪个文档取决于内容性质:规范 → 本文件,功能 → README,实现细节/技术决策 → 技术设计.md。禁止未经确认直接开发,禁止先写代码后补文档。

数据访问规范(硬性要求)

  • 事务:涉及多张表的增删改操作必须包数据库事务,禁止逐表裸调用。事务放 dao 层方法内,service 层负责编排;tx.Begin 后必须用 defer 防护已提交后的二次 Rollback
  • SQL 单表约束:每个 SQL 只允许访问一张表,禁止 JOIN 与跨表子查询(IN (SELECT ...) / EXISTS);跨表数据一律拆为多条单表 SQL + 应用层内存组装——先取外键 id 列表,再对目标表 IN 查询;IN 参数须按 ≤100 分批(SQLite 变量数上限 999)
  • 禁止 N+1 查询:禁止在循环中逐条查库。循环场景一律改为批处理——一次 ListByXxx 取回后按外键在内存分组
  • 缓存一致性:DAO 查询走缓存(TTL 来自 database.cache.ttl),写操作后必须清对应缓存
  • 批处理 SQL:批量写入用 InsertAll 类方法,批量删除用 IN 子句,禁止循环单条 INSERT/DELETE
  • 配置即使用:config.yml 中出现 redis / mq 等中间件配置时,代码必须实际接入使用,禁止"配置了但代码不用"或"代码写死但配置缺失"
  • 消息/回调幂等:接入 MQ / Webhook 时,消费与回调处理必须幂等——MQ 至少一次语义、webhook 失败重试都可能重复投递同一事件,禁止依赖"只投一次"假设。幂等手段:以业务唯一键(如 任务ID + 事件类型)先查重或建唯一约束再落库,重复事件直接忽略;重试与补偿逻辑同样要防重复执行

运维部署规范(硬性要求)

  • 部署形态:Docker Compose 单机部署,前后端一体单端口;运行时数据目录挂载持久化,容器重建不丢数据。部署文件在项目根目录(Dockerfile / docker-compose.yml),启动与使用见 README
  • 数据即文件:文件型存储,备份 = 打包「数据库目录(小而关键)+ 上传文件目录(大而可重建)」两个目录;迁移 = 拷贝到新机器即可
  • 运行时数据与代码分离:数据目录不提交 git;删除即丢数据,改动前先确认
  • 配置即文件:项目配置文件为唯一配置入口(监听端口、并发度等),环境变量覆盖无效

约定

  • controller 方法签名固定为 (ctx, *dto.XxxReq) (*dto.XxxRes, error),实例注册模式 var Xxx = &xxx{}
  • 参数校验优先用 GoFrame DTO 校验:请求结构体用 v tag(required / regex / in 等)声明,框架自动校验并返回错误,controller 不手写校验;仅 DTO 表达不了的业务规则(跨字段依赖、查库校验如重名、取值范围依赖配置)放 service。JSON 格式解析可留在 controller 或下沉 service,但须保持与调用点一致
  • 响应组装(实体 → DTO 字段映射)在 controller 进行
  • service 方法签名 ctx 开头,错误统一用 gerror
  • 编译验证:go build ./...