---
trigger: always_on
---
# 后端架构与分支规范
## 分层架构
- 严格遵守 Controller → Service → Store → Mapper 的分层原则
- Controller层:负责API映射,参数接收,不处理业务逻辑
- Service层:核心业务逻辑,参数校验
- Store层:数据储存层,负责数据查询和事务处理(@DSTransactional)
- Mapper层:数据库操作
## 扩展分层
- **Agg(聚合根/Model)层**:领域模型对象,位于 `sales` 模块的 `model` 包下
- 使用 `@Getter` + `@ToString`(不用 `@Data`,通过方法封装属性设置)
- 必须提供 `create()` / `modify()` / `fillingDbId()` 方法封装属性设置
- `create` 和 `modify` 共用私有 `fillingMainData()` 方法
- 全参构造函数用于 Entity→Agg 的转换
- **Bond层**:复杂业务编排层,承载核心业务逻辑(几千行级别),命名如 `XxxBond`
- 标注 `@Component`,直接注入多个 Mapper/Service/Store
- 不遵循标准 CRUD 模板,按业务场景定制方法
- **Trans层**:跨领域业务编排层,命名如 `XxxServiceTrans`
- 标注 `@Component`,使用 `@DSTransactional` 保障多数据源事务
- 整合多个 Service/Store 的调用
## 各层注解规范
| 层 | 类注解 | 注入方式 | 事务注解 |
|---|--------|---------|--------|
| Controller | `@RestController` + `@Slf4j` + `@Api(tags="...")` | `@Autowired` | 无 |
| Service接口 | 无 | - | - |
| ServiceImpl | `@Slf4j` + `@Service` | `@Autowired` | 无(事务放Store层) |
| Store接口 | 无 | - | - |
| StoreImpl | `@Slf4j` + `@Component` | `@Autowired` | `@DSTransactional`(写操作) |
| Agg | `@Getter` + `@ToString` + `@ApiModel` | - | - |
| Bond | `@Component` | `@Autowired` | 按需 |
**关键规则**:
- Store层用 `@Component` 而非 `@Service`
- 事务使用 `@DSTransactional`(`com.baomidou.dynamic.datasource.annotation.DSTransactional`),而非 `@Transactional`
- 特殊场景使用 `@DS("primary")` 或 `@DS("hologres")` 指定数据源
## 各层包路径归属
| 类型 | 包路径 | 所属模块 |
|------|--------|----------|
| Entity | `com.lvcc.biz.entity.sales.{module}` | biz |
| DTO | `com.lvcc.biz.dto.sales.{module}` | biz |
| QueryDTO | `com.lvcc.biz.dto.sales.{module}` | biz |
| VO | `com.lvcc.biz.vo.sales.{module}` | biz |
| Mapper | `com.lvcc.biz.dao.sales.{module}` | biz |
| Mapper XML | `biz/src/main/resources/mapper/{module}/` | biz |
| Controller | `com.lvcc.sales.{module}.controller` | sales |
| Service | `com.lvcc.sales.{module}.service` | sales |
| ServiceImpl | `com.lvcc.sales.{module}.service.impl` | sales |
| Agg(Model) | `com.lvcc.sales.{module}.model` | sales |
| Store | `com.lvcc.sales.{module}.store` 或 `com.lvcc.biz.store.sales.{module}` | sales/biz |
| StoreImpl | `com.lvcc.sales.{module}.store.impl` 或 `com.lvcc.biz.store.sales.{module}.impl` | sales/biz |
| Bond | `com.lvcc.sales.{module}.bond` | sales |
## 各层返回值规范
| 层 | 操作类型 | 返回类型 | 示例 |
|----|---------|---------|------|
| Controller | 所有 | `RestResponse` | `RestResponse.success().setData(data)` |
| Service | 分页查询 | `Page<VO>` | `Page<AiOutboundCallVO>` |
| Service | 新增/删除/更新 | `boolean` | throws FebsException |
| Service | 详情查询 | `VO` | throws FebsException |
| Store | 分页查询 | `List<VO>` | Mapper直返 |
| Store | 新增或修改 | `Long`(主键ID) | - |
| Store | 删除 | `boolean` | - |
| Store | 详情 | `Agg` 或 `VO` | - |
## Controller 标准方法模板
```java
// 新增:POST /save
@PostMapping("/save")
public RestResponse create(@RequestBody XxxDTO dto) throws FebsException
// 单删:GET /delete/{id}
@GetMapping("/delete/{id}")
public RestResponse delete(@PathVariable("id") String id) throws FebsException
// 批删:POST /delete/batch
@PostMapping("/delete/batch")
public RestResponse deleteBatch(@RequestBody String ids) throws FebsException // JSON字符串手动解析
// 分页查询:POST /list
@PostMapping("/list")
public RestResponse queryXxxPage(@RequestBody XxxQueryDTO queryDto) // 不throws
// 详情查询:GET /get/{id}
@GetMapping("/get/{id}")
public RestResponse findXxxById(@PathVariable("id") String id) throws FebsException
// 更新:POST /update/{id}
@PostMapping("/update/{id}")
public RestResponse update(@PathVariable("id") String id, @RequestBody XxxDTO dto) throws FebsException
```
**关键规则**:
- id 参数类型统一为 **String**(Controller层),在Service层转为Long
- 批量操作接收 JSON字符串,用 `JSON.parse()` 手动解析
- 更新操作路径为 `/update/{id}`,将 id 设置到 dto:`dto.setId(Long.valueOf(id))`
- 分页查询方法 **不throws FebsException**
- 返回格式:`RestResponse.success().setData(data)` 或 `RestResponse.success("成功")`
## 分页查询模式
- QueryDTO 继承 `PageBaseDTO`(含 pageNum=1, pageSize=10)
- Service层构造 `Page<VO>` 对象:`new Page<>(pageNum, pageSize)`
- Store层方法签名:`List<VO> queryXxxPage(Page<VO> queryPage, XxxQueryDTO queryDto)`
- Mapper方法签名:`List<VO> queryXxxRecord(Page<VO> queryPage, @Param("data") XxxQueryDTO queryDto)`
- MyBatis-Plus 自动将分页信息填充到 Page 对象
## DDD设计原则
- 领域模型优先,数据库仅为持久化细节
- 通过聚合根(Agg)维护数据一致性
- 通过限界上下文将系统拆分为高内聚、低耦合的模块
- Agg不使用 `@Setter` / `@Data` 注解(通过 `create`/`modify`/`fillingDbId` 方法保证不可变性)
- 添加字段需同步改动:Entity → StoreImpl → ServiceImpl → Agg → VO/DTO(按需)
## Store层保存模式
```java
@DSTransactional
public Long saveXxxRecord(XxxAgg saveEntity, String userKey) {
XxxEntity curEntity = this.findXxxEntityByDataKey(saveEntity.getId()); // 1. 查询现有实体
curEntity = this.buildXxxEntity(curEntity, saveEntity, userKey); // 2. 构建实体
if (ObjectUtil.isEmpty(curEntity.getId())) {
xxxMapper.insert(curEntity); // 3. id为空 → insert
} else {
xxxMapper.updateById(curEntity); // 4. id有值 → updateById
}
saveEntity.fillingDbId(curEntity.getId()); // 5. 回填聚合根的id
return curEntity.getId(); // 6. 返回主键
}
```
- `buildXxxEntity` 方法处理新增/修改场景的Entity构建
- 逻辑删除:`set(XxxEntity::getDeleted, 1)` 而非物理删除
- 查询实体时必须加 `eq(XxxEntity::getDeleted, 0)` 条件
## 获取当前用户
- Service层:`UserInfo user = JWTUtil.getCurrentUser(); String userKey = String.valueOf(user.getUserId());`
- Controller层日志:`FebsUtil.getCurrentUser()`
## 数据权限注解
```java
@ComplexPermissions(andPermissionEnums = MenuPermEnum.XXX, message = "暂无访问权限")
```
- 用于Controller方法上,控制菜单权限
- 权限码在 `MenuPermEnum` 枚举中定义
## 操作日志注解
```java
@Log("操作描述")
```
- 用于Controller方法上,记录操作日志
## 代码生成器
- 新增表必须采用代码生成器生成的代码风格
- 添加字段时需改动位置:Entity → StoreImpl → ServiceImpl → Agg层 → VO和DTO(按需补充)
## 代码分支规范
- 主分支:`master_test`,禁止在未经允许的情况下直接修改并提交代码
- 需求合集:`factory_platform+合集名称`,如 `factory_platform_M20250901`
- 线上优化:`master_online_optimization+日期`,如 `master_online_optimization_20251101`
- 普通需求:`factory_+需求英文命名+日期`,如 `factory_ai_perms_20250929`
- 合并规范:完成需求后进行代码提测,测试完成后由组长根据上线清单进行代码合并
- 提测时必须在提测单标明是否有SQL,并将SQL粘贴到提测单
- 测试开始时需重新合并 `master_production` 到自己的提测分支
## 本地开发环境
- 本地开发禁止连接测试环境的消息队列
- 微服务版本禁止注册测试环境的注册中心
- 启动顺序:Nacos → Gateway → Sales → 其他按需启动
- 外网映射在dev环境 `t_service_config` 表中配置,地址格式 `http://factorytransmit.frp.lbbtech.com/服务标识/接口路径`
系统架构与分支规范
LINgZone22026-08-26 17:35
相关推荐
用户64596598710881 小时前
Rocky Linux 9 + VMware + Node.js + Nginx 从零部署教程AC赳赳老秦1 小时前
企业级合规审计体系:用 OpenClaw 落地采集全链路留痕,自动生成合规审计报告这个DBA有点耶1 小时前
SQL优化进阶:读懂“索引下推”,告别回表噩梦发量惊人的中年网工2 小时前
如何验证DDoS高防真的生效?从源站隐藏到正常访问的完整验收方法吃杠碰小鸡2 小时前
VsCode中开发Java项目Aloudata2 小时前
数据治理台账 vs 元数据知识图谱:数据资产如何从文档走向计算化宠友信息2 小时前
内容社区源码开发实践解析,用Spring Boot打造1:1仿小红书源码平台马可家的菠萝2 小时前
自动保存已经有了,为什么笔记软件还需要“历史版本”?