Cursor AI 编程实战(篇二):Rules、速查与 Adapter/App 全文

Cursor AI 编程实战(篇二):Rules、速查与 Adapter/App 全文

系列 :共三篇。本篇讲 Cursor Rules(全局/项目、.mdc)、第四节中的分层脱敏片段与 速查 A--E 小代码块,以及「代码分析 / Git」规则全文;并收录 附录 E:Adapter + App 层 .mdc 完整正文 (可直存 .cursor/rules/)。
上一篇篇一:Prompt 与案例
下一篇篇三:Domain / Infrastructure / 策略
合并完整版Cursor-AI编程最佳实践-CSDN发表版.md


系列说明:本文为《Cursor AI 编程实战》连载之一,脱敏整理自团队《最佳实战》。三篇可独立阅读,也可按篇一 → 篇二 → 篇三顺序配合使用。


四、Rules:把「项目语义」交给 AI

LLM 会话相对无状态;Cursor Rules 把团队规范、分层约定、中间件用法固化成可版本化的上下文。

4.1 全局规则(User Rules)

路径:Cursor Settings → Rules → User Rules。作用所有项目。

原则:短、通用、可被项目规则覆盖。示例:

  • Always respond in 中文(按需)
  • 代码简洁,必要注释;回复言简意赅,避免重复解释

4.2 项目规则(推荐)

路径:项目根目录 .cursor/rules/,单文件一条规则,扩展名 .mdc(Markdown + 元数据),可纳入 Git。

特性 说明
格式 .mdc,可读性强
版本 与代码同仓,可评审
模块化 按 Java 规范、Git、中间件等拆分
作用域 可用路径 glob 限定;子目录可有独立 rules

官方建议:单文件尽量 500 行以内 ;写清楚、少模糊词;可配合 /Generate Cursor Rules 生成初稿再迭代。

4.3 分层与依赖示例(脱敏后的 .mdc 片段)

以下将企业内部 Maven 坐标、Starter 命名、二方搜索组件版本线及事务注解等,统一替换为 com.yourorg.platform.*platform-*platform-v2PlatformTransactional 等占位符,便于公开发表;读者落地时请改回各自公司的真实坐标与 API 名称。

markdown 复制代码
---
alwaysApply: true
---
# 后端开发通用规范(脱敏示例)

