Files
a-share-analysis/docs/governance/code-development-standards.md
T

16 KiB
Raw Permalink Blame History

代码开发规范

1. 目标

本规范保证代码对普通开发者可连续阅读、对关键业务可解释、对历史版本可维护。规范适用于 Vue 3 前端、NestJS 后端、调度器、RocketMQ Worker、数据适配器、规则引擎和 AI 分析模块。

2. 技术基线

  • 前端:Vue 3、Vite、TypeScript、Dart Sass、Naive UI;浏览器 HTTP 请求统一使用 Axios,接口类型由 OpenAPI 生成。
  • 后端:NestJS、TypeScript。
  • 数据库:MySQL 5.7.x,字符集 utf8mb4,排序规则 utf8mb4_general_ci;缓存:Redis;消息队列:RocketMQ 5.x。
  • 图表:Lightweight Charts负责K线和分时,ECharts负责热力图、分布和资金趋势。
  • AI:由服务端调用兼容 OpenAI 格式的付费 API,密钥不得下发浏览器。

依赖版本必须由锁文件固定。引入新依赖前必须在功能设计中说明用途、现有替代方案、维护状态、许可证、安全风险、前端体积或服务端运行成本以及退出方案,验证通过后才能安装。

3. 文件组织与拆分尺度

代码按业务能力组织,而不是把每个函数、类型和常量拆成独立文件。

应当拆分

  • 文件同时承担两个可独立变化的业务职责。
  • 模块形成稳定复用边界,且确实被多个调用方使用。
  • 单个文件已经难以在一次正常阅读中理解完整流程。
  • 外部系统适配、持久化、消息传输与纯业务计算需要隔离测试。

不应拆分

  • 只为减少行数创建单函数文件。
  • 将同一业务流程拆成大量需要来回跳转的薄包装。
  • 为尚未出现的复用场景提前抽象。
  • 为形式上的“分层”添加不产生业务价值的转发层。

复用代码、工具类、Composable 或组件只有在真实重复已经出现、职责边界稳定且抽取确实能降低维护成本时才建立。名称相似、预计以后会用或单纯减少文件行数不构成抽取依据;Review 必须能说明抽取的现有调用方和所有权。

Vue 页面可以包含与页面强绑定的小型展示逻辑;当子区域拥有独立状态、独立测试价值或被复用时再提取组件。NestJS 模块应让入口、服务和领域计算的调用关系容易跟读。

4. 命名与语言

  • 文件、变量、函数、类型和数据库字段使用清晰英文命名。
  • 产品文案、业务日志、错误说明、代码注释和提交摘要优先使用中文。
  • 禁止 data1、temp2、handleThing 等缺乏语义的命名。
  • A 股领域词汇使用统一词典,例如 limitUpCount、brokenBoardRate、marketBreadth。
  • 时间字段必须体现语义,例如 tradeDate、snapshotAt、sourceUpdatedAt,不得统称 time。

5. 注释规范

代码必须写详细但有信息价值的中文注释。注释解释“为什么、口径、限制和风险”,不复述语法。

必须注释

  • 对外导出的公共接口、类型和关键函数。
  • 涨跌停、复权、停牌、午间休市等 A 股特殊规则。
  • 指标公式、阈值来源、单位、数据日期和缺失值处理。
  • RocketMQ 的 Topic、Tag、Group、幂等、重试、死信和消息顺序假设。
  • Redis 与 MySQL 一致性边界和缓存失效策略。
  • AI 输入裁剪、事实约束、降级和成本控制逻辑。
  • 看似可以简化但因兼容性或历史原因必须保留的实现。
  • 异常恢复、降级策略和安全边界。

禁止注释

// 将 count 加一
count += 1;

此类注释没有提供代码之外的信息。代码变更时,注释必须同步更新;失真的注释视为缺陷。

6. 类型与数据契约

  • 禁止无理由使用 any;外部输入先以 unknown 接收并校验。
  • HTTP API 使用版本化 OpenAPI 契约。
  • RocketMQ 事件包含 eventId、eventType、schemaVersion、occurredAt、tradeDate、traceId 和 idempotencyKey。
  • Topic 使用稳定的业务域命名,环境隔离由部署配置的前缀完成;Tag 表达同一 Topic 内的事件类型,Consumer Group 表达独立消费进度,禁止把临时实例名作为 Group。RocketMQ 5.x Proxy Producer 不配置旧 Remoting Producer Group。
  • 需要顺序处理的消息必须明确消息组键,例如证券代码或交易日;未声明顺序键时不得依赖全局到达顺序。
  • 金额、成交额和比率必须明确单位,避免同一字段混用元、万元、亿元或百分比小数。
  • 数据来源、采集时间、交易日期、完整性和口径版本必须可追溯。

