工程约定
受众:开发者 摘要:编码、运行时和 starter 开发的统一规范。Agent 协作规范见
.ai/conventions.md;文档写作规范见.ai/STYLE.md。
基础约定
| 项 | 值 |
|---|---|
| Spring Boot 基线 | 4.x |
| JDK 编译运行 | 17+ |
| Maven groupId | com.springopen |
| Java base package | com.springopen |
| Starter base package | com.springopen.starter.* |
| Maven artifact | spring-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.z:x 表示重大架构调整或兼容性边界变化,通常可能包含破坏性变更;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 contract、Provider SPI、应用装配层。
Git 提交
提交标题使用「英文 type + 中文主题」:
<type>: <一句话主题,中文>允许的常规 Conventional Commits type:
| type | 场景 |
|---|---|
feat | 新功能 |
fix | 修复缺陷 |
docs | 文档 |
style | 代码格式、空白、排版等不影响行为的样式调整 |
refactor | 重构,不新增功能也不修复缺陷 |
perf | 性能优化 |
test | 新增或调整测试 |
build | 构建、依赖和打包配置 |
ci | CI / 自动化流水线配置 |
chore | 杂项维护,不影响源码、测试或运行行为 |
revert | 回滚已有提交 |
项目不启用 feat(scope): 这类 scope 前缀;普通手写提交不使用 merge:,合并提交优先保留 Git 默认 merge 信息或按实际变更选择上述类型。
常量化
Java 代码中高频配置前缀、配置 key、框架级路由必须用常量,避免字符串散落后 IDE 无法安全重命名。
| 用途 | 常量类 |
|---|---|
| 配置前缀 | PropertyPrefixes |
| 完整配置 key | PropertyKeys |
@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,不使用 XxxKeyNames。Keys 更短,也和 MessageKeys、AttributeKeys、ConfigKeys 这类既有命名保持一致;只有外部协议或第三方 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.label、payment.cashier.message.pay-failed。不要新增 lowSuccessRate、AiMerchantAdmin、pay_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 按各自规范保留。
@ConfigurationProperties(prefix = PropertyPrefixes.STORAGE)
@RequestMapping(ApiRoutes.FILE_RECORDS)
PaymentProviderNames.WECHAT
StorageDiskNames.PUBLICAPI 路由必须挂在 /api/** 下,REST 路径必须带版本:/api/v{major}/{domain}/**(详见 .ai/decisions/adr-007-web-api-spec.md、Web MVC Starter 和 API 兼容策略)。版本号固定放在 /api 后面,业务域放在主版本后面;后台管理 API 对外统一使用 /api/v1/admin/{domain}/**,后端 Controller 必须直接挂载真实 owning domain,不通过旧模块后台路径兼容转发。后端优先复用 ApiRoutes.API_V1、ApiRoutes.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。
异常层级:
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_tables和seed_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
- TraceId:
TraceIdFilter自动注入,链路日志、操作日志、调度日志、通知发送日志和开放平台调用日志统一使用 - Locale:
Accept-Language优先;缺失或不支持时 fallbackzh-CN;业务 i18n 走Langfacade 或 SpringMessageSource - 语言编码:字符串语言编码统一使用
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.md、provider.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.safety,spring-open-starter-rate-limit 使用 com.springopen.starter.rate.limit,spring-open-starter-data-redis 使用 com.springopen.starter.data.redis,spring-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 必须有一行开关:
spring.open.<name>.enabled: false关闭后 AutoConfiguration 整体跳过,应用启动不报错、不注册 Bean、不创建表、不占端口。
Provider 扩展模型
可替换能力遵循 Provider 扩展模型,业务代码依赖项目 Facade / Manager,不直接 import 厂商 SDK。完整规则见 Provider 扩展模型。
可替换资源顶层使用 default-provider + providers 集合结构:
spring:
open:
cache:
default-provider: redis
providers:
redis:
provider: redis
local:
provider: memory配置模型
详见 configuration.md 和 config.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 依赖。跨模块协作优先:
- 通过被调 starter / 模块的 Facade(静态调用)
- 通过
ApplicationEventPublisher发事件 - 通过
Notification.send(...)Facade 发通知 - 通过应用装配层显式编排
- 通过稳定标量 ID(
userId/articleId/orderId),不直接引用其他模块 Entity
注释掉任一业务模块(如 spring-open-module-mall)后,其他模块仍可编译、测试、启动。
业务展示元数据多语言
详见 .ai/decisions/adr-029-dynamic-metadata-i18n.md。
字典、菜单、支付方式、站点信息、分类、标签、模板、通知标题等业务展示元数据由后端按 locale 解析展示值,前端只消费 displayName / name / label / title / description,不维护独立硬编码翻译表。
解析顺序:
- 显式 i18n key 命中(
messages.xxx)走 Spring MessageSource - 没有 key 时按原文 + namespace + field + context + locale 查询动态业务文案
- 命中返回目标语言文案
- 未命中返回原文托底,并记录缺失翻译
前端最多做 displayName || name || code 防御兜底。
各 Starter 运行时约定
每个 starter 的具体配置项、provider 列表、扩展点见对应文档:
| Starter | 文档 | 关键运行时事实 |
|---|---|---|
| Web | web.md | Result<T> / 全局异常 / TraceId / Validation / ResponseEntity |
| View | view.md | Thymeleaf / WebJar Vue 3 + Pico CSS / public/ 挂载 / 智能根路由 |
| Config | config.md | Environment + Database provider / 缓存链路 / 多实例广播 |
| Database | database.md | MyBatis-Plus / Liquibase / BaseEntity / BaseService / 分页 / 乐观锁 |
| Data Redis | redis.md | RedisManager / Redisson / Topic / 分布式锁 |
| Cache | cache.md | redis / memory / file store |
| Auth | iam.md | Sa-Token / JWT / 多端会话 / StpInterface |
| I18n | i18n.md | Spring i18n / 数据库 store / 缺失收集 |
| Translate | translate.md | LibreTranslate / Google / 腾讯云 / 占位符保护 |
| Template | template.md | simple engine / Repository SPI / Notification / Mail / SMS 集成 |
| Queue | queue.md | sync / memory / redis / database provider / 失败重试 / 后台运维 |
| Scheduler | scheduler.md | db-scheduler / redis / memory provider / 执行日志 / 暂停恢复 |
| Event | event.md | 同步 / 异步 / Spring 事务后监听 |
| Hook | hook.md | @Hook.Execute / Hook.execute(...) 方法外层增强 |
| Pipeline | pipeline.md | Pipeline.execute(...) / PipelineFlow.execute(ctx) 有序步骤编排 |
| Chain | chain.md | Chain.execute(...) / ChainFlow.execute(ctx) 条件决策责任链 |
| WebSocket | websocket.md | 单实例 / 多实例 / 用户绑定 / 房间 / Redisson 分发 |
| Notification | notification.md | log / memory / database / mail / sms / websocket / webhook channels |
| mail.md | log / memory / smtp provider | |
| SMS | sms.md | log / memory / 短信宝 / sms4j providers |
| Storage | storage.md | public / local / memory / s3 / 服务商 disks |
| Payment | payment.md | 服务商 providers / Web3 边界 |
| Web3 | web3.md | EVM 钱包 / RPC / Token / NFT / Event / DEX / 签名广播 / Web3 支付 |
| Rate Limit | rate-limit.md | 登录 / 短信验证码 / Open Platform / 通用接口限流 |
| Feature Flag | feature-flag.md | 功能开关 / 灰度 rollout / variant / 属性 gate |
| JSON:API | json-api.md | 可选 Resource Document 表示层 / 开放 API 兼容 |
| Plugin | plugin.md | 插件描述文件 / 本地安装 contract / 只读 registry |
| Logging | logging.md | 请求日志 / TraceId / 敏感 query 脱敏 / 慢请求 |
| Observability | observability.md | Actuator health / info / metrics |
| Knowledge | knowledge.md | 文件源 / Markdown 切片 / 关键词搜索 |
| AI Task | ai-task.md | 任务模型 / handler 注册 |
| AI Model | ai-model.md | 模型调用 Facade / Manager / ConfigResolver / Provider SPI |
| OpenAPI | api/openapi.md | OpenAPI 3.1 / Swagger UI / 分组 / AI 元数据 |
| Search | search.md | Provider 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:
./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 模板):
# <Name> Starter
> 一句话定位
## 何时使用
## 快速开始
## Facade API
## 配置项
## Provider / 扩展点
## 验证命令
## 相关AI Agent 协作
详见 .ai/conventions.md 和 AGENTS.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。