## 项目架构规范
### 父 POM 配置
- 所有业务工程继承统一父 POM(示例):
```xml
<parent>
  <groupId>com.yourorg.platform</groupId>
  <artifactId>platform-parent</artifactId>
  <version>1.0.0-SNAPSHOT</version>
</parent>

Adapter 层

  • 职责:适配不同触点(API、Admin、H5、Wechat、PDA 等)
  • 包路径*.adapter.*
  • 文件命名 :以 *Adapter*Controller 结尾
  • 特殊规则
    • 使用 @Action 注解,方法入参必须为单个入参,返回值包装在 Response 下
    • Action 名称全局唯一
    • 只能调用 App 层代码
    • 不同端使用不同包和 path 前缀(/admin、/tenant、/api)

App 层

  • 职责:不同场景的业务入口(接口、MQ、定时任务)
  • 包路径*.app.*
  • 文件命名 :以 *AppService*Consumer*Job 结尾
  • 特殊规则:允许在简单场景下跳过 Domain 访问 Infrastructure

Domain 层

  • 职责:核心业务逻辑、领域模型抽象
  • 包路径*.domain.*
  • 文件命名 :以 *Service*DO 结尾

Infrastructure 层

  • 职责:DB、搜索、文件、外部 RPC;防腐通过 Gateway
  • 包路径*.infrastructure.*
  • 文件命名 :以 *Repository*Gateway 结尾

SPI 层

  • 职责:扩展点、字典、消息、基础 PO 等
  • 包路径*.spi.*

基础框架依赖(示例命名)

  • platform-common-api:基础模型、请求应答、通用异常、工具类
  • platform-common-runtime:运行机制、日志切面、上下文、过滤器
  • platform-spring-boot-starter-mybatis:数据访问
  • platform-spring-boot-starter-cache:缓存
  • platform-spring-boot-starter-rocketmq:消息队列

包与类命名

  • 根包:com.yourorg.platform.{应用}.{module}
  • 各层包路径包含层次标识:adapter、app、domain、infrastructure、spi

依赖注入

  • 优先构造器注入:@AllArgsConstructor@RequiredArgsConstructor
  • 避免 @Autowired 字段注入

日志规范(完整示例)

java 复制代码
@Slf4j
public class BusinessService {
    public void processOrder(OrderDTO order) {
        log.info("开始处理订单,订单号:{}", order.getOrderNo());

        try {
            log.debug("订单处理详情:{}", JSON.toJSONString(order));
            // 业务逻辑
        } catch (Exception e) {
            log.error("订单处理失败,订单号:{},错误信息:", order.getOrderNo(), e);
            throw e;
        }

        log.info("订单处理完成,订单号:{}", order.getOrderNo());
    }
}

异常处理(完整示例)

java 复制代码
public void validateOrder(OrderDTO order) {
    if (order == null) {
        throw new BusinessException(OrderMsg.ORDER_NOT_FOUND);
    }

    if (StringUtils.isEmpty(order.getOrderNo())) {
        throw new BusinessException(OrderMsg.ORDER_NO_REQUIRED);
    }
}

中间件与数据访问(节选)

  • MyBatis-Plus:PO 继承统一 BaseModel;禁止 SELECT *;禁止循环打 DB;必须用 #{}
  • 消息:使用团队封装的 PlatformMQProducer(示例名),避免直接使用原生客户端 API
  • 搜索:若依赖 platform-v2 搜索组件,需在规则中写明「仅在某运行环境下启用」,例如:
xml 复制代码
<dependency>
    <groupId>com.yourorg.platform.v2</groupId>
    <artifactId>platform-search-es-api</artifactId>
    <version>${platform.v2.version}</version>
</dependency>

事务注解(脱敏)

  • 使用团队统一事务注解(示例名 @PlatformTransactional)标记事务方法

  • 缩小事务边界;事务内尽量避免远程调用与 MQ;提交后再发消息可用 TransactionSynchronizationManager 等模式

    速查 A:Adapter 层 Controller 与异常处理(完整代码示例,脱敏)

    java 复制代码
    @Api(tags = "业务模块名称")
    @RequiredArgsConstructor
    @RestController
    @RequestMapping("/admin/module/action")
    public class ModuleAdminAction {
    
        private final ModuleAdminService moduleAdminService;
    
        @ApiOperation("业务操作描述")
        @Action(name = "操作名称", value = "UNIQUE_ACTION_CODE", order = 50)
        @PostMapping(value = "actionMethod")
        public Response<ReturnType> actionMethod(@RequestBody RequestType request) {
            ReturnType result = moduleAdminService.businessMethod(request);
            return Response.ok(result);
        }
    }
java 复制代码
@Api(tags = "商品管理")
@RequiredArgsConstructor
@RestController
@RequestMapping("/admin/gen/sku/action")
public class GenSkuAdminAction {

    private final GenSkuAdminService genSkuAdminService;

    @ApiOperation("商品-分页查询")
    @Action(name = "商品-分页查询", value = "GEN_SKU_PAGE_QUERY_ACTION", order = 10)
    @PostMapping(value = "pageQueryAction")
    public Response<Paging<GenSkuMdDTO>> pageQueryAction(@RequestBody GenSkuPageRequest request) {
        Paging<GenSkuMdDTO> result = genSkuAdminService.pageQuery(request);
        return Response.ok(result);
    }
}
java 复制代码
@PostMapping(value = "businessAction")
public Response<ResultType> businessAction(@RequestBody RequestType request) {
    try {
        ResultType result = appService.process(request);
        return Response.ok(result);
    } catch (BusinessException e) {
        throw e;
    } catch (Exception e) {
        log.error("系统异常", e);
        throw new SystemException("系统处理异常");
    }
}

速查 B:MyBatis-Plus 依赖、配置与 Repository 查询(完整示例,脱敏)

xml 复制代码
<dependency>
  <groupId>com.yourorg.platform.common</groupId>
  <artifactId>platform-spring-boot-starter-mybatis</artifactId>
</dependency>
yaml 复制代码
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:appdb}?autoReconnect=true&tcpKeepAlive=true&useUnicode=true&characterEncoding=UTF8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
    username: ${MYSQL_USERNAME:root}
    password: ${MYSQL_PASSWORD:root}
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
  type-handlers-package: com.yourorg.platform.app.mybatis.typehandler
  type-aliases-package: com.yourorg.platform.app.workbench.*.repository.po
java 复制代码
default List<EntityPO> listByIds(Collection<Long> ids, String... fields) {
    if (CollectionUtils.isEmpty(ids)) {
        return Lists.newArrayList();
    }
    QueryWrapper<EntityPO> queryWrapper = new QueryWrapper<>();
    if (fields != null && fields.length > 0) {
        queryWrapper.select(fields);
    }
    queryWrapper.lambda().in(EntityPO::getId, ids);
    return selectList(queryWrapper);
}

速查 C:MQ 消费者模板(完整示例,脱敏)

java 复制代码
@Slf4j
@RequiredArgsConstructor
@MQConsumer(consumerGroup = "${GID_MODULE_GROUP:GID_MODULE_GROUP}")
public class ModuleMqListener {

    private final ModuleAppService moduleAppService;

    @MQSubscribe(topic = "${platform.mq.topic:}", tag = {MqConstant.MODULE_TAG})
    public void processMessage(List<DataType> dataList) {
        log.info("[业务消息监听]收到监听消息,消息数量:{}", dataList.size());

        if (CollectionUtils.isEmpty(dataList)) {
            log.warn("接收到空消息列表");
            return;
        }

        try {
            moduleAppService.processData(dataList);
            log.info("[业务消息监听]消息处理完成");
        } catch (Exception e) {
            log.error("[业务消息监听]消息处理异常", e);
            throw e;
        }
    }
}

速查 D:App 层批量写库示例(完整示例)

java 复制代码
@Service
@AllArgsConstructor
@Slf4j
public class GenLabelAppService {

    private final GenLabelLinkMdRepo genLabelLinkMdRepo;
    private final IdGenerator idGenerator;

    public void genAdaptModelLabelSave(GenLabelLinkMdReq request) {
        log.info("开始保存商品标签关联,请求参数:{}", JSON.toJSONString(request));

        if (request == null || CollectionUtils.isEmpty(request.getLabelIds())) {
            throw new BusinessException(GenMsg.LABEL_IDS_REQUIRED);
        }

        List<GenLabelLinkMdPO> linkList = request.getLabelIds().stream()
            .map(labelId -> {
                GenLabelLinkMdPO link = new GenLabelLinkMdPO();
                link.setId(idGenerator.nextId());
                link.setModelId(request.getModelId());
                link.setLabelId(labelId);
                return link;
            })
            .collect(Collectors.toList());

        genLabelLinkMdRepo.saveBatch(linkList);
        log.info("商品标签关联保存完成,保存数量:{}", linkList.size());
    }
}

速查 E:事务提交后发送消息(完整示例,脱敏注解名)

java 复制代码
@Service
@RequiredArgsConstructor
public class OrderAppService {

    @PlatformTransactional
    public void processOrderBatch(List<OrderDTO> orders) {
        if (CollectionUtils.isEmpty(orders)) {
            return;
        }

        orders.forEach(order -> {
            validateOrder(order);
            saveOrder(order);
            updateInventory(order);
        });

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                messageProducer.sendOrderMessage(orders, MqConstant.ORDER_PROCESSED);
            }
        });
    }
}

说明:@PlatformTransactional 为脱敏后的占位注解名,请替换为团队实际提供的事务注解。


五、辅助规则示例:代码分析与 Git

5.1 代码分析规则(完整)

markdown 复制代码
---
description: 代码分析规则
globs:
alwaysApply: false
---

# 代码分析规则

## 目标
根据代码入口深入分析完整业务流程,生成详细的业务流程文档,便于团队理解和维护代码。

## 关键规则
- **必须生成分析文档保存到项目的 docs 目录下**
- **必须使用 sequential-thinking 辅助分析**
- **必须深入方法内部逻辑,因此你可能需要检索代码**
- **建议使用 sequential-thinking 辅助检索代码**

### 1. 聚焦业务核心逻辑
- 忽略日志打印、参数基础校验等次要逻辑
- 忽略异常处理中的技术细节,只关注业务异常处理逻辑
- 忽略与业务无关的工具方法调用(如字符串处理、集合操作等)
- 聚焦业务状态转换、流程分支、核心计算等关键逻辑

### 2. 深入方法调用链
- 追踪每个关键方法的内部实现,不仅停留在方法调用层面
- 对调用链上的每个重要方法都分析其内部业务逻辑
- 对于外部依赖的服务(如 RPC 调用),说明其功能和业务意义
- 深入分析每个关键业务分支的条件和处理逻辑

### 3. 结合已有文档
- 优先使用已有文档中的描述,避免重复分析
- 如果已有文档对某个方法有详细描述,直接引用该内容
- 基于已有文档进行补充和完善
- 对已有文档与代码实现不一致的地方进行标注

### 4. 文档输出规范
- 分析结果保存到 `/docs` 目录下,使用 Markdown 格式
- 文档命名格式:`业务名称-流程分析.md`(如:`订单创建-流程分析.md`)
- 文档需包含方法调用树,清晰展示调用层级关系
- 使用分步业务流程描述完整处理过程

## 文档结构模板
```markdown
# 业务名称流程分析
## 功能概述
[简要描述该业务功能的目的和作用]
## 入口方法
`com.example.Class.method`
## 方法调用树