7. 错误处理与日志

  • 不吞异常,不使用空 catch。
  • 对外错误不泄露密钥、内部地址和原始上游响应。
  • 日志使用结构化字段,至少包含 traceId、任务类型、交易日期和数据源。
  • 免费数据源失败时保留最后有效快照,并明确标记陈旧,禁止伪装实时数据。
  • AI 失败不得影响规则分析和基础行情展示。

8. 环境配置规范

  • 应用只接受 development 与 production,环境必须显式设置。生产启动不得回退到开发默认值、模拟数据、热更新或调试模式。
  • API、Scheduler、Worker 必须使用各自的配置 Schema,在进程启动时验证类型、格式、范围和条件必填项;配置错误立即退出。
  • 同一进程的开发与生产模板必须保持相同变量名。环境差异通过配置值、依赖注入和 Adapter 收敛,禁止在业务代码中散落 APP_ENV 分支。
  • 只有 *.env.example 可以提交;真实 .env、*.env.local、密码、Token 和 API Key 必须被 Git 忽略并由运行环境注入。
  • Web 只能使用公开的 VITE_* 变量。数据库、Redis、RocketMQ、数据源和 AI 凭据禁止进入前端变量或构建产物。
  • MySQL、RocketMQ、正式数据源与 AI 使用显式功能开关;关闭时连接变量可为空,开启后缺少任何必填值都必须拒绝启动。
  • 开发源码进程与 Ubuntu 生产源码部署复用同一变量契约;项目脚本不得安装、启动、停止或删除 Redis、MySQL、RocketMQ 等外部基础设施。
  • 生产配置必须位于发布目录之外,切换应用版本不得覆盖配置;日志和诊断输出必须对配置值脱敏。

9. 前端规范

  • 页面不得直接访问第三方行情源、Redis、RocketMQ 或 AI 服务。
  • web/src/utils/request.ts 是唯一 Axios 实例、拦截器、超时、取消和通用错误处理入口;其他文件不得创建 Axios 实例。
  • 所有业务接口按后端业务域放在 web/src/api/<业务域>/index.ts,API 文件统一调用 @/utils/request,不得绕过封装。
  • 页面、组件、Store 和 Composable 不得直接导入 Axios、调用浏览器 fetch、拼接业务 URL 或依赖 AxiosResponse。
  • 后端 OpenAPI 文档是本项目 HTTP 契约的机器可读描述,与兼容 OpenAI API 的 AI Provider 协议无关。前端保留 openapi-typescript 生成类型,实际请求由 Axios 发出。
  • API 函数优先使用生成的请求和响应类型,返回 DTO 或明确的业务结果;禁止用 any 取代已有契约,也不得把 Axios 类型扩散到功能层。
  • DTO 到 ViewModel 的转换放在对应功能的 adapter 中;页面不得直接绑定后端 DTO,以免传输字段变化扩散到展示层。
  • 全局服务器状态使用 TanStack Vue Query,本地界面偏好使用 Pinia或轻量组合式函数。
  • Sass token集中管理颜色、间距、圆角、字号和断点。
  • A 股默认红涨绿跌,但状态必须同时提供文字或形状,不能只依赖颜色。
  • 图表必须标注单位、时间范围、数据日期和更新时间。
  • 所有核心交互覆盖加载、空、部分数据、错误和过期状态。

