# 代码实现正反例

正例展示实现应具备的边界，不要求照抄语言或框架。所有需求号、路径和验证命令必须来自当前任务。

## 正例

```ts
export async function createOrder(
  principalId: string,
  idempotencyKey: string,
  input: CreateOrderInput,
): Promise<Order> {
  if (input.items.length === 0) throw new ValidationError("items 不能为空");
  const requestHash = stableHash(input);

  return transaction.run(async (tx) => {
    // claimOrLoad 由数据库唯一约束 (principal_id, idempotency_key) 保证并发下只有一个 owner。
    const claim = await tx.idempotency.claimOrLoad({ principalId, idempotencyKey, requestHash });
    if (claim.requestHash !== requestHash) throw new ConflictError("幂等键已用于不同请求");
    if (claim.orderId) return tx.orders.requireById(claim.orderId);
    if (!claim.ownedByCaller) throw new RetryableConflictError("同一请求正在处理中");

    await tx.inventory.reserve(input.items);
    const order = await tx.orders.insert(input);
    await tx.idempotency.complete(claim.id, order.id);
    return order;
  });
}
```

- 存储约束：`(principal_id, idempotency_key)` 唯一；`claimOrLoad` 在唯一键竞争后重读已存在 claim，不能靠进程内锁。
- 测试覆盖：正常创建、空明细、库存不足整体回滚、相同幂等键并发只创建一单、同键不同请求体冲突、处理中请求给出可重试结果
- 需求追踪：测试计划/报告将 `REQ-014` 对应到该测试命令与真实结果
- 验证证据：`npm test -- order-create` 与 `npm run typecheck` 的真实结果

好在：输入边界、失败路径、数据库级并发所有权、事务语义、外部追溯证据和可运行验证同时存在。示例中的注释解释并发不变量；正式项目只保留代码本身无法清楚表达的约束。

## 反例

```ts
export async function createOrder(input: any) {
  try { return await repository.save(input); } catch { return null; }
}
```

问题：`any` 隐藏契约，异常被吞，失败不可区分，没有事务、测试、需求承接或证据。

## 转换层样板正反例（后端）

正例（Java/Lombok + MapStruct，转换逻辑声明式集中）：

```java
@Data // Lombok 消实体 get/set 样板
public class Order { private Long id; private String status; /* ... */ }

@Mapper(componentModel = "spring") // MapStruct 声明式映射,编译期生成,字段对不上编译报错
public interface OrderConverter {
  OrderVO toVO(Order order);
  List<OrderVO> toVOList(List<Order> orders);
}
```

反例（手写样板，字段增删时错字段温床）：

```java
public OrderVO toVO(Order order) {
  OrderVO vo = new OrderVO();
  vo.setId(order.getId());       // 实体加字段后此处漏 copy,静默丢数据
  vo.setStatus(order.getStatus());
  // ...30 行 getX/setX
  return vo;
}
```

注：ORM 选型（MyBatis-Plus/JPA 等）跟随项目既有决策——本例约束的是"转换与样板"，不切换持久化栈。

## 组件通用化正反例（前端）

正例：三个列表页都用"筛选栏+表格+分页"形态 → 抽 `ProTable`（props 传列定义/请求函数，slots 插操作列），三页全部切换消费；新增列表页只写列定义。

反例：第三个列表页继续复制前两页的 `<el-table>` + 分页 markup（改列宽要改三处，漂移开始）。

## 前端锚点对照正反例

正例：

- 锚点：原型说明素材证据表 → `docs/context/prototype/src/pages/OrderConfirm.tsx:40-96`（或运行中的预览路由）
- 文案：按钮“提交订单”、空态“暂无待支付商品”逐字迁移，不自编同义文案
- 形态：金额摘要固定在提交按钮上方、宽度不足时底部吸附；新增地址跳独立页而非弹窗
- 列与操作：列“商品/单价/数量/小计”，行操作仅“删除”；不新增原型没有的批量操作
- 通用组件：只封装请求与分页逻辑；搜索区、卡片、单据样式按本页原型，不用通用 CRUD 外壳覆盖

反例：

- 用通用表格页组件套出“订单管理”：标题自编（“确认付款”改成“去结算”）、把跳页改成弹窗、自行加“批量删除”
- 问题：文案、形态、操作集三处偏离锚点；原型说明文档再详细也兜不住实现阶段未对照素材的自由发挥

## SOLID 与分层正反例（后端 Java）