入口方法

├─ 一级调用方法1

│ ├─ 二级调用方法1.1

│ ├─ 二级调用方法1.2

├─ 一级调用方法2

├─ 二级调用方法2.1

└─ 二级调用方法2.2

└─ 三级调用方法

复制代码
## 详细业务流程
1. [步骤1:业务逻辑描述]
2. [步骤2:业务逻辑描述]
   - [子步骤2.1:详细逻辑]
   - [子步骤2.2:详细逻辑]
3. [步骤3:业务逻辑描述]
## 关键业务规则
- [规则1:描述业务规则及其条件]
- [规则2:描述业务规则及其条件]
## 数据流转
- 输入:[描述方法输入及其业务含义]
- 处理:[描述关键数据处理和转换]
- 输出:[描述方法输出及其业务含义]
## 扩展点/分支逻辑
- [分支1:触发条件及处理逻辑]
- [分支2:触发条件及处理逻辑]
## 外部依赖
- 标注对外部系统的依赖
## 注意事项
- [列出实现中需要特别注意的点]

系统交互图

  • 如果业务流程包含多个系统模块,请使用 PlantUML 画出时序图

代码分析技巧

步骤1:明确业务入口

  • 确定代码分析的起点(通常是 Controller、Action 层的公开方法)
  • 了解该方法的调用场景和业务背景