10. 后端与任务规范

  • 后端以业务模块为一级目录,模块内部按实际职责建立 controllers、services、dto、domain、repositories、adapters、types、utils;小模块不强制凑齐全部目录。
  • Controller 只处理协议、边界参数和响应映射;Service 编排用例、事务与降级;Domain 保存纯业务规则和模型;Repository 描述持久化端口;Adapter 实现 MySQL、Redis、RocketMQ、外部数据源和 AI Provider 边界。
  • DTO、领域模型与数据库实体必须分离。领域层不得依赖 NestJS、TypeORM、Redis 或 RocketMQ SDK;基础设施实现依赖领域端口。
  • 禁止在应用级全局目录堆放所有 Controller 或 Service,禁止为目录外观创建空层或只做无意义转发的包装。跨模块代码只有在真实复用且所有者明确后才上移到 server/libs。
  • 分层参考 RuoYi 的职责清晰性和成熟 NestJS 模块实践,不照搬 Java 目录,也不以分层数量衡量架构质量。
  • API、Scheduler和Worker作为独立进程维护,不在请求期间临时抓取全市场数据。
  • Scheduler 只决定何时投递;MessageBusAdapter 负责隔离客户端;RocketMQ 负责任务传输;Worker 负责执行;MySQL 负责事实记录;Redis 负责当前快照和短期状态。
  • 消费者必须幂等,消息确认只能发生在持久化或可验证处理完成后。
  • RocketMQ 可能重复投递;消费者不得把“收到一次”当作“只执行一次”,必须以 idempotencyKey 和业务唯一约束防重。
  • 普通重试使用 RocketMQ 消费重试;延迟任务使用受版本支持的定时/延迟消息;超过服务端 Consumer Group 最大投递次数后进入死信,必须可查询、告警和人工重放。
  • 业务代码只依赖项目定义的 MessageBusAdapter 和版本化事件契约,禁止直接依赖 RocketMQ SDK 类型;客户端、NameServer 或 Proxy 接入方式由基础设施适配层决定。
  • 免费数据源通过 Adapter 统一为内部模型,上层不得依赖供应商原始字段。

11. MySQL 5.7.x 兼容规范

  • 开发、测试和生产环境统一使用 MySQL 5.7.x,不设置最低补丁版本;禁止使用 latest、MySQL 5.6.x、8.x、MariaDB 或其他数据库代替兼容性验证。
  • 创建数据库必须显式声明 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;建表与迁移脚本必须显式保持 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci。
  • ORM 和数据库驱动必须声明 utf8mb4,连接建立后验证会话、数据库和表的排序规则为 utf8mb4_general_ci,不得依赖服务器默认值。
  • 文本列默认继承表级规则。列级改变字符集或排序规则必须由具体功能设计解释,并通过 ADR 审批。
  • 禁止使用 MySQL 8.x 专属或在 5.7 行为不同的公用表表达式、递归 CTE、窗口函数、函数索引、不可见索引、utf8mb4_0900_* 排序规则和 JSON 增强能力。
  • 不得依赖 CHECK 约束在 MySQL 8.x 的强制执行语义;MySQL 5.7.x 下的业务约束必须由边界校验、普通索引或可测试的写入逻辑保证。
  • utf8mb4_general_ci 不区分大小写。证券代码、外部事件 ID、幂等键等身份字段必须规范化输入,或显式采用经设计批准的二进制比较与唯一键,不能误把默认比较当成大小写敏感。
  • 索引长度按 MySQL 5.7.x 和 utf8mb4 最多四字节字符预算;超长标识使用受控长度、前缀索引或稳定摘要时,必须说明唯一性和碰撞处理。
  • JSON 字段只能使用 MySQL 5.7.x 已支持并经过真实环境验证的能力,查询和索引不得建立在 8.x 特性上。
  • 每份迁移脚本必须能够从上一版本基线在 MySQL 5.7.x 正向执行;禁止用直接修改共享数据库表代替迁移脚本。
  • 代码 Review 必须检查服务端版本、字符集、排序规则、SQL/迁移兼容性、索引长度和字符串比较语义;任一不符合项按 Important 处理并阻断发布。

Schema 基线规范

  • 每个新应用版本开发前必须封存上一已发布版本的开发库 Schema;只能读取 APP_ENV=development、a_share_analysis_dev 和 MySQL 5.7.x,主机仅允许回环地址或固定开发白名单 112.192.20.11,禁止连接、探测或读取生产数据库。配置优先读取 Git 忽略的 deploy/env/development/api.env.local。
  • 导出使用 MySQL 5.7.x mysqldump 的 --no-data --routines --triggers --events,完整保留表、索引、外键、视图、触发器、存储过程、函数和事件,不得包含任何表数据。
  • 规范化必须确定性删除导出时间、环境 DEFINER 和表选项中的当前 AUTO_INCREMENT=<值>,同时保留列定义的 AUTO_INCREMENT 属性和业务 SQL。
  • 账号密码只能通过受控子进程环境传递,不得进入参数、日志、SQL、清单或 checksum 文件。
  • manifest.yaml、checksum.sha256 和 schema.sql 的 SHA-256 必须一致;既有版本目录只允许内容完全一致的幂等验证,不允许覆盖。
  • MySQL 未启用时只创建状态为 not_applicable 的清单,不得创建空 schema.sql;启用但无对象时创建 format v2 的 captured_empty 清单和零对象计数,不伪造空 SQL;启用后的缺失基线必须阻断下一版本开发。既有 format v1 基线必须继续只读验证,不得静默升级或重写。

