跳到内容

工程约定

受众:开发者 摘要:编码、运行时和 starter 开发的统一规范。Agent 协作规范见 .ai/conventions.md;文档写作规范见 .ai/STYLE.md

基础约定

Spring Boot 基线4.x
JDK 编译运行17+
Maven groupIdcom.springopen
Java base packagecom.springopen
Starter base packagecom.springopen.starter.*
Maven artifactspring-open-*
配置前缀spring.open.*
环境变量前缀SPRING_OPEN_*(应用基础变量保留 APP_NAME / APP_VERSION / APP_PORT
数据库 / Redis 环境变量DB_HOST / DB_PORT / DB_DATABASE / DB_USERNAME / DB_PASSWORD / REDIS_HOST / REDIS_PORT / REDIS_PASSWORD
许可证Apache License 2.0
JSON 栈Jackson;Spring Boot 4 MVC 用 Jackson 3,内部持久化 / 队列 / 通知 / 第三方 Provider JSON 可用独立 Jackson 2 ObjectMapper Bean

模块分层 / 命名 / "新代码放哪儿"决策图见 模块地图.ai/decisions/adr-045-module-governance-taxonomy.md:基石 spring-open-starter-common(纯 Java 原语,包 com.springopen.starter.common.*)/ spring-open-starter-<cap>(引擎,禁 @RestController,包 com.springopen.starter.<cap>.*,例如 Web MVC 为 com.springopen.starter.webmvc.*)/ spring-open-core-<x>(运行时底座,直接挂在 spring-open-core/ 下)/ spring-open-module-<biz>(业务叶子)。./.ai/scripts/check modules 静态校验这些边界(starter 禁 REST、starter 包前缀、core↛业务、业务↛业务、core 直接挂载)。

第三方依赖版本优先由 Spring Boot parent、BOM 或 spring-open-dependencies 统一管理,子模块不散落硬编码外部依赖版本。

项目版本号采用语义化三段式 x.y.zx 表示重大架构调整或兼容性边界变化,通常可能包含破坏性变更;y 表示向后兼容的功能新增、模块增强或能力扩展;z 表示向后兼容的缺陷修复、文档修正、依赖安全补丁或小范围维护。

框架主线、starter、业务模块、视图和应用装配统一按 Java 17 编译 / 运行 / 开发,可以使用 Java 17 语法和 API。只有不依赖任何 Spring / Spring Boot 相关 Maven 包的独立纯 Java 模块,才允许声明和维护 JDK 1.8 兼容;是否兼容以该模块 pom.xml 的编译 release / source / target 和实际验证为准。依赖 Spring 相关 Maven 包的模块不承诺 JDK 1.8 兼容。

application.yml 的目标是开箱即用、按需配置:默认值应能启动完整框架,开发者只在切换数据库、密钥、账号、RPC、第三方服务商等部署差异时再覆盖配置。不要把可选能力设计成大量必填环境变量。

站点名、OpenAPI 标题等展示名默认从 spring.application.name 派生,可使用配置 SpEL #pascal('${spring.application.name}');版权年份等默认展示文案使用 #year()。收款地址、私钥、证书和第三方凭证等部署差异项默认用空值表达未配置,不使用 REPLACE_*、假钱包地址或假密钥作为运行时默认值。

账号、密钥、令牌等部署差异配置优先独立环境变量或 spring.open.*。生产级密钥、私钥、支付密钥建议使用专用配置或模块内自动密钥机制;需要通用兜底时统一使用 spring.application.secret,不要让业务密钥直接回退到 spring.datasource.password

合理使用 Lombok 减少样板:DTO / VO / Properties / 枚举字段 / 构造器注入类优先窄注解(@Getter / @Setter / @RequiredArgsConstructor / @AllArgsConstructor)。不为了用而用;公共 API 语义、不可变对象、防御性复制、带校验或派生逻辑的构造器保留显式代码。避免在实体和复杂业务对象上滥用 @Data

新增 Provider / 第三方服务接入前必须先评估官方 SDK 和成熟生态库;有合适轮子时优先做项目边界适配。

新增人工审核流程必须同步设计超时自动处理:启停 / 超时时间 / 单次处理数量 / 自动动作 / 审计留痕;自动通过或拒绝不写死。

新增能力必须同时满足「简洁易用可扩展」:默认使用路径要少配置、少概念、少步骤;扩展点只放在真实变化点上,避免为了假设场景堆复杂抽象。开发者拿到默认配置应先能跑通,再按业务需要扩展 Provider、配置、事件或模块内扩展点。

项目框架按生产可用要求建设,不再新增 demo / Demo / DEMO 命名。用户可见文案、配置、运行入口、模块名、包名、类名、方法名、路由、数据库标识和文档标题都不得使用 demo 表达功能定位。需要表达快速体验、样板、示例、预览或联调验证时,按语义使用 quickstart / sample / example / preview / showcase / integration;生产功能直接使用真实业务命名。

项目规范不写外部架构方法论口径;涉及设计说明时使用项目既有表述:模块边界数据模型表结构Service contractProvider SPI应用装配层

Git 提交

提交标题使用「英文 type + 中文主题」:

text
<type>: <一句话主题,中文>

允许的常规 Conventional Commits type:

type场景
feat新功能
fix修复缺陷
docs文档
style代码格式、空白、排版等不影响行为的样式调整
refactor重构,不新增功能也不修复缺陷
perf性能优化
test新增或调整测试
build构建、依赖和打包配置
ciCI / 自动化流水线配置
chore杂项维护,不影响源码、测试或运行行为
revert回滚已有提交

项目不启用 feat(scope): 这类 scope 前缀;普通手写提交不使用 merge:,合并提交优先保留 Git 默认 merge 信息或按实际变更选择上述类型。

常量化

Java 代码中高频配置前缀、配置 key、框架级路由必须用常量,避免字符串散落后 IDE 无法安全重命名。

用途常量类
配置前缀PropertyPrefixes
完整配置 keyPropertyKeys
@ConditionalOnExpression 表达式PropertyExpressions
API 路由ApiRoutes

适用范围:@ConfigurationProperties / @ConditionalOnProperty / @RequestMapping 等 / Properties 默认值 / 运行时 key 拼接 / 后台配置元数据 / 多模块开关组合 / @Value 占位符。

模块内 Provider 名 / 高频 key / 字段名常量类按模块自己维护:

用途命名建议
Provider / Store / Disk / Channel / Mailer / Connection / Gateway / Engine*ProviderNames / *StoreNames
通用 JSON / Map / metadata / payload / protocol key*Keys / *ProtocolKeys / *MetadataKeys / *PayloadKeys
参数 / 属性 / 请求头*ParameterNames / *AttributeKeys / *Headers

格式:public final class + public static final String + 私有构造器。

默认使用 XxxKeys,不使用 XxxKeyNamesKeys 更短,也和 MessageKeysAttributeKeysConfigKeys 这类既有命名保持一致;只有外部协议或第三方 API 原生命名已经使用 KeyNames 时才保留。

同一个 key 被多个模块以相同语义复用时,上提到共同上层:框架级放 core common 或公共 starter,能力级放对应 starter,业务聚合级放上层模块 support。不要在多个模块重复定义同一语义 key;也不要仅因字面量相同但语义不同而强行共用。

第三方生态库 / 厂商品牌 Java 类型名保留官方大小写(IJPay / JustAuth / Redisson);配置 key 和 provider 名保持 kebab-case / lower-case(jd-pay / qq-pay)。

i18n 文案 key 使用全小写 kebab-case,并按点分语义路径分层。推荐结构是 业务域.子域.场景.类型.语义.叶子,例如 ai.merchant.admin.operations.signal.success-rate.labelpayment.cashier.message.pay-failed。不要新增 lowSuccessRateAiMerchantAdminpay_failed 这类大小写或下划线混用的文案 key。同一 locale 文件或同一对象层级内不得出现重复 key,治理时必须用 ./.ai/scripts/check i18n 或等价脚本检查重复 key,避免 JS/TS 对象或 properties 后写覆盖前写。后端返回给前端用于查文案的 code / riskCode / statusCode 等语义码,也优先使用同一套 kebab-case 取值,例如 low-success-rate,避免前端再做大小写转换。常用叶子命名固定为 title / label / description / placeholder / message / confirm / success / failed / empty / action。历史已存在 key 不要求一次性迁移,触碰或新增同域 key 时再收敛;第三方协议字段、数据库列名、环境变量和对外 wire contract 按各自规范保留。

java
@ConfigurationProperties(prefix = PropertyPrefixes.STORAGE)
@RequestMapping(ApiRoutes.FILE_RECORDS)
PaymentProviderNames.WECHAT
StorageDiskNames.PUBLIC

API 路由必须挂在 /api/** 下,REST 路径必须带版本:/api/v{major}/{domain}/**(详见 .ai/decisions/adr-007-web-api-spec.mdWeb MVC StarterAPI 兼容策略)。版本号固定放在 /api 后面,业务域放在主版本后面;后台管理 API 对外统一使用 /api/v1/admin/{domain}/**,后端 Controller 必须直接挂载真实 owning domain,不通过旧模块后台路径兼容转发。后端优先复用 ApiRoutes.API_V1ApiRoutes.ADMIN_V1 和域常量派生路由,避免大面积前缀调整时散改 Controller。

例外:YAML 配置文件可保留字面量(Spring Boot 不支持配置 key 引用 Java 常量);测试中验证绑定的完整配置 key 可保留字面量。

代码风格

  • 公共 API 提供清晰 Javadoc,可空必须明确
  • 必需参数显式校验
  • 注释是代码 contract 的一部分:公开 API、SPI / Provider / Facade / Manager、配置属性、DTO / VO 对外字段、非显然业务规则、状态机、权限 / 数据范围 / 资金 / 幂等 / 兼容边界必须提供简洁 Javadoc 或必要行内注释
  • 繁琐运行流程和关键机制必须有说明:自动配置 / 启动装配、过滤器 / 拦截器链路、状态机、事件发布与消费、异步任务、补偿重试、账务扣费、权限数据范围、迁移执行顺序、跨模块协作等,至少在入口方法、核心分支或机制类顶部说明流程、触发条件和边界
  • 方法注释说明用途、触发时机、关键约束、失败语义和返回语义;字段 / 变量注释说明名称表达不完整的业务含义、单位、格式、来源、脱敏边界或兼容含义
  • 内部实现只在复杂逻辑处加必要中文注释;不写「设置字段值」「调用方法」「返回结果」这类重复代码字面含义的注释
  • Optional 只用于表达「可能不存在」的返回值,不滥用在字段上
  • 工具类放 spring-open-starter-toolkit,包名 com.springopen.starter.toolkit;必须保持纯 Java,不依赖 Spring Boot / Spring Framework / 数据库 / Web / i18n。若该模块声明 JDK 1.8 兼容,必须同时在模块 pom.xml 和验证记录中体现
  • 工具类优先 JDK 和 Apache Commons;Guava 只在确有价值时引入
  • 不再新增 com.springopen.core.util.*,新代码直接用 com.springopen.starter.toolkit.*

CRUD 方法名简洁动作化:page / list / detail / create / update / delete / deleteBatch / tree。不写 pageExamples / createExample 这类资源名后缀;只有 Service 管理多个资源或 Mapper 辅助方法需要区分实体时才保留资源名或 ById 后缀。

错误与响应

详见 .ai/decisions/adr-007-web-api-spec.md

异常层级:

text
FrameworkException
  ├── BusinessException
  ├── ConfigurationException
  ├── UnauthorizedException
  ├── ForbiddenException
  ├── ResourceNotFoundException
  ├── ValidationException
  └── RateLimitException

响应规则:

  • Controller 统一返回 ResponseEntity<?>
  • 普通 JSON 的响应体使用 Result<T>
  • HTTP status 只表传输层,业务状态在 Result.code
  • HTTP 200 + Result.code=4xx 表达业务失败;HTTP 4xx / 5xx 只表达传输或框架级失败
  • 错误码使用 GlobalResultCode 全局码 + 模块业务码扩展
  • 全局异常处理把 Sa-Token 未登录 / 无权限统一转为 200 OK + Result.code 业务状态

数据库

详见 database.md

  • 默认 ORM:MyBatis-Plus
  • Mapper 扫描统一由 spring-open-starter-database 提供:@MapperScan(basePackages = "com.springopen", markerInterface = BaseMapper.class)。业务模块不要再手写局部 @MapperScan;Mapper 接口必须继承 BaseMapper<T>。不使用 com.springopen.**.mapper 这类 Ant 风格写法,避免 IDE / Spring 扫描语义不一致
  • 迁移:Liquibase(标准 changeSet 优先,初始化基线迁移文件使用 0000_00_00_401000_module_cms_create_tables.xml + 0000_00_00_401100_module_cms_seed_data.xml 这类基线排序前缀 + owner + action 的 schema / seed 分文件形态;基线阶段每个 owner 只保留 create_tablesseed_data 两类文件;当前开发期保护已迁移文件,新增表、字段、索引、约束、菜单和基线数据修正使用真实日期时间前缀新增增量迁移,不回改已经在本地库、共享库或协作环境执行过的迁移文件;全新模块尚未执行的迁移草稿可在首次执行前整理;V1.0 发布前再单独做一次基线迁移整理 / freeze;新增 changeSet ID 使用 0000_00_00_401000.001.module-cms-create-cms-content<yyyy_MM_dd_HHmmss>.001.module-cms-add-xxx 这类排序前缀 + 文件内 changeSet 序号 + owner + 语义格式;排序前缀按实际执行顺序递增且全仓唯一,初始化基线按 starter / core / module / install / reserved 分区,每个 owner 默认预留 1000 序号并按 100 递增;跨数据库特性必须用 dbms 限制,提交前执行 ./.ai/scripts/check migrations,详见 .ai/decisions/adr-011-liquibase-multi-db.md
  • 实体继承 BaseEntity,主键统一 Long id,不使用 ID 泛型
  • entity 包只放数据库表映射实体类;状态、类型、分类、消息 key、字段 key、转换器、校验器等辅助类放当前模块 support 或语义更明确的包,文件路径必须和 package 声明一致
  • Service 写操作必须加 @Transactional
  • 表名按模块前缀:sys_* / cms_* / blog_* / forum_* / mall_*
  • 软删除字段 deleted / 乐观锁字段 version 标准化
  • 审计字段 created_at / updated_at / created_by / updated_by 自动填充
  • 全表 UPDATE / DELETE 拦截器默认启用,避免误操作
  • 分页插件自动按 spring.datasource.url 推断数据库类型

TraceId / Locale / Tenant

详见 web.mdiam.md

  • TraceId:TraceIdFilter 自动注入,链路日志、操作日志、调度日志、通知发送日志和开放平台调用日志统一使用
  • Locale:Accept-Language 优先;缺失或不支持时 fallback zh-CN;业务 i18n 走 Lang facade 或 Spring MessageSource
  • 语言编码:字符串语言编码统一使用 LocaleCodes,语言解析和归一化统一使用 LocaleUtils;配置和接口使用 zh-CN / zh-TW / en-US 这类 BCP 47 短横线形式;zh_CN / zh_TW / en_US 只用于资源文件后缀或外部输入归一化
  • Tenant / Organization / Department 工作空间:请求头 X-Tenant-Id / X-Organization-Id / X-Department-Id,后端通过 WorkspaceManager 校验归属后使用;不固定写入 token,避免切换时必须重新登录;GET /api/v1/iam/workspace/current 同步返回当前成员、席位、角色和权限快照,前端进入工作空间后不再额外分页查询自己
  • 当前工作空间成员:GET /api/v1/iam/workspace/members 返回当前空间成员分页、公开用户信息和身份资料摘要;前端可用 keyword / page / size 查询,不直接拼接 profile 表做权限判断
  • 用户标识使用稳定 userId,不用 username / email / mobile / 钱包地址 / OAuth openId 等可变值作为业务关联键

安全与脱敏

  • 不打印密钥、私钥、token、密码、完整邮箱、完整手机号、卡密、网盘 / OSS 长期裸链接、签名材料
  • BCrypt 哈希密码,字段命名 password_hash,响应 VO 不输出
  • 敏感配置 key 识别覆盖 kebab-case / snake_case / camelCase / 环境变量风格:api-key / api_key / clientSecret / SECRET_KEY
  • 配置变更日志旧值 / 新值脱敏后写入
  • 安装令牌、管理员密码、数据库密码不打印日志
  • Token 审计只保存 hash、短后缀、设备和动作信息
  • 在线会话 API 不输出 token 明文,只允许后缀辅助排查
  • 操作日志只采集请求 URI / 查询参数 / 路径变量,不采集 JSON body
  • 业务可见 provider 名按服务商语义(alipay / aliyun-oss / wechat),不暴露 IJPay / x-file-storage / sms4j 等生态库名(详见 .ai/decisions/adr-005-ecosystem-first.md

模块默认启用规范

用于生产的业务模块和生产核心模块默认启用:只要应用 Maven 依赖存在,模块级 spring.open.<module>.enabled 缺省即按 true 处理;开发者不使用某模块时,优先移除或注释应用侧 Maven 引用,也可以显式配置 enabled=false 关闭运行时。

模块级 @ConditionalOnProperty 应设置 havingValue=true, matchIfMissing=true,避免开发者删除示例配置后模块被误关。例外是 Provider、第三方账号、验证码、OCR / 外部翻译 Provider、集群、多实例、外部回调、AI 真实模型、扫描器等需要凭证、会产生成本或有安全影响的子能力;这些能力可以继续默认关闭,必须由管理员显式配置。

Starter 开发规范

详见 starters.mdprovider.md.ai/decisions/adr-003-one-capability-one-starter.md.ai/decisions/adr-004-facade-manager-driver-provider.md

Starter 文件结构和 Provider 扩展点统一按 Provider 扩展模型 执行。所有 starter 源码包名使用 com.springopen.starter.<starter> 前缀;连字符 starter 名转成点分段,例如 spring-open-starter-content-safety 使用 com.springopen.starter.content.safetyspring-open-starter-rate-limit 使用 com.springopen.starter.rate.limitspring-open-starter-data-redis 使用 com.springopen.starter.data.redisspring-open-starter-webmvc 使用 com.springopen.starter.webmvc。不得回退旧 Web starter 包名。高频固定字符串必须模块内常量化:provider / channel / store / gateway / 消息类型 / 事件名 / 模板通道 / i18n key 都放当前模块常量类。业务代码不要直接散落 "sms""mail""messages.xxx" 这类裸值。模块内通用 key 默认收敛到 XxxKeys;多个模块以相同语义复用时再上提到共同上层,避免重复定义。

可选但推荐:

  • <Name>RuntimeConfigSource.java + ConfigManager<Name>RuntimeConfigSource.java:后台数据库覆盖配置
  • 按依赖可用性拆 <Name>XxxAutoConfiguration.java(如 queue / scheduler / auth / notification 已按 Redis / Mail / SMS / DB 拆)

每个 starter 必须有一行开关:

yaml
spring.open.<name>.enabled: false

关闭后 AutoConfiguration 整体跳过,应用启动不报错、不注册 Bean、不创建表、不占端口。

Provider 扩展模型

可替换能力遵循 Provider 扩展模型,业务代码依赖项目 Facade / Manager,不直接 import 厂商 SDK。完整规则见 Provider 扩展模型

可替换资源顶层使用 default-provider + providers 集合结构:

yaml
spring:
  open:
    cache:
      default-provider: redis
      providers:
        redis:
          provider: redis
        local:
          provider: memory

配置模型

详见 configuration.mdconfig.md

优先级配置源
1(最高)后台数据库配置项(config_entry
2本地 application.yml / application-*.yml
3环境变量 / .env
4默认值

涉及第三方服务商的能力(Mail / SMS / Storage / Translate / Notification / Payment)支持数据库配置项同名 key 覆盖本地。

配置 key 使用 kebab-case,Java properties 字段使用 camelCase。

每个 starter 必须提供 @ConfigurationProperties 配置类和顶层 Javadoc,让 IDE 通过统一 metadata 支持 YAML 自动补全。

配置元数据处理器只在父 POM maven-compiler-plugin annotationProcessorPaths 中统一声明,子模块不重复添加。

跨模块通信

详见 modules.md

业务模块互不建立 Maven 依赖。跨模块协作优先:

  1. 通过被调 starter / 模块的 Facade(静态调用)
  2. 通过 ApplicationEventPublisher 发事件
  3. 通过 Notification.send(...) Facade 发通知
  4. 通过应用装配层显式编排
  5. 通过稳定标量 ID(userId / articleId / orderId),不直接引用其他模块 Entity

注释掉任一业务模块(如 spring-open-module-mall)后,其他模块仍可编译、测试、启动。

业务展示元数据多语言

详见 .ai/decisions/adr-029-dynamic-metadata-i18n.md

字典、菜单、支付方式、站点信息、分类、标签、模板、通知标题等业务展示元数据由后端按 locale 解析展示值,前端只消费 displayName / name / label / title / description,不维护独立硬编码翻译表。

解析顺序:

  1. 显式 i18n key 命中(messages.xxx)走 Spring MessageSource
  2. 没有 key 时按原文 + namespace + field + context + locale 查询动态业务文案
  3. 命中返回目标语言文案
  4. 未命中返回原文托底,并记录缺失翻译

前端最多做 displayName || name || code 防御兜底。

各 Starter 运行时约定

每个 starter 的具体配置项、provider 列表、扩展点见对应文档:

Starter文档关键运行时事实
Webweb.mdResult<T> / 全局异常 / TraceId / Validation / ResponseEntity
Viewview.mdThymeleaf / WebJar Vue 3 + Pico CSS / public/ 挂载 / 智能根路由
Configconfig.mdEnvironment + Database provider / 缓存链路 / 多实例广播
Databasedatabase.mdMyBatis-Plus / Liquibase / BaseEntity / BaseService / 分页 / 乐观锁
Data Redisredis.mdRedisManager / Redisson / Topic / 分布式锁
Cachecache.mdredis / memory / file store
Authiam.mdSa-Token / JWT / 多端会话 / StpInterface
I18ni18n.mdSpring i18n / 数据库 store / 缺失收集
Translatetranslate.mdLibreTranslate / Google / 腾讯云 / 占位符保护
Templatetemplate.mdsimple engine / Repository SPI / Notification / Mail / SMS 集成
Queuequeue.mdsync / memory / redis / database provider / 失败重试 / 后台运维
Schedulerscheduler.mddb-scheduler / redis / memory provider / 执行日志 / 暂停恢复
Eventevent.md同步 / 异步 / Spring 事务后监听
Hookhook.md@Hook.Execute / Hook.execute(...) 方法外层增强
Pipelinepipeline.mdPipeline.execute(...) / PipelineFlow.execute(ctx) 有序步骤编排
Chainchain.mdChain.execute(...) / ChainFlow.execute(ctx) 条件决策责任链
WebSocketwebsocket.md单实例 / 多实例 / 用户绑定 / 房间 / Redisson 分发
Notificationnotification.mdlog / memory / database / mail / sms / websocket / webhook channels
Mailmail.mdlog / memory / smtp provider
SMSsms.mdlog / memory / 短信宝 / sms4j providers
Storagestorage.mdpublic / local / memory / s3 / 服务商 disks
Paymentpayment.md服务商 providers / Web3 边界
Web3web3.mdEVM 钱包 / RPC / Token / NFT / Event / DEX / 签名广播 / Web3 支付
Rate Limitrate-limit.md登录 / 短信验证码 / Open Platform / 通用接口限流
Feature Flagfeature-flag.md功能开关 / 灰度 rollout / variant / 属性 gate
JSON:APIjson-api.md可选 Resource Document 表示层 / 开放 API 兼容
Pluginplugin.md插件描述文件 / 本地安装 contract / 只读 registry
Logginglogging.md请求日志 / TraceId / 敏感 query 脱敏 / 慢请求
Observabilityobservability.mdActuator health / info / metrics
Knowledgeknowledge.md文件源 / Markdown 切片 / 关键词搜索
AI Taskai-task.md任务模型 / handler 注册
AI Modelai-model.md模型调用 Facade / Manager / ConfigResolver / Provider SPI
OpenAPIapi/openapi.mdOpenAPI 3.1 / Swagger UI / 分组 / AI 元数据
Searchsearch.mdProvider SPI / 聚合搜索 / 热词 / 建议

详细规则不在本文档复制,由各 starter 文档维护。本文档只放跨 starter 适用的通用规则。

IAM / RBAC / 数据权限

详见 iam.md

统一账号体系(详见 .ai/decisions/adr-008-unified-iam.md):

  • iam_user 唯一账号主体;不拆 admin_user / user
  • 身份绑定走 iam_user_identity(username / email / mobile / OAuth / 钱包)
  • 业务资料通过 iam_user_profile / iam_admin_profile / iam_merchant_profile / iam_developer_profile 扩展
  • 统一使用 Sa-Token StpUtil
  • 用户禁用后已登录 token 仍允许主动退出,但当前用户 / token 信息 / 续期 / 多端会话查询 / 按设备退出 / 踢下线必须重新校验
  • 身份禁用后对应身份不能继续登录

数据权限(详见 .ai/decisions/adr-009-explicit-data-scope.md):

  • 第一阶段不启用全局 SQL 拦截器
  • 通过 DataScopeManager 计算范围,Service / Query Support 显式合并
  • 范围类型:ALL / TENANT / ORGANIZATION / ORGANIZATION_AND_CHILDREN / DEPARTMENT / DEPARTMENT_AND_CHILDREN / SELF / CUSTOM

操作日志:

  • 默认自动记录 /api/v1/admin/**POST / PUT / PATCH / DELETE 请求
  • 通过 Spring MVC HandlerInterceptor 采集,不需要业务 Controller 手工调用
  • 只采集请求 URI / 查询参数 / 路径变量,不采集 JSON body
  • 路由:/api/v1/admin/audit/operation-logs,权限 audit:operation-log:view / delete

数据库实体 CRUD 规范

新增与数据库表交互的实体类必须同步:

  • CRUD API(GET /resources / GET /resources/{id} / POST /resources / PUT /resources/{id} / DELETE /resources/{id}
  • DTO / VO 分层(Controller 不暴露 Entity)
  • 权限码、OpenAPI 注解
  • 必要单元测试
  • 长期文档记录

关系表通过所属聚合资源提供查询和设置接口,但必须覆盖新增、删除、修改关系和查询关系。

审计日志等敏感表如限制修改或删除,必须在设计文档明确原因,并至少提供后台查询、详情和受控清理。

初始化种子数据只创建角色、权限和菜单,不创建默认用户、默认密码或默认登录身份。

测试要求

类型工具
单元测试JUnit 5 / AssertJ / Mockito
Spring Boot 测试Spring Boot Test
集成测试Testcontainers

每个 starter 至少提供三组测试(详见 .ai/STYLE.md 模块记忆模板):

  • Properties binding test
  • AutoConfiguration test(含关闭开关)
  • Manager / Facade unit test

涉及数据库或 Redis 的 starter 补充:

  • Testcontainers 集成测试,或
  • 可替代的 slice test,或
  • sample smoke test

默认 skipITs=true 跳过集成测试。需要运行 *IT 时显式 -DskipITs=false

bash
./mvnw -pl spring-open-starters/spring-open-starter-scheduler -am verify -DskipITs=false

依赖安全升级

  • CVE / Mend / IDEA 安全扫描提示升级不能直接按扫描器建议改
  • 修改前查阅 Maven Central / mvnrepository / 官方 release notes / GitHub advisory / 当前 dependency tree
  • 升级前判断与 Spring Boot / JDK 基线 / 上游 SDK / 传递依赖 / 当前模块测试兼容性
  • 覆盖传递漏洞依赖优先在 spring-open-dependencies 统一管理版本
  • 验证至少执行受影响模块 dependency tree + 测试,结果写入 .ai/memory/

Sample 与文档

每个稳定 starter 提供最小 sample:最小配置 + 可运行示例 + README + 常见 API 展示。

每个 starter 文档至少包含(见 .ai/STYLE.md Starter 模板):

text
# <Name> Starter
> 一句话定位
## 何时使用
## 快速开始
## Facade API
## 配置项
## Provider / 扩展点
## 验证命令
## 相关

AI Agent 协作

详见 .ai/conventions.mdAGENTS.md

当前默认协作:主分支内按模块负责人全栈闭环。一个会话负责一个模块或功能簇时,可在该模块边界内同时收口后端、前端、配置、迁移、测试、截图、文档和 .ai 摘要。

模块负责人守住边界:

  • 只处理当前模块需要的共享底座和视图入口
  • 不夹带无关模块,不做跨核心重构
  • 共享底座改动说明消费方、兼容性、文档影响
  • 开发期不按代码风险类别反复暂停;涉及项目外写入、force push、删远程分支、清库、不可逆迁移或生产级敏感操作时,按 AGENTS.md 授权边界先确认

只有用户明确要求独立分支 / 外部 PR / 临时实验时启用非 main 分支。非 main 分支 PR-first:同步 main → 验证 → 提交 → 推送 → 创建 PR。

文档维护

文档体系是长期任务主线。新增功能 / 修复 / 配置变化 / 数据库变化 / 权限变化 / API 行为变化 / 运维入口变化必须同步做文档影响检查。

更新位置内容
docs/开发者使用说明、配置示例、API 调用方式、注意事项、运维入口
.ai/Agent 进度、验证、任务计划、架构决策(不混入正式文档)

文档入口单一职责:首页只放常用入口,modules.md 只做索引,具体配置 / API / 示例只写在对应模块文档内。

不重复维护同一段说明;深层专题从所属主模块文档进入。

新增模块文档时同步更新 modules.md;首页只在常用入口时增加链接。

文档-only 改动至少执行 git diff --check;新增 Markdown 链接时执行相对链接检查。

写作规范详见 .ai/STYLE.md

Released under the Apache License 2.0.