步骤2:构建方法调用树

  • 从入口方法开始,追踪所有重要的方法调用
  • 使用缩进表示调用层级,清晰展示调用关系
  • 忽略非核心方法调用(如日志、参数校验等)

步骤3:分析业务流程

  • 按照代码执行顺序分析业务处理步骤
  • 重点关注业务状态转换和分支逻辑
  • 提取关键业务规则和数据处理逻辑

步骤4:整理业务规则

  • 总结条件判断中隐含的业务规则
  • 分析不同场景下的处理差异
  • 提炼业务逻辑的核心决策点

步骤5:描述数据流转

  • 分析关键数据的来源、处理和去向
  • 说明数据模型转换和业务含义
  • 关注核心业务实体的状态变化

好的分析的特征

  1. 完整性:覆盖所有核心业务逻辑和分支

  2. 层次性:清晰展示处理流程的层次结构

  3. 业务性:以业务视角描述,而非技术实现细节

  4. 精确性:准确反映代码的实际处理逻辑

  5. 可理解性:业务人员也能理解的表述方式

  6. 实用性:帮助读者快速理解业务流程和规则

    5.2 Git 分支与提交(完整)

    markdown 复制代码
    ---
    description: Git 分支管理与提交规则
    globs:
    alwaysApply: false
    ---
    # Git 分支管理与提交规则
    
    ## 分支命名规范
    
    ### 主要分支
    - `develop`: 主开发分支,所有 MR 提交至此分支
    - `release/<major>.<minor>`: 生产发版分支,用于回归测试和发布
    - `sprint/<major>.<minor>`: 送测分支,基于迭代检出,release 后删除
    
    ### 开发分支
    - `feature/<your-feature>`: 功能特性分支,用于新功能开发
    - `bugfix/<what-you-want-to-fix>`: 基于 develop 分支的 bug 修复分支
    - `hotfix/<what-you-want-to-fix>`: 基于 release 分支的紧急修复分支
    
    ## Git Commit Message 规范
    
    ### 提交格式