12. 测试要求

  • 功能和缺陷修复遵循测试先行:先看到测试因缺少行为而失败,再实现最小代码。
  • 纯规则和指标计算使用单元测试,期望值必须手工推导。
  • 数据源 Adapter 使用契约测试和固定样本。
  • HTTP 与 RocketMQ 使用契约测试;消息测试必须覆盖 Topic、Tag、Group、序列化、重复投递、重试和死信边界。
  • 关键页面使用 Playwright流程测试和视觉截图对比。
  • 合并前必须通过类型检查、测试、构建和适用的视觉验证。

13. 安全要求

  • 禁止提交 .env、API Key、密码、真实账号和生产数据。
  • AI 密钥、数据源凭证和支付凭证仅存在服务端密钥管理中。
  • SQL必须参数化;外部输入必须在边界处校验。
  • 日志和异常报告必须脱敏。
  • 每个功能和缺陷修复必须在设计中识别资产、数据、信任边界、攻击面、缓解措施和残余风险,在测试计划和 Review 中保存验证证据。
  • 按适用性检查认证与授权、输入校验、SQL/命令/模板注入、XSS、CSRF、CORS、SSRF、限流、凭据、敏感数据、日志、错误、依赖供应链、数据保留和滥用场景。
  • 外部数据源和 AI 还必须检查密钥隔离、上游不可信内容、提示词与事实边界、超时重试、成本控制和不可用降级。
  • 不适用项必须说明不适用于什么、为什么以及适用边界;空白、无、不适用 或 N/A 不能作为安全评估证据。

14. 性能要求

  • 每个功能必须说明延迟、吞吐、数据新鲜度或资源预算中适用的目标,并按风险选择验证强度。
  • 后端评估数据量与算法复杂度、MySQL 查询/索引/N+1、Redis 用途/TTL/一致性/击穿、API 载荷与分页、Scheduler/Worker 并发/幂等/背压,以及外部数据源和 AI 的超时/重试/批处理/成本。
  • 前端评估依赖与构建体积、渲染次数、长列表、图表数据量、交互响应和内存占用;不能只以开发环境主观感觉作为证据。
  • 高风险路径必须提供 benchmark、profile 或负载测试;低频治理逻辑等不适用场景必须写明无需负载测试的原因和边界。
  • 性能优化不得牺牲数据口径、安全、可观察性、异常恢复或 MySQL 5.7.x 兼容性;禁止无证据的预优化。

15. 代码 Review 要求

开发者完成自测不等于 Review 完成。审查者必须从功能 PRD、设计和 Git diff 出发,检查:

  • 业务行为是否真正满足验收标准,是否存在遗漏分支。
  • 类型、数据口径、缓存、消息、事务和异常恢复是否正确。
  • 是否引入安全、性能、并发、幂等或数据一致性风险。
  • 安全策略、性能目标和残余风险是否与实际实现一致,是否提供了风险相称的验证证据。
  • GitNexus 开发前影响是否覆盖其他功能、模块和同步修改,开发后实际影响是否与计划一致。
  • 前端是否遵守 Axios 单一封装和 API 分域,后端是否按业务模块内实际职责分层。
  • 复用抽取是否基于已出现的重复,新增依赖是否完成用途、安全、许可证、体积/成本和退出评估。
  • 测试是否能捕获真实回归,而非只验证 Mock 或实现细节。
  • 中文注释是否解释关键原因、口径和限制,是否存在失真注释。
  • 文件拆分是否便于常规阅读,是否出现过度抽象或巨型多职责文件。

Critical 和 Important 问题必须修复并复审。Minor 问题可以延期,但必须写明理由和后续功能编号。