正例（职责各归其位：Controller 只组装、Service 编排事务、领域对象守状态规则）：

```java
@RestController
@RequestMapping("/orders")
public class OrderController {                 // 只做参数绑定与响应组装
    private final OrderApplicationService service;
    @PostMapping
    public Result<OrderVO> create(@Valid @RequestBody CreateOrderCmd cmd) {
        return Result.ok(service.create(cmd));
    }
}

@Service
public class OrderApplicationService {          // 编排 + 事务边界
    @Transactional
    public OrderVO create(CreateOrderCmd cmd) {
        inventoryService.check(cmd);            // 领域规则委托领域服务
        Order order = Order.create(cmd);        // 状态合法性由领域行为守卫
        return OrderVO.from(repository.save(order));
    }
}
```

反例（Controller 混业务判断与 SQL；领域状态被外部随意改）：

```java
@PostMapping
public Result<OrderVO> create(@RequestBody CreateOrderCmd cmd) {
    if (cmd.getItems().isEmpty()) throw new BizException("明细为空");  // 业务判断，该进领域
    orderMapper.insertBySql("INSERT INTO t_order ...");               // SQL，该进 Repository
    order.setStatus("PAID");                                          // 绕过领域行为直改状态
    return Result.ok(new OrderVO());
}
```

问题：三处职责混在一层，权限/规则变化要改 Controller，无法单测领域规则。确定性源码扫描会拦 Controller 内 SQL；业务判断与状态直改由审查兜底。

## SOLID 与分层正反例（前端 TS）

正例（HTTP 收口到 api/、状态规则抽 composable、魔法值常量化）：

```ts
// api/order.ts —— HTTP 唯一收口
export function createOrder(cmd: CreateOrderCmd): Promise<OrderVO> {
  return request.post<OrderVO>("/orders", cmd);
}
// composables/useOrderStatus.ts —— 规则收敛，可独立测试
export function useOrderStatus() {
  const validateStatus = (s: OrderStatus) => allowedNextOf(order.status).includes(s);
  return { validateStatus };
}
```

反例（组件直调 HTTP + watch 散落规则 + 裸字符串）：

```ts
// Order.vue 组件内
axios.post("/api/order/create", form);          // 直调：改路径要全局搜字符串
watch(() => form.status, () => {
  if (form.status === "A") { submit(); }        // 裸魔法值 + 规则散落组件
});
```

问题：契约变化散落 N 个组件；状态规则无法复用与测试。确定性源码扫描会拦 `.vue` 内直调 axios/fetch。

## 结构指标反例（确定性门禁）

```ts
// 反例 1：方法 87 行、嵌套 4 层、参数 6 个——三项全超
export function buildReport(a, b, c, d, e, f) {
  if (a) { for (...) { if (...) { while (...) { /* 第 4 层 */ } } } }
  /* ...87 行... */
}
// 正例：参数收拢为 cmd 对象、卫语句拍平嵌套、按阶段拆 buildHeader/buildRows/buildSummary
```

修复方向不是注释掉检查：先拆职责（每个阶段一个函数），真有合理场景（长映射表/生成代码）用 waiver 按 `file:Ln` 豁免并注明理由。

## 注释正反例（确定性门禁）

反例（噪音注释——任务编号、变更日志、复述代码，全被门禁拦截）：

```java
// REQ-014 修改                          ← 任务编号：追踪进测试名称（it("REQ-014 ...")）
// 2026-08-27 by zhang 修复金额计算        ← 变更日志：git log 已承载
// 调用 settle 方法结算订单                ← 复述代码：零信息量
public BigDecimal settle(Long orderId) { ... }
```

正例（只解释代码本身无法表达的意图；公共 API 用项目约定的 doc 格式）：

```java
/**
 * 结算订单并返回应收金额。
 * 失败语义：明细缺失抛 BizException，不产生任何状态变化。
 */
public BigDecimal settle(Long orderId) {
    // 金额按明细重算而非取预缴快照：预缴期间明细可追加减项，快照会少收
    //（不变量：settle 后 bill.locked=1，明细不可再变）。
    ...
}
```

好在：新读者不查聊天记录、不看 git 历史就能理解行为边界与不变量；追踪与历史各有唯一承载（测试名称/git log），注释零噪音。

## 迁移到当前项目

替换语言、框架、实体、错误类型、事务边界和需求编号。保留的是“类型明确、失败可观察、契约可追踪、测试可运行”的原则；证据必须来自当前项目的编译和测试结果。