():

复制代码
### CommitType 类型
- `fix`: 修复性提交,针对缺陷或 Issue
- `feat`: 特性提交,新增功能或 API
- `docs`: 文档提交,增加或修改文档
- `perf`: 优化性提交,功能重构或性能优化
- `test`: 单元测试提交,增加或调整测试
- `chore`: 其他杂项,如配置文件、脚本等
- `refactor`: 重构

### Module 模块
- `model`: 模型部分
- `scene`: 场景部分
- `flow`: 流程部分
- `connector`: 连接器部分
- `console`: 控制台部分
- `common`: 公共部分

### 示例

feat(common): Move builtin field type to builtin fields module

fix(model): Fix data validation error in user model

docs(readme): Update installation instructions

复制代码
## 分支操作规范

### 代码合并原则
1. **从 develop 合并到 feature**: 使用 `git rebase develop`
2. **从 feature 合并到 develop**: 使用 `git merge feature/<branch-name>`
3. **hotfix 分支**: 需要同时提 MR 至 release 分支和 cherry-pick 至 develop 分支

### 推荐工作流程
1. 创建 feature 分支前先拉取最新 develop 分支
2. feature 分支开发过程中定期 rebase develop 内容
3. 提交前确保代码编译通过且单测执行成功
4. 解决冲突时使用 `git add .` 和 `git rebase --continue`,不要使用 `git commit`

## 代码质量要求
- 每次 git push 触发 pipeline,确保代码编译通过
- 单元测试必须执行成功
- 提交信息应简洁明了,必要时附上任务 ID

## 注意事项
- feature/develop 分支仅用于部署联调环境
- 部署联调环境时,将 feature 分支 merge 至 feature/develop
- MVP 阶段后,所有合并必须通过 MR (Merge Request)
- release 分支只允许文档补充和 bugfix,不允许新特性直接合并

附录 E:Adapter / App 层 .mdc 全文(脱敏)

以下为《最佳实战》原文两份入口层规则的正文级复制,可存为 .cursor/rules/adapter.mdcapp.mdc。已做替换:消息 topic 占位符统一为 ${platform.mq.topic:};消息体类型为 PlatformMessage ;事务注解为 @PlatformTransactional(请按贵司实际 API 替换)。路径说明中的笔误「action``」已改为「action`」。

E.1 Adapter 层开发规范(.mdc

markdown 复制代码
---
description: Adapter层开发规范
globs:
alwaysApply: false
---
# Adapter层开发规范

## Adapter层职责定义

Adapter层负责适配不同触点场景,包括API、Admin、PC、H5、App等,是系统的入口层。

## 核心约束

### 调用限制
- **只能调用App层代码**,禁止直连Domain/Infrastructure层
- 不同触点使用不同包和path前缀进行隔离

### 包结构要求
```
adapter/
├── tenant/     # 租户端接口
├── admin/      # 运营端接口
└── api/        # 模块间接口
```

## Controller开发规范

### 基础结构模板
```java
@Api(tags = "业务模块名称")
@RequiredArgsConstructor
@RestController
@RequestMapping("/admin/module/action")  // 按触点区分path前缀
public class ModuleAdminAction {
    
    private final ModuleAdminService moduleAdminService;  // 只能注入App层服务
    
    @ApiOperation("业务操作描述")
    @Action(name = "操作名称", value = "UNIQUE_ACTION_CODE", order = 50)
    @PostMapping(value = "actionMethod")
    public Response<ReturnType> actionMethod(@RequestBody RequestType request) {
        ReturnType result = moduleAdminService.businessMethod(request);
        return Response.ok(result);
    }
}
```

### 路径前缀规范
- **管理端**: `/admin/{module}/action`
- **模块间API**: `/api/{module}/action`
- **H5端**: `/h5/{module}/action`

### @Action注解要求
- **name**: 业务操作的中文描述,全局唯一
- **value**: 操作代码,使用大写字母和下划线,全局唯一
- **order**: 排序字段,用于菜单排序

### 方法参数和返回值规范
- **入参**: 必须为单个入参对象,不允许多个参数
- **返回值**: 必须包装在Response<T>中
- **void方法**: 返回Response.ok()

### 完整示例
```java
@Api(tags = "商品管理")
@RequiredArgsConstructor
@RestController
@RequestMapping("/admin/gen/sku/action")
public class GenSkuAdminAction {
    
