# 编码风格规范

## 一、命名规范

| 类型 | 命名规则 | 示例 |
|------|---------|------|
| Controller | `{功能}RestController` | `LotteryActivityRestController` |
| Service 接口 | `{功能}Service` | `LotteryActivityRestService` |
| Service 实现 | `{功能}ServiceImpl` | `LotteryActivityRestServiceImpl` |
| 写操作执行器 | `{功能}CmdExe` 或 `{功能}Exe` | `LotteryDrawExe` |
| 查询执行器 | `{功能}QryExe` | `LotteryRecordQryExe` |
| 写命令 | `{功能}Cmd` | `LotteryDrawCmd` |
| 查询命令 | `{功能}QryCmd` | `LotteryRecordQryCmd` |
| 返回对象 | `{功能}CO` | `LotteryDrawResultCO` |
| DAO | `{实体名}DAO` | `LotteryTicketDAO` |
| Entity | `{实体名}` | `LotteryTicket` |
| 数据库服务 | `{功能}DatabaseService` | `LotteryActivityDatabaseService` |
| Gateway | `{功能}Gateway` + `{功能}GatewayImpl` | `LotteryActivityGateway` |

---

## 二、Controller 规范

```java
/**
 * 功能说明.
 *
 * @author xxx
 * @eo.api-type http
 * @eo.groupName 模块名.子模块名
 * @eo.path /模块/资源
 */
@RestController
@RequestMapping(value = "/user/user-profile", produces = MediaType.APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
public class UserProfileRestController extends BaseController {

    private final UserProfileService userProfileService;

    /**
     * 接口说明.
     *
     * @eo.name 接口名称
     * @eo.url /具体路径（GET 无路径时留空）
     * @eo.method get/post/put/delete
     * @eo.request-type formdata（GET）/ json（POST/PUT）
     */
    @GetMapping
    public UserProfileDTO getProfile(UserProfileQryCmd cmd) {
        return userProfileService.getUserProfile(cmd);
    }

    @PostMapping("/draw")
    public LotteryDrawResultCO draw(@RequestBody @Validated LotteryDrawCmd cmd) {
        return lotteryService.draw(cmd);
    }
}
```

**关键点**：
- 继承 `BaseController`
- `@RequiredArgsConstructor`（构造器注入）
- API 文档用 `@eo.` 注解，不用 Swagger
- GET 请求参数直接用 Cmd 对象接收（无 `@RequestBody`）
- POST/PUT 用 `@RequestBody @Validated`

---

## 三、Cmd / CO 规范

```java
// 写命令 - 继承 AppExtCommand（包含 reqUserId 等公共字段）
@Data
public class LotteryDrawCmd extends AppExtCommand {
    @NotNull
    private Long activityId;

    @Min(1) @Max(10)
    private Integer drawCount;
}

// 查询命令 - 分页继承 PageQueryCmd
@Data
public class LotteryRecordQryCmd extends PageQueryCmd {
    private Long activityId;
}

// 返回对象
@Data
public class LotteryDrawResultCO {
    private Long prizeId;
    private String prizeName;
}
```

**规则**：
- 校验注解用 `jakarta.validation.constraints.*`（不用 javax）
- Cmd 需要用户身份时继承 `AppExtCommand`，通过 `cmd.getReqUserId()` 获取当前用户

---

## 四、Service / CmdExe 规范

```java
// ServiceImpl - 门面，调用 CmdExe
@Service
@RequiredArgsConstructor
@Slf4j
public class LotteryActivityRestServiceImpl implements LotteryActivityRestService {
    private final LotteryDrawExe lotteryDrawExe;
    private final LotteryRecordQryExe recordQryExe;

    @Override
    public LotteryDrawResultCO draw(LotteryDrawCmd cmd) {
        return lotteryDrawExe.execute(cmd);
    }
}

// CmdExe - 核心业务逻辑
@Component
@RequiredArgsConstructor
@Slf4j
public class LotteryDrawExe {
    private final LotteryActivityDatabaseService lotteryDbService;

    @Transactional
    public LotteryDrawResultCO execute(LotteryDrawCmd cmd) {
        // 1. 参数校验
        // 2. 业务逻辑
        // 3. 记录日志
        log.info("用户抽奖: userId={}, activityId={}", cmd.getReqUserId(), cmd.getActivityId());
        // 4. 返回结果
    }
}
```

---

## 五、Infrastructure 规范

```java
// DAO - 继承 BaseDAO
public interface LotteryTicketDAO extends BaseDAO<LotteryTicket> {}

// DatabaseService - 简单场景直接用
@Service
@RequiredArgsConstructor
public class LotteryActivityDatabaseService {
    private final LotteryTicketDAO ticketDAO;

    public void consumeTicket(Long userId, Long activityId) {
        LambdaQueryWrapper<LotteryTicket> wrapper = new LambdaQueryWrapper<LotteryTicket>()
            .eq(Objects.nonNull(userId), LotteryTicket::getUserId, userId)
            .eq(Objects.nonNull(activityId), LotteryTicket::getActivityId, activityId);
        // ...
    }
}

// Gateway - 复杂场景：锁 + 缓存 + 多数据源
@Component
@RequiredArgsConstructor
public class LotteryActivityGatewayImpl implements LotteryActivityGateway {
    private final LotteryTicketDAO ticketDAO;
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void consumeTicketWithLock(Long userId, Long activityId) {
        String lockKey = "lottery:lock:" + userId;
        // 分布式锁 + DB 操作 + 缓存更新
    }
}
```

---

## 六、通用代码规范

### 条件判断
```java
// ✅
if (StringUtils.isNotBlank(name)) { ... }
if (Objects.nonNull(userId)) { ... }

// ❌
if (name != null && !name.isEmpty()) { ... }
if (userId != null) { ... }
```

### 金额
```java
// ✅
BigDecimal amount = new BigDecimal("100.00");
BigDecimal total = a.add(b);

// ❌ 禁止
double amount = 100.00;
```

### 返回分页
```java
return PageResult.of(records, total, cmd.getPageNum(), cmd.getPageSize());
```

### 异常
```java
// ✅
throw new BusinessException("抽奖券不足");

// ❌
throw new RuntimeException("抽奖券不足");
```

### Redis Key 规范
```
缓存:      {module}:{entity}:{id}           user:info:10001
排行榜:    ranking:{type}:{period}:{date}   ranking:send_gift:day:20240101
分布式锁:  {module}:lock:{bizId}            coin:lock:10001
```

---

## 七、新功能开发顺序（黄金路径）

1. 定义 Cmd / CO（other-client）
2. 定义 Service 接口（other-client）
3. 实现 CmdExe（other-application）
4. 实现 ServiceImpl（other-application）
5. 创建 Controller（other-adapter）
6. 按需创建 DatabaseService 或 Gateway（other-infrastructure）
7. 如需跨服务调用，添加 Inner API

---

## 八、质量检查清单

- [ ] 命名清晰，无 a/b/temp 类命名
- [ ] 参数校验（`@Validated`、`@NotNull`）
- [ ] 写操作有 `@Transactional`
- [ ] 金额操作有幂等性保障（bizNo）
- [ ] 并发场景有分布式锁
- [ ] 关键操作有 `log.info` / `log.error`
- [ ] 业务异常用 `BusinessException`
- [ ] 无魔法值（常量或枚举）
- [ ] 分页查询用 `PageResult`