    private final GenSkuAdminService genSkuAdminService;
    
    @ApiOperation("商品-分页查询")
    @Action(name = "商品-分页查询", value = "GEN_SKU_PAGE_QUERY_ACTION", order = 10)
    @PostMapping(value = "pageQueryAction")
    public Response<Paging<GenSkuMdDTO>> pageQueryAction(@RequestBody GenSkuPageRequest request) {
        Paging<GenSkuMdDTO> result = genSkuAdminService.pageQuery(request);
        return Response.ok(result);
    }
}
```

## API文档规范

### Swagger注解使用
- **@Api(tags = "模块名称")**: 定义Controller的业务模块
- **@ApiOperation("操作描述")**: 详细描述该接口的功能
- **@ApiParam**: 用于参数说明(如果需要)

### 接口文档要求
- 接口名称要清晰表达业务含义
- 操作描述要包含具体的业务场景
- 参数和返回值要有明确的类型定义

## 错误处理规范

### 统一异常处理
```java
@PostMapping(value = "businessAction")
public Response<ResultType> businessAction(@RequestBody RequestType request) {
    try {
        ResultType result = appService.process(request);
        return Response.ok(result);
    } catch (BusinessException e) {
        throw e;
    } catch (Exception e) {
        log.error("系统异常", e);
        throw new SystemException("系统处理异常");
    }
}
```

### 参数校验
- 使用@Valid注解进行参数校验
- 复杂业务校验在App层处理
- 基础格式校验在请求对象中定义

## 安全规范

### 权限控制
- 使用@Action注解进行权限控制
- 不同端口的接口要有相应的权限策略
- 敏感操作要有额外的权限验证

### 数据安全
- 不要在Controller层直接处理敏感数据
- 日志中避免输出敏感信息
- 返回数据要进行脱敏处理

## 性能考虑

### 接口性能
- 避免在Controller层进行复杂计算
- 大数据量操作要考虑分页和异步处理
- 合理使用缓存减少重复计算

### 监控埋点
- 重要业务接口要有性能监控
- 异常情况要有告警机制
- 关键业务流程要有链路追踪

E.2 App 层开发规范(.mdc

markdown 复制代码
---
description: App层开发规范
alwaysApply: false
---
# App层开发规范

## App层职责定义

App层是业务入口层,负责协调各层组件完成业务功能,包括接口调用、MQ消费、定时任务执行等。

## 包结构规范

```
app/
├── service/    # 应用服务
├── consumer/   # MQ消费者
├── producer/   # MQ生产者
└── job/        # 定时任务
```

## AppService开发规范

### 基础结构模板
```java
@Service
@RequiredArgsConstructor
@Slf4j
public class ModuleAppService {
    
    private final ModuleRepo moduleRepo;  // 可直接注入Infrastructure层
    private final ModuleDomainService moduleDomainService;  // 注入Domain层服务
    private final IdGenerator idGenerator;  // 框架组件
    
    public void businessMethod(RequestDTO request) {
        // 简单CRUD可直接在App层实现
        // 复杂业务逻辑调用Domain层
    }
}
```

### 业务处理原则
- **简单CRUD直接在App层实现**,无需经过Domain层
- **复杂业务逻辑调用Domain层**,保持业务逻辑的复用性
- **可直接跳过Domain层访问Infrastructure层**

### 完整示例
```java
@Service
@AllArgsConstructor
@Slf4j
public class GenLabelAppService {
    
    private final GenLabelLinkMdRepo genLabelLinkMdRepo;
    private final IdGenerator idGenerator;
    
    public void genAdaptModelLabelSave(GenLabelLinkMdReq request) {
        log.info("开始保存商品标签关联,请求参数:{}", JSON.toJSONString(request));
        
        // 参数校验
        if (request == null || CollectionUtils.isEmpty(request.getLabelIds())) {
            throw new BusinessException(GenMsg.LABEL_IDS_REQUIRED);
        }
        
        // 批量保存
        List<GenLabelLinkMdPO> linkList = request.getLabelIds().stream()
            .map(labelId -> {
                GenLabelLinkMdPO link = new GenLabelLinkMdPO();
                link.setId(idGenerator.nextId());
                link.setModelId(request.getModelId());
                link.setLabelId(labelId);
                return link;
            })
            .collect(Collectors.toList());
            
        genLabelLinkMdRepo.saveBatch(linkList);
        log.info("商品标签关联保存完成,保存数量:{}", linkList.size());
    }
}
```

## MQ消费者规范

### 消费者结构模板
```java
@Slf4j
@RequiredArgsConstructor
@MQConsumer(consumerGroup = "${GID_MODULE_GROUP:GID_MODULE_GROUP}")
public class ModuleMqListener {
    
    private final ModuleAppService moduleAppService;

    @MQSubscribe(topic = "${platform.mq.topic:}", tag = {MqConstant.MODULE_TAG})
    public void processMessage(List<DataType> dataList) {
        log.info("[业务消息监听]收到监听消息,消息数量:{}", dataList.size());
        
        if (CollectionUtils.isEmpty(dataList)) {
            log.warn("接收到空消息列表");
            return;
        }
        
        try {
            moduleAppService.processData(dataList);
            log.info("[业务消息监听]消息处理完成");
        } catch (Exception e) {
            log.error("[业务消息监听]消息处理异常", e);
            throw e;  // 重新抛出异常,触发重试机制
        }
    }
}
```

### 消费者开发要点
- 使用@MQConsumer注解定义消费者组
- 使用@MQSubscribe注解订阅特定topic和tag
- 消费方法要有完善的日志记录
- 异常要适当处理,决定是否重试

### 完整示例
```java
@Slf4j
@RequiredArgsConstructor
@MQConsumer(consumerGroup = "${GID_OMS_ORDER_GROUP:GID_OMS_ORDER_GROUP}")
public class OmsOrderPushSoListener {
    
    private final OmsOrderPushSoService omsOrderPushSoService;

    @MQSubscribe(topic = "${platform.mq.topic:}", tag = {MqConstant.OMS_ORDER_PUSH_SO})
    public void orderPushSo(List<Long> orderIds) {
        log.info("[渠道订单下推SO监听]收到监听消息,订单数量:{}", orderIds.size());
        
        if (CollectionUtils.isEmpty(orderIds)) {
            log.warn("接收到空订单ID列表");
            return;
        }
        
        try {
            for (Long orderId : orderIds) {
                omsOrderPushSoService.pushSingleOrder(orderId);
            }
            log.info("[渠道订单下推SO监听]所有订单处理完成");
        } catch (Exception e) {
            log.error("[渠道订单下推SO监听]订单处理异常,订单IDs:{}", orderIds, e);
            throw e;
        }
    }
}
```

## 定时任务规范

### 任务配置文件(job_definition.json)
```json
[
  {
    "module": "{模块}",
    "jobKey": "JOB_KEY",
    "jobName": "{任务描述}",
    "jobType": "Crontab",
    "execType": "Default",
    "crontab": "{cron表达式}",
    "params": "{}",
    "extraInfo": "{}",
    "status": "ENABLED"
  }
]
```

### 定时任务执行类
```java
@Slf4j
@Component
@RequiredArgsConstructor
public class ModuleScheduledJob {

    private final ModuleAppService moduleAppService;

    /**
     * 定时任务描述
     * cron表达式说明
     */
    @Job(JobConst.JOB_KEY)
    public void executeScheduledTask() {
        log.info("开始执行定时任务:{}", JobConst.JOB_KEY);
        
        try {
            moduleAppService.processScheduledTask();
            log.info("定时任务执行完成:{}", JobConst.JOB_KEY);
        } catch (Exception e) {
            log.error("定时任务执行异常:{}", JobConst.JOB_KEY, e);
            // 根据业务需求决定是否重新抛出异常
        }
    }
}
```

### 定时任务开发要点
- 使用@Job注解标记定时任务方法
- 任务名称要与配置文件中的jobKey保持一致
- 要有完善的日志记录和异常处理
- 长时间运行的任务要考虑拆分和监控

### 完整示例
```java
@Slf4j
@Component
@RequiredArgsConstructor
public class TenantEnableScheduledJob {

    private final GenTenantAppService genTenantAppService;

    /**
     * 每天0点执行租户启用任务
     * cron表达式:0 0 0 * * ? 表示每天0点0分0秒执行
     */
    @Job(TenantJobConst.GEN_TENANT_ENABLE_SCHEDULE_JOB)
    public void executeTenantEnableJob() {
        log.info("开始执行租户启用定时任务");
        
        try {
            List<Long> pendingTenantIds = genTenantAppService.getPendingEnableTenants();
            log.info("待启用租户数量:{}", pendingTenantIds.size());
            
            if (CollectionUtils.isNotEmpty(pendingTenantIds)) {
                genTenantAppService.batchEnableTenants(pendingTenantIds);
                log.info("批量启用租户完成,启用数量:{}", pendingTenantIds.size());
            }
            
            log.info("租户启用定时任务执行完成");
        } catch (Exception e) {
            log.error("租户启用定时任务执行异常", e);
            // 根据业务需求决定是否需要告警
        }
    }
}
```

## 消息生产者规范

### 消息发送服务
```java
@Service
@RequiredArgsConstructor
@Slf4j
public class ModuleMessageProducer {
    
    private final EventProducer eventProducer;
    
    @Value("${MQ_TOPIC:MODULE_MQ_TOPIC}")
    private String normalTopic;
    
    public void sendBusinessMessage(BusinessDTO businessData, String tag) {
        log.info("准备发送业务消息,tag:{}", tag);
        
        PlatformMessage platformMessage = new PlatformMessage();
        platformMessage.setBody(businessData);
        platformMessage.setTags(tag);
        platformMessage.setTopic(normalTopic);
        
        // send方法默认持久化,支持可靠性投递
        Long result = eventProducer.send(platformMessage);
        if (Objects.isNull(result)) {
            log.error("消息发送失败,数据:{}", JSON.toJSONString(businessData));
            throw new BusinessException(ModuleMsg.MESSAGE_SEND_FAILED);
        }
        
        log.info("业务消息发送成功,消息ID:{}", result);
    }
}
```

## 事务处理规范

### 事务使用原则
- 使用@PlatformTransactional注解标记事务方法
- 尽可能缩小事务粒度,避免大事务
- 事务方法内避免远程调用和MQ发送

### 事务处理示例
```java
@Service
@RequiredArgsConstructor
public class OrderAppService {
    
    @PlatformTransactional
    public void processOrderBatch(List<OrderDTO> orders) {
        if (CollectionUtils.isEmpty(orders)) {
            return;
        }
        
        // 批量处理,减少事务时间
        orders.forEach(order -> {
            validateOrder(order);
            saveOrder(order);
            updateInventory(order);
        });
        
        // 事务提交后发送消息
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                messageProducer.sendOrderMessage(orders, MqConstant.ORDER_PROCESSED);
            }
        });
    }
}
```

相关推荐
Rubin智造社3 小时前
Claude Code开发者大会系列2|“饮鸩止渴”还是“即刻解药”?Anthropic与SpaceX的联姻内幕
大数据·数据库·人工智能·开发者大会·anthropic·claude code
噢,我明白了3 小时前
表单的完整 CRUD 练习【极简个人记账本】(含前端后端链接mySQL)
java·前端·数据库·mysql
通往曙光的路上3 小时前
mysql1
java
Tigshop开源商城8 小时前
『物流设置+SEO优化』Tigshop开源商城系统 JAVA v5.8.26 版本更新!
java·开源商城系统·tigshop
Tigshop开源商城10 小时前
『订单税率+收货地址校验国家字段』功能上新|跨境运营更高效,Tigshop开源商城系统 JAVA v5.8.23 版本更新
java·开源商城系统·tigshop
珠海西格电力10 小时前
零碳园区的能源供给成本主要包括哪些方面?
大数据·分布式·微服务·架构·能源
REDcker10 小时前
C++变量存储与ELF段布局详解 从const全局到rodata与nm_readelf验证实践
java·c++·面试
经济元宇宙10 小时前
摄影培训行业百科:机构选择与学习路径全解析
大数据·人工智能·学习
Java识堂10 小时前
mcp指南
ai