智能代码生成与优化助手

66 浏览
5 试用
1 购买
Oct 15, 2025更新

本提示词专为程序开发场景设计,能够根据用户需求智能生成高质量代码,并提供详细的代码解释和优化建议。通过任务分步法和链式思维分析,确保代码逻辑严谨、结构清晰。支持多种编程语言和开发框架,涵盖函数实现、类设计、算法优化等核心开发需求。具备代码审查和性能分析能力,能够识别潜在问题并提供改进方案,帮助开发者提升编码效率和代码质量,适用于日常开发、项目重构、技术学习等多种办公场景。

代码实现

# 文件: pom.xml
<!-- Spring Boot 后端示例:用户与订单 REST 服务 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>shop-service</artifactId>
  <version>1.0.0</version>
  <name>shop-service</name>
  <properties>
    <java.version>17</java.version>
    <spring-boot.version>3.3.3</spring-boot.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <!-- Web & Validation -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <!-- JPA & DB -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- H2 内存数据库,便于快速运行 -->
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>
    <!-- 仅使用密码加密(不引入完整 security) -->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-crypto</artifactId>
    </dependency>
    <!-- 测试(可选) -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <mainClass>com.example.shop.Application</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

# 文件: src/main/resources/application.yml
server:
  port: 8080

spring:
  datasource:
    url: jdbc:h2:mem:shopdb;DB_CLOSE_DELAY=-1;MODE=MySQL
    username: sa
    password: ""
    driver-class-name: org.h2.Driver
    hikari:
      pool-name: ShopHikariPool
      minimum-idle: 5
      maximum-pool-size: 20
      idle-timeout: 60000
      max-lifetime: 1800000
      connection-timeout: 30000
      validation-timeout: 5000
      leak-detection-threshold: 20000
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true
        show_sql: false
        jdbc:
          batch_size: 50
        order_inserts: true
        order_updates: true
  jackson:
    serialization:
      WRITE_DATES_AS_TIMESTAMPS: false

logging:
  level:
    root: INFO
    com.example.shop: DEBUG

# 文件: src/main/java/com/example/shop/Application.java
package com.example.shop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

# 文件: src/main/java/com/example/shop/domain/User.java
package com.example.shop.domain;

import jakarta.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "users", 
       uniqueConstraints = @UniqueConstraint(name = "uk_users_username", columnNames = "username"))
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(nullable = false, length = 64)
  private String username;

  @Column(nullable = false, length = 100)
  private String passwordHash;

  @Column(nullable = false)
  private LocalDateTime createdAt;

  public User() {}

  public User(String username, String passwordHash) {
    this.username = username;
    this.passwordHash = passwordHash;
    this.createdAt = LocalDateTime.now();
  }

  public Long getId() { return id; }
  public void setId(Long id) { this.id = id; }
  public String getUsername() { return username; }
  public void setUsername(String username) { this.username = username; }
  public String getPasswordHash() { return passwordHash; }
  public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
  public LocalDateTime getCreatedAt() { return createdAt; }
  public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

# 文件: src/main/java/com/example/shop/domain/OrderStatus.java
package com.example.shop.domain;

public enum OrderStatus {
  PENDING, PAID, CANCELLED
}

# 文件: src/main/java/com/example/shop/domain/Order.java
package com.example.shop.domain;

import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;

@Entity
@Table(name = "orders", indexes = {
    @Index(name = "idx_orders_user_id", columnList = "user_id"),
    @Index(name = "idx_orders_created_at", columnList = "createdAt")
})
public class Order {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @ManyToOne(fetch = FetchType.LAZY, optional = false)
  @JoinColumn(name = "user_id", nullable = false,
              foreignKey = @ForeignKey(name = "fk_orders_user"))
  private User user;

  @Column(nullable = false, length = 128)
  private String itemName;

  @Column(nullable = false, precision = 12, scale = 2)
  private BigDecimal amount;

  @Enumerated(EnumType.STRING)
  @Column(nullable = false, length = 16)
  private OrderStatus status = OrderStatus.PENDING;

  @Column(nullable = false)
  private LocalDateTime createdAt = LocalDateTime.now();

  public Order() {}

  public Order(User user, String itemName, BigDecimal amount) {
    this.user = user;
    this.itemName = itemName;
    this.amount = amount;
    this.status = OrderStatus.PENDING;
    this.createdAt = LocalDateTime.now();
  }

  public Long getId() { return id; }
  public void setId(Long id) { this.id = id; }
  public User getUser() { return user; }
  public void setUser(User user) { this.user = user; }
  public String getItemName() { return itemName; }
  public void setItemName(String itemName) { this.itemName = itemName; }
  public BigDecimal getAmount() { return amount; }
  public void setAmount(BigDecimal amount) { this.amount = amount; }
  public OrderStatus getStatus() { return status; }
  public void setStatus(OrderStatus status) { this.status = status; }
  public LocalDateTime getCreatedAt() { return createdAt; }
  public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

# 文件: src/main/java/com/example/shop/repository/UserRepository.java
package com.example.shop.repository;

import com.example.shop.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
  Optional<User> findByUsername(String username);
  boolean existsByUsername(String username);
}

# 文件: src/main/java/com/example/shop/repository/OrderRepository.java
package com.example.shop.repository;

import com.example.shop.domain.Order;
import com.example.shop.domain.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, Long> {
  Page<Order> findByUser(User user, Pageable pageable);
}

# 文件: src/main/java/com/example/shop/dto/RegisterRequest.java
package com.example.shop.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class RegisterRequest {
  @NotBlank
  @Size(min = 3, max = 32)
  private String username;

  @NotBlank
  @Size(min = 6, max = 64)
  private String password;

  public String getUsername() { return username; }
  public void setUsername(String username) { this.username = username; }
  public String getPassword() { return password; }
  public void setPassword(String password) { this.password = password; }
}

# 文件: src/main/java/com/example/shop/dto/LoginRequest.java
package com.example.shop.dto;

import jakarta.validation.constraints.NotBlank;

public class LoginRequest {
  @NotBlank
  private String username;
  @NotBlank
  private String password;

  public String getUsername() { return username; }
  public void setUsername(String username) { this.username = username; }
  public String getPassword() { return password; }
  public void setPassword(String password) { this.password = password; }
}

# 文件: src/main/java/com/example/shop/dto/LoginResponse.java
package com.example.shop.dto;

public class LoginResponse {
  private String token;

  public LoginResponse() {}
  public LoginResponse(String token) { this.token = token; }

  public String getToken() { return token; }
  public void setToken(String token) { this.token = token; }
}

# 文件: src/main/java/com/example/shop/dto/OrderCreateRequest.java
package com.example.shop.dto;

import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;

public class OrderCreateRequest {
  @NotBlank
  private String itemName;

  @NotNull
  @DecimalMin(value = "0.01")
  private BigDecimal amount;

  public String getItemName() { return itemName; }
  public void setItemName(String itemName) { this.itemName = itemName; }
  public BigDecimal getAmount() { return amount; }
  public void setAmount(BigDecimal amount) { this.amount = amount; }
}

# 文件: src/main/java/com/example/shop/dto/OrderResponse.java
package com.example.shop.dto;

import com.example.shop.domain.OrderStatus;
import java.math.BigDecimal;
import java.time.LocalDateTime;

public class OrderResponse {
  private Long id;
  private String itemName;
  private BigDecimal amount;
  private OrderStatus status;
  private LocalDateTime createdAt;

  public OrderResponse() {}

  public OrderResponse(Long id, String itemName, BigDecimal amount, OrderStatus status, LocalDateTime createdAt) {
    this.id = id;
    this.itemName = itemName;
    this.amount = amount;
    this.status = status;
    this.createdAt = createdAt;
  }

  public Long getId() { return id; }
  public void setId(Long id) { this.id = id; }
  public String getItemName() { return itemName; }
  public void setItemName(String itemName) { this.itemName = itemName; }
  public BigDecimal getAmount() { return amount; }
  public void setAmount(BigDecimal amount) { this.amount = amount; }
  public OrderStatus getStatus() { return status; }
  public void setStatus(OrderStatus status) { this.status = status; }
  public LocalDateTime getCreatedAt() { return createdAt; }
  public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

# 文件: src/main/java/com/example/shop/auth/TokenService.java
package com.example.shop.auth;

import org.springframework.stereotype.Service;
import java.time.Instant;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class TokenService {
  private static class TokenInfo {
    final Long userId;
    final Instant expireAt;
    TokenInfo(Long userId, Instant expireAt) {
      this.userId = userId; this.expireAt = expireAt;
    }
  }

  private final Map<String, TokenInfo> store = new ConcurrentHashMap<>();
  private static final Duration TTL = Duration.ofHours(24);

  public String issue(Long userId) {
    String token = UUID.randomUUID().toString();
    store.put(token, new TokenInfo(userId, Instant.now().plus(TTL)));
    return token;
  }

  public Long verify(String token) {
    TokenInfo info = store.get(token);
    if (info == null) return null;
    if (Instant.now().isAfter(info.expireAt)) {
      store.remove(token);
      return null;
    }
    return info.userId;
  }

  public void revoke(String token) {
    store.remove(token);
  }
}

# 文件: src/main/java/com/example/shop/auth/TokenAuthFilter.java
package com.example.shop.auth;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class TokenAuthFilter extends OncePerRequestFilter {

  private final TokenService tokenService;

  public TokenAuthFilter(TokenService tokenService) {
    this.tokenService = tokenService;
  }

  @Override
  protected boolean shouldNotFilter(HttpServletRequest request) {
    String path = request.getRequestURI();
    // 公共接口无需鉴权
    return path.startsWith("/api/auth/");
  }

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
    String header = request.getHeader("Authorization");
    if (header == null || !header.startsWith("Bearer ")) {
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      response.setContentType("application/json");
      response.getWriter().write("{\"message\":\"Unauthorized: missing Bearer token\"}");
      return;
    }
    String token = header.substring("Bearer ".length());
    Long userId = tokenService.verify(token);
    if (userId == null) {
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      response.setContentType("application/json");
      response.getWriter().write("{\"message\":\"Unauthorized: invalid/expired token\"}");
      return;
    }
    request.setAttribute("userId", userId);
    filterChain.doFilter(request, response);
  }
}

# 文件: src/main/java/com/example/shop/service/UserService.java
package com.example.shop.service;

import com.example.shop.domain.User;
import com.example.shop.repository.UserRepository;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;

@Service
public class UserService {
  private final UserRepository userRepository;

  public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  @Transactional
  public User register(String username, String rawPassword) {
    if (userRepository.existsByUsername(username)) {
      throw new IllegalArgumentException("Username already exists");
    }
    String salt = BCrypt.gensalt(12);
    String hash = BCrypt.hashpw(rawPassword, salt);
    User user = new User(username, hash);
    try {
      return userRepository.save(user);
    } catch (DataIntegrityViolationException ex) {
      // 并发注册时兜底
      throw new IllegalArgumentException("Username already exists");
    }
  }

  @Transactional(readOnly = true)
  public Optional<User> authenticate(String username, String rawPassword) {
    return userRepository.findByUsername(username)
        .filter(u -> BCrypt.checkpw(rawPassword, u.getPasswordHash()));
  }

  @Transactional(readOnly = true)
  public User requireById(Long id) {
    return userRepository.findById(id)
        .orElseThrow(() -> new IllegalArgumentException("User not found"));
  }
}

# 文件: src/main/java/com/example/shop/service/OrderService.java
package com.example.shop.service;

import com.example.shop.domain.Order;
import com.example.shop.domain.User;
import com.example.shop.repository.OrderRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;

@Service
public class OrderService {
  private final OrderRepository orderRepository;
  private final UserService userService;

  public OrderService(OrderRepository orderRepository, UserService userService) {
    this.orderRepository = orderRepository;
    this.userService = userService;
  }

  @Transactional
  public Order placeOrder(Long userId, String itemName, BigDecimal amount) {
    if (amount == null || amount.signum() <= 0) {
      throw new IllegalArgumentException("Amount must be positive");
    }
    User user = userService.requireById(userId);
    Order order = new Order(user, itemName, amount);
    return orderRepository.save(order);
  }

  @Transactional(readOnly = true)
  public Page<Order> listOrders(Long userId, Pageable pageable) {
    User user = userService.requireById(userId);
    return orderRepository.findByUser(user, pageable);
  }
}

# 文件: src/main/java/com/example/shop/web/AuthController.java
package com.example.shop.web;

import com.example.shop.auth.TokenService;
import com.example.shop.domain.User;
import com.example.shop.dto.LoginRequest;
import com.example.shop.dto.LoginResponse;
import com.example.shop.dto.RegisterRequest;
import com.example.shop.service.UserService;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

  private final UserService userService;
  private final TokenService tokenService;

  public AuthController(UserService userService, TokenService tokenService) {
    this.userService = userService;
    this.tokenService = tokenService;
  }

  @PostMapping("/register")
  public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest req) {
    User user = userService.register(req.getUsername(), req.getPassword());
    return ResponseEntity.ok().body(
        java.util.Map.of("id", user.getId(), "username", user.getUsername(), "createdAt", user.getCreatedAt())
    );
  }

  @PostMapping("/login")
  public ResponseEntity<?> login(@Valid @RequestBody LoginRequest req) {
    return userService.authenticate(req.getUsername(), req.getPassword())
        .map(u -> ResponseEntity.ok(new LoginResponse(tokenService.issue(u.getId()))))
        .orElseGet(() -> ResponseEntity.status(401).body(java.util.Map.of("message", "Invalid credentials")));
  }
}

# 文件: src/main/java/com/example/shop/web/OrderController.java
package com.example.shop.web;

import com.example.shop.domain.Order;
import com.example.shop.dto.OrderCreateRequest;
import com.example.shop.dto.OrderResponse;
import com.example.shop.service.OrderService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/orders")
public class OrderController {

  private final OrderService orderService;

  public OrderController(OrderService orderService) {
    this.orderService = orderService;
  }

  @PostMapping
  public ResponseEntity<?> create(@Valid @RequestBody OrderCreateRequest req, HttpServletRequest request) {
    Long userId = (Long) request.getAttribute("userId");
    Order saved = orderService.placeOrder(userId, req.getItemName(), req.getAmount());
    return ResponseEntity.ok(toDto(saved));
  }

  @GetMapping
  public ResponseEntity<?> list(@RequestParam(defaultValue = "0") int page,
                                @RequestParam(defaultValue = "10") int size,
                                HttpServletRequest request) {
    Long userId = (Long) request.getAttribute("userId");
    Page<Order> result = orderService.listOrders(userId, PageRequest.of(page, size));
    return ResponseEntity.ok(result.map(this::toDto));
  }

  private OrderResponse toDto(Order o) {
    return new OrderResponse(o.getId(), o.getItemName(), o.getAmount(), o.getStatus(), o.getCreatedAt());
  }
}

# 文件: src/main/java/com/example/shop/exception/GlobalExceptionHandler.java
package com.example.shop.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Map;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {

  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ResponseEntity<?> handleValidation(MethodArgumentNotValidException ex) {
    Map<String, String> errors = ex.getBindingResult().getFieldErrors().stream()
        .collect(Collectors.toMap(
            fe -> fe.getField(),
            fe -> fe.getDefaultMessage(),
            (a, b) -> a
        ));
    return ResponseEntity.badRequest().body(Map.of("message", "Validation failed", "errors", errors));
  }

  @ExceptionHandler(IllegalArgumentException.class)
  public ResponseEntity<?> handleIllegalArgument(IllegalArgumentException ex) {
    return ResponseEntity.badRequest().body(Map.of("message", ex.getMessage()));
  }

  @ExceptionHandler(Exception.class)
  public ResponseEntity<?> handleOther(Exception ex) {
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
        .body(Map.of("message", "Internal error", "detail", ex.getMessage()));
  }
}

# 文件: scripts/run.sh
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/.."
if [ -x "./mvnw" ]; then
  ./mvnw spring-boot:run
else
  mvn spring-boot:run
fi

# 文件: scripts/run.ps1
Param()
$ErrorActionPreference = "Stop"
Set-Location (Split-Path $MyInvocation.MyCommand.Path) | Out-Null
Set-Location .. | Out-Null
if (Test-Path ".\mvnw.cmd") {
  .\mvnw.cmd spring-boot:run
} else {
  mvn spring-boot:run
}

代码说明

  • 功能描述:

    • 提供用户注册与登录接口:/api/auth/register、/api/auth/login。
    • 使用简单的 Bearer Token 风格的鉴权(TokenService 发行/验证),保护订单相关接口。
    • 提供下单与查单 REST 接口:POST /api/orders 创建订单,GET /api/orders 分页查询当前用户订单。
    • 使用 Spring Data JPA 进行数据访问,内置 H2 内存数据库可直接运行。
    • 通过 @Transactional 管理事务,读写分离(readOnly),并通过 HikariCP 管理连接池。
  • 参数说明:

    • RegisterRequest
      • username:字符串,3-32 字符
      • password:字符串,6-64 字符
    • LoginRequest
      • username:字符串,必填
      • password:字符串,必填
    • OrderCreateRequest
      • itemName:商品名称,必填
      • amount:订单金额,>= 0.01
  • 返回值:

    • /api/auth/register:返回新用户的 id、username、createdAt。
    • /api/auth/login:返回 { token }。
    • /api/orders POST:返回新创建订单的 OrderResponse。
    • /api/orders GET:返回分页的订单列表(每条为 OrderResponse)。

使用示例

# 启动服务
bash scripts/run.sh
# 或 Windows PowerShell
# ./scripts/run.ps1

# 1) 注册用户
curl -sS -X POST "http://localhost:8080/api/auth/register" \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"p@ssw0rd"}'

# 2) 登录获取 token
TOKEN=$(curl -sS -X POST "http://localhost:8080/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"p@ssw0rd"}' | jq -r '.token')

echo "TOKEN=$TOKEN"

# 3) 创建订单
curl -sS -X POST "http://localhost:8080/api/orders" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"itemName":"Book","amount":39.99}'

# 4) 分页查询订单
curl -sS "http://localhost:8080/api/orders?page=0&size=10" \
  -H "Authorization: Bearer $TOKEN"

优化建议

  • 性能考虑:

    • 连接池:
      • 使用 HikariCP(已在 application.yml 配置),根据环境调整 maximum-pool-size(典型为 CPU×2 到 ×4),并设置 leak-detection-threshold 便于检测连接泄漏。
      • 将 connection-timeout 控制在 30s 左右,避免请求长时间卡顿。
    • JPA 优化:
      • 已启用批量写入(hibernate.jdbc.batch_size=50),在批量插入/更新场景提升性能。
      • 为 orders(user_id, createdAt) 建立索引(已在实体上声明),加速查单。
      • 读操作标记 @Transactional(readOnly = true) 降低事务开销并允许底层优化。
    • 资源使用:
      • 控制 page size(如 10-50),避免一次性返回大量数据。
      • 关闭 show_sql 以减少 IO;生产环境将日志级别控制在 INFO。
  • 扩展性:

    • 身份认证:
      • 将简单内存 Token 替换为 JWT(如加入 jjwt)或接入 Spring Security,支持角色和权限控制。
      • 将 Token 存储迁移至 Redis 以支持水平扩展与统一过期管理。
    • 业务扩展:
      • 订单状态流转:新增支付、取消等端点;引入事件驱动(如 Spring 事件或消息队列)进行异步处理。
      • 增加订单搜索与统计接口,支持时间范围、金额区间过滤。
    • 数据层:
      • 拆分读写数据源(Read/Write Split),读库使用只读事务与更大的连接池。
      • 引入审计字段(updatedAt、version)并开启乐观锁,保证并发安全。
  • 注意事项:

    • 密码安全:使用 BCrypt 哈希(已实现),禁止明文存储。生产环境中增加密码复杂度校验与登录限制。
    • Token 安全:目前为内存存储且 24h 过期,重启后失效。生产环境应使用持久化或分布式缓存存储,并考虑刷新令牌。
    • 事务边界:仅在服务层使用 @Transactional,控制好方法粒度;避免在控制器层开启事务。
    • 数据迁移:H2 仅用于开发演示,上线需替换为 MySQL/PostgreSQL,并使用 Flyway/Liquibase 管理 schema。
  • DAO层重构建议:

    • 现状常见问题(旧 DAO 层可能存在):
      • 手写 JDBC,拼接 SQL 存在注入风险;连接未正确关闭导致泄漏;事务由调用方手工管理,边界不清晰。
      • 缺乏分页与统一的异常处理;模型映射分散、重复代码多。
    • 重构方案:
      • 采用 Repository 模式:迁移到 Spring Data JPA 或 MyBatis。
        • JPA:将表结构映射为 @Entity,定义 JpaRepository 接口,使用方法名派生或 @Query 实现查询。
        • MyBatis:提取 Mapper 接口与 XML/注解 SQL,保留对复杂 SQL 的精细控制。
      • 统一事务管理:在 Service 层使用 @Transactional 控制读写事务和传播行为;DAO 层不直接处理事务。
      • 安全与性能:
        • 参数化查询,彻底消除 SQL 注入风险。
        • 加入分页(Pageable 或 LIMIT/OFFSET),避免全表扫描。
        • 为高频查询字段加索引(如 users.username,orders.user_id, createdAt)。
        • 避免 N+1:JPA 中合理使用 fetch 策略与关联查询;MyBatis 中通过合适的 resultMap 与 join。
      • 连接管理:通过 DataSource(HikariCP)统一管理连接,禁止手工获取与释放。
      • 领域建模:提取 DTO/VO 与实体分离,控制序列化边界;在 Service 中完成组装与转换。
      • 代码规范:抽取公共异常与错误码;日志标准化;单元测试覆盖关键 DAO 方法。
  • 事务与连接池优化建议(总结):

    • 事务:
      • 写操作使用默认传播 REQUIRED;读操作使用 readOnly=true。
      • 根据业务考虑隔离级别:查询为 READ_COMMITTED,涉及库存扣减等可能考虑 REPEATABLE_READ 或使用悲观/乐观锁机制。
      • 缩小事务范围,避免将网络调用置于事务中。
    • 连接池:
      • 监控连接使用情况(Hikari 指标暴露到 Prometheus/Micrometer),按负载调整 maximum-pool-size。
      • 合理设置 maxLifetime 小于数据库端连接超时,避免池中存活连接被数据库端回收。
      • 使用连接健康检查(validationTimeout),并确保 SQL 或 ping(Hikari 默认配置即可)。

代码实现

// ===== package.json (参考) =====
// 依赖建议:react 18+, react-router-dom 6+, zustand, clsx(可选)
/*
{
  "name": "product-demo",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "clsx": "^2.1.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.26.2",
    "zustand": "^4.5.5"
  },
  "devDependencies": {
    "@types/react": "^18.3.10",
    "@types/react-dom": "^18.3.0",
    "typescript": "^5.6.3",
    "vite": "^5.4.9"
  }
}
*/

// ===== src/types/product.ts =====
export interface Product {
  id: string;
  title: string;
  price: number;
  thumbnail: string;
  description: string;
  rating: number; // 0-5
  stock: number;
}

export interface PagedResult<T> {
  items: T[];
  page: number;
  pageSize: number;
  total: number;
}

// ===== src/api/products.ts =====
/**
 * 轻量模拟数据层:
 * - 提供分页列表与详情接口
 * - 支持 AbortSignal 取消请求
 * - 内置 200~600ms 随机网络延迟
 */
import type { Product, PagedResult } from "../types/product";

const MOCK_DB: Product[] = Array.from({ length: 60 }).map((_, i) => ({
  id: String(i + 1),
  title: `示例商品 #${i + 1}`,
  price: Math.round((Math.random() * 900 + 100) * 100) / 100,
  thumbnail: `https://picsum.photos/seed/product_${i + 1}/320/220`,
  description:
    "这是一段示例商品描述,支持多行文本,用于展示详情内容与样式效果。",
  rating: Math.round(Math.random() * 50) / 10, // 0.0 - 5.0
  stock: Math.floor(Math.random() * 200),
}));

const delay = (ms: number, signal?: AbortSignal) =>
  new Promise<void>((resolve, reject) => {
    const timer = setTimeout(resolve, ms);
    const onAbort = () => {
      clearTimeout(timer);
      reject(new DOMException("Aborted", "AbortError"));
    };
    if (signal) {
      if (signal.aborted) onAbort();
      signal.addEventListener("abort", onAbort, { once: true });
    }
  });

export async function fetchProducts(
  page = 1,
  pageSize = 20,
  q?: string,
  signal?: AbortSignal
): Promise<PagedResult<Product>> {
  await delay(200 + Math.random() * 400, signal);
  const start = (page - 1) * pageSize;
  const end = start + pageSize;

  const filtered = q
    ? MOCK_DB.filter(
        (p) =>
          p.title.toLowerCase().includes(q.toLowerCase()) ||
          p.description.toLowerCase().includes(q.toLowerCase())
      )
    : MOCK_DB;

  return {
    items: filtered.slice(start, end),
    page,
    pageSize,
    total: filtered.length,
  };
}

export async function fetchProductById(
  id: string,
  signal?: AbortSignal
): Promise<Product> {
  await delay(200 + Math.random() * 400, signal);
  const prod = MOCK_DB.find((p) => p.id === id);
  if (!prod) throw new Error("商品不存在");
  return prod;
}

// ===== src/store/products.ts =====
/**
 * 轻量状态管理(Zustand):
 * - 规范化缓存(map 存储,避免重复对象引用引起的重渲染)
 * - 列表分页状态 + 搜索词
 * - 选择性订阅(selector + shallow),减少渲染
 */
import { create } from "zustand";
import { shallow } from "zustand/shallow";
import type { Product, PagedResult } from "../types/product";
import { fetchProductById, fetchProducts } from "../api/products";

type Status = "idle" | "loading" | "success" | "error";

interface ProductState {
  // 数据
  productsById: Map<string, Product>;
  listIds: string[]; // 当前列表 id 集
  total: number;
  page: number;
  pageSize: number;
  query: string;
  // 状态
  listStatus: Status;
  detailStatus: Record<string, Status>;
  error?: string;
  // 行为
  setQuery: (q: string) => void;
  loadList: (opts?: { page?: number; pageSize?: number; q?: string }) => Promise<void>;
  loadProduct: (id: string) => Promise<Product | undefined>;
  prefetchProduct: (id: string) => void;
  clearError: () => void;
}

export const useProductStore = create<ProductState>((set, get) => ({
  productsById: new Map(),
  listIds: [],
  total: 0,
  page: 1,
  pageSize: 20,
  query: "",
  listStatus: "idle",
  detailStatus: {},
  error: undefined,

  setQuery: (q) => set({ query: q }),

  clearError: () => set({ error: undefined }),

  loadList: async (opts) => {
    const page = opts?.page ?? get().page;
    const pageSize = opts?.pageSize ?? get().pageSize;
    const q = opts?.q ?? get().query;

    set({ listStatus: "loading", error: undefined });
    const ac = new AbortController();

    try {
      const res: PagedResult<Product> = await fetchProducts(page, pageSize, q, ac.signal);
      // 规范化写入
      set((state) => {
        const map = new Map(state.productsById);
        for (const p of res.items) map.set(p.id, p);
        return {
          productsById: map,
          listIds: res.items.map((p) => p.id),
          page: res.page,
          pageSize: res.pageSize,
          total: res.total,
          listStatus: "success",
        };
      });
    } catch (e: any) {
      if (e?.name === "AbortError") return;
      set({ listStatus: "error", error: e?.message ?? "加载失败" });
    }
  },

  loadProduct: async (id) => {
    const cached = get().productsById.get(id);
    if (cached) return cached;

    set((state) => ({
      detailStatus: { ...state.detailStatus, [id]: "loading" },
      error: undefined,
    }));
    const ac = new AbortController();

    try {
      const p = await fetchProductById(id, ac.signal);
      set((state) => {
        const map = new Map(state.productsById);
        map.set(p.id, p);
        return {
          productsById: map,
          detailStatus: { ...state.detailStatus, [id]: "success" },
        };
      });
      return p;
    } catch (e: any) {
      if (e?.name === "AbortError") return;
      set((state) => ({
        detailStatus: { ...state.detailStatus, [id]: "error" },
        error: e?.message ?? "加载失败",
      }));
    }
  },

  prefetchProduct: (id) => {
    // 静默预取(不覆盖已存在的错误状态)
    const has = get().productsById.has(id);
    if (has) return;
    // 不 await,避免阻塞 hover 事件
    get().loadProduct(id);
  },
}));

// 可复用选择器(减少组件渲染)
export const useProductList = () =>
  useProductStore(
    (s) => ({
      ids: s.listIds,
      productsById: s.productsById,
      status: s.listStatus,
      page: s.page,
      pageSize: s.pageSize,
      total: s.total,
      query: s.query,
      error: s.error,
    }),
    shallow
  );

export const useProductById = (id: string) =>
  useProductStore(
    (s) => ({
      product: s.productsById.get(id),
      status: s.detailStatus[id] ?? "idle",
    }),
    shallow
  );

// ===== src/components/Skeleton.tsx =====
import React from "react";
import styles from "./styles.module.css";

export const Skeleton: React.FC<{ lines?: number; height?: number }> = ({
  lines = 3,
  height = 14,
}) => {
  return (
    <div aria-busy="true">
      {Array.from({ length: lines }).map((_, i) => (
        <div
          key={i}
          className={styles.skeleton}
          style={{ height, marginBottom: 8 }}
        />
      ))}
    </div>
  );
};

// ===== src/components/ProductCard.tsx =====
import React from "react";
import { Product } from "../types/product";
import { PrefetchLink } from "./PrefetchLink";
import styles from "./styles.module.css";

interface Props {
  product: Product;
}

const Stars: React.FC<{ rating: number }> = React.memo(({ rating }) => {
  const n = Math.round(rating);
  return (
    <span aria-label={`评分 ${rating}`}>
      {"★".repeat(n)}
      {"☆".repeat(5 - n)}
    </span>
  );
});

export const ProductCard = React.memo(function ProductCard({ product }: Props) {
  return (
    <article className={styles.card} aria-label={product.title}>
      <div className={styles.thumbWrap}>
        <img
          src={product.thumbnail}
          alt={product.title}
          loading="lazy"
          decoding="async"
          className={styles.thumb}
        />
      </div>
      <div className={styles.cardBody}>
        <h3 className={styles.title} title={product.title}>
          {product.title}
        </h3>
        <div className={styles.meta}>
          <span className={styles.price}>¥{product.price.toFixed(2)}</span>
          <span className={styles.rating}>
            <Stars rating={product.rating} /> ({product.rating.toFixed(1)})
          </span>
        </div>
        <PrefetchLink to={`/products/${product.id}`} productId={product.id} className={styles.btn}>
          查看详情
        </PrefetchLink>
      </div>
    </article>
  );
});

// ===== src/components/PrefetchLink.tsx =====
import React from "react";
import { Link, LinkProps } from "react-router-dom";
import { useProductStore } from "../store/products";

type PLinkProps = LinkProps & { productId?: string };

/**
 * PrefetchLink
 * - 鼠标悬停时:预取商品详情数据 + 预加载详情页面 chunk
 */
export const PrefetchLink: React.FC<PLinkProps> = ({ productId, onMouseEnter, ...rest }) => {
  const prefetch = useProductStore((s) => s.prefetchProduct);

  const handleMouseEnter: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
    onMouseEnter?.(e);
    if (productId) {
      prefetch(productId);
    }
    // 预加载详情页面 chunk(与路由路径保持一致)
    import("../pages/ProductDetailPage");
  };

  return <Link {...rest} onMouseEnter={handleMouseEnter} />;
};

// ===== src/components/SearchBar.tsx =====
import React from "react";
import styles from "./styles.module.css";

interface Props {
  value: string;
  onChange: (v: string) => void;
}

export const SearchBar: React.FC<Props> = ({ value, onChange }) => {
  const [text, setText] = React.useState(value);
  const deferred = React.useDeferredValue(text);

  React.useEffect(() => {
    onChange(deferred);
  }, [deferred, onChange]);

  return (
    <div className={styles.searchBar}>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="搜索商品(标题/描述)"
        className={styles.input}
        aria-label="搜索商品"
      />
    </div>
  );
};

// ===== src/pages/ProductListPage.tsx =====
import React from "react";
import { ProductCard } from "../components/ProductCard";
import { SearchBar } from "../components/SearchBar";
import { Skeleton } from "../components/Skeleton";
import { useProductList, useProductStore } from "../store/products";
import styles from "../components/styles.module.css";

const PER_PAGE = 20;

const Empty: React.FC<{ q: string }> = ({ q }) => (
  <div className={styles.empty}>没有找到匹配的商品{q ? `:“${q}”` : ""}</div>
);

const Pager: React.FC<{
  page: number;
  pageSize: number;
  total: number;
  onPage: (p: number) => void;
}> = React.memo(({ page, pageSize, total, onPage }) => {
  const pages = Math.max(1, Math.ceil(total / pageSize));
  return (
    <div className={styles.pager} role="navigation" aria-label="分页">
      <button
        className={styles.btn}
        onClick={() => onPage(Math.max(1, page - 1)))}
        disabled={page <= 1}
      >
        上一页
      </button>
      <span className={styles.pageInfo}>
        第 {page} / {pages} 页(共 {total} 条)
      </span>
      <button
        className={styles.btn}
        onClick={() => onPage(Math.min(pages, page + 1))}
        disabled={page >= pages}
      >
        下一页
      </button>
    </div>
  );
});

const ProductListInner: React.FC = () => {
  const { ids, productsById, status, page, pageSize, total, query, error } =
    useProductList();
  const loadList = useProductStore((s) => s.loadList);
  const setQuery = useProductStore((s) => s.setQuery);

  React.useEffect(() => {
    loadList({ page: 1, pageSize: PER_PAGE });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const onChangeQuery = React.useCallback(
    (q: string) => {
      setQuery(q);
      loadList({ page: 1, pageSize: PER_PAGE, q });
    },
    [setQuery, loadList]
  );

  const onPage = React.useCallback(
    (p: number) => {
      loadList({ page: p, pageSize, q: query });
    },
    [loadList, pageSize, query]
  );

  return (
    <div className={styles.container}>
      <h2 className={styles.h2}>商品列表</h2>
      <SearchBar value={query} onChange={onChangeQuery} />

      {status === "loading" && ids.length === 0 ? (
        <div className={styles.grid}>
          {Array.from({ length: 12 }).map((_, i) => (
            <div key={i} className={styles.card}>
              <div className={styles.thumbWrap}>
                <div className={styles.skeleton} style={{ height: 220 }} />
              </div>
              <div className={styles.cardBody}>
                <Skeleton lines={2} />
              </div>
            </div>
          ))}
        </div>
      ) : error ? (
        <div className={styles.error} role="alert">
          加载失败:{error}
        </div>
      ) : ids.length === 0 ? (
        <Empty q={query} />
      ) : (
        <>
          <div className={styles.grid}>
            {ids.map((id) => {
              const p = productsById.get(id)!;
              return <ProductCard key={id} product={p} />;
            })}
          </div>
          <Pager
            page={page}
            pageSize={pageSize}
            total={total}
            onPage={onPage}
          />
        </>
      )}
    </div>
  );
};

export default function ProductListPage() {
  return <ProductListInner />;
}

// ===== src/pages/ProductDetailPage.tsx =====
import React from "react";
import { useParams, Link } from "react-router-dom";
import { useProductById, useProductStore } from "../store/products";
import { Skeleton } from "../components/Skeleton";
import styles from "../components/styles.module.css";

export default function ProductDetailPage() {
  const params = useParams();
  const id = params.id!;
  const { product, status } = useProductById(id);
  const loadProduct = useProductStore((s) => s.loadProduct);

  React.useEffect(() => {
    if (!product) loadProduct(id);
  }, [id, product, loadProduct]);

  return (
    <div className={styles.container}>
      <Link to="/" className={styles.linkBack}>
        ← 返回列表
      </Link>

      {status === "loading" && !product ? (
        <div className={styles.detail}>
          <div className={styles.detailImage}>
            <div className={styles.skeleton} style={{ height: 280 }} />
          </div>
          <div className={styles.detailBody}>
            <Skeleton lines={5} />
          </div>
        </div>
      ) : !product ? (
        <div className={styles.error} role="alert">
          商品不存在或加载失败
        </div>
      ) : (
        <article className={styles.detail}>
          <div className={styles.detailImage}>
            <img
              src={product.thumbnail}
              alt={product.title}
              loading="eager"
              className={styles.thumbLarge}
            />
          </div>
          <div className={styles.detailBody}>
            <h2 className={styles.h2}>{product.title}</h2>
            <div className={styles.detailMeta}>
              <span className={styles.priceLarge}>¥{product.price.toFixed(2)}</span>
              <span>评分:{product.rating.toFixed(1)} / 5</span>
              <span>库存:{product.stock}</span>
            </div>
            <p className={styles.desc}>{product.description}</p>
            <button className={styles.btnPrimary} onClick={() => alert("加入购物车成功")}>
              加入购物车
            </button>
          </div>
        </article>
      )}
    </div>
  );
}

// ===== src/router.tsx =====
import React from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";

const ProductListPage = React.lazy(() => import("./pages/ProductListPage"));
const ProductDetailPage = React.lazy(() => import("./pages/ProductDetailPage"));

const withSuspense = (el: React.ReactNode) => (
  <React.Suspense fallback={<div style={{ padding: 24 }}>加载中...</div>}>
    {el}
  </React.Suspense>
);

export const router = createBrowserRouter([
  {
    path: "/",
    element: withSuspense(<ProductListPage />),
  },
  {
    path: "/products/:id",
    element: withSuspense(<ProductDetailPage />),
  },
]);

export function AppRouter() {
  return <RouterProvider router={router} />;
}

// ===== src/App.tsx =====
import React from "react";
import { AppRouter } from "./router";

export default function App() {
  // 可在此放全局布局、主题切换等
  return <AppRouter />;
}

// ===== src/main.tsx =====
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./global.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// ===== src/components/styles.module.css =====
.container {
  max-width: 1080px;
  margin: 0 auto;
  padding: 16px;
}

.h2 {
  font-size: 20px;
  margin: 12px 0 8px;
}

.searchBar {
  margin: 12px 0 16px;
}
.input {
  width: 100%;
  height: 36px;
  padding: 0 12px;
  border: 1px solid #dcdfe6;
  border-radius: 6px;
  outline: none;
}
.input:focus {
  border-color: #409eff;
  box-shadow: 0 0 0 3px rgba(64,158,255,.15);
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
}

.card {
  border: 1px solid #ebeef5;
  border-radius: 8px;
  overflow: hidden;
  background: #fff;
  display: flex;
  flex-direction: column;
}
.thumbWrap {
  background: #fafafa;
}
.thumb {
  width: 100%;
  height: 220px;
  object-fit: cover;
  display: block;
}
.cardBody {
  padding: 10px 12px 12px;
  display: grid;
  gap: 8px;
}
.title {
  font-size: 16px;
  line-height: 1.3;
  height: 42px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: #666;
  font-size: 13px;
}
.price {
  color: #f56c6c;
  font-weight: 600;
}

.btn, .btnPrimary, .linkBack {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: 6px;
  border: 1px solid #dcdfe6;
  background: #fff;
  cursor: pointer;
  text-decoration: none;
  color: #333;
}
.btn:hover, .linkBack:hover {
  border-color: #409eff;
  color: #409eff;
}
.btnPrimary {
  background: #409eff;
  border-color: #409eff;
  color: #fff;
}
.btnPrimary:hover {
  background: #66b1ff;
  border-color: #66b1ff;
}

.skeleton {
  position: relative;
  width: 100%;
  border-radius: 6px;
  background: linear-gradient(90deg, #f2f2f2 25%, #f8f8f8 37%, #f2f2f2 63%);
  background-size: 400% 100%;
  animation: shimmer 1.2s ease-in-out infinite;
}
@keyframes shimmer {
  0% { background-position: 100% 0; }
  100% { background-position: 0 0; }
}

.error {
  background: #fdecea;
  color: #d93025;
  border: 1px solid #f5c6c3;
  padding: 12px;
  border-radius: 8px;
}

.empty {
  color: #909399;
  padding: 32px 0;
  text-align: center;
}

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 16px 0;
}
.pageInfo {
  color: #666;
}

.detail {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-top: 8px;
}
.detailImage {
  background: #fafafa;
  border: 1px solid #ebeef5;
  border-radius: 8px;
  overflow: hidden;
}
.thumbLarge {
  width: 100%;
  height: 280px;
  object-fit: cover;
}
.detailBody {
  display: grid;
  gap: 12px;
}
.detailMeta {
  display: flex;
  gap: 12px;
  color: #666;
}
.priceLarge {
  font-size: 20px;
  font-weight: 700;
  color: #f56c6c;
}
.linkBack {
  margin-bottom: 10px;
  width: fit-content;
}

@media (max-width: 768px) {
  .detail {
    grid-template-columns: 1fr;
  }
}

// ===== src/global.css =====
:root {
  color-scheme: light;
  --bg: #f7f7f9;
  --text: #222;
}
* { box-sizing: border-box; }
html, body, #root { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial;
}
a { color: inherit; }
button { font: inherit; }

代码说明

  • 功能描述:

    • 提供基于 React + TypeScript 的商品列表与详情组件,包含:
      • 路由配置(列表页、详情页,使用 React.lazy 代码分割与 Suspense)
      • 轻量状态管理(Zustand),提供商品列表、详情的规范化缓存、分页与搜索状态
      • 列表页:搜索框(useDeferredValue 降低输入时重渲染压力)、分页、骨架屏、卡片组件(Memo 优化、图片懒加载)
      • 详情页:进入时根据 URL 参数加载数据,显示骨架屏,提供“加入购物车”交互示例
      • PrefetchLink:链接悬停时预取详情数据、预加载详情页面代码块,优化导航性能
      • 样式:CSS Modules,包含卡片网格、按钮、骨架屏、响应式布局
  • 参数说明:

    • store/products.ts
      • useProductStore:Zustand 全局状态
        • productsById: Map<string, Product> 规范化缓存
        • listIds: string[] 当前列表展示的 id 集
        • total, page, pageSize: 分页信息
        • query: 当前搜索关键字
        • listStatus: 列表加载状态
        • detailStatus: 每个商品的详情加载状态
        • error: 错误消息
        • setQuery(q: string): 设置搜索词
        • loadList(opts?): 加载商品列表(支持 page、pageSize、q)
        • loadProduct(id): 加载单个商品详情,返回 Product
        • prefetchProduct(id): 预取商品详情,鼠标悬停等场景短路缓存
        • clearError(): 清理错误
    • 组件
      • ProductCard({ product }): 展示单个商品卡片
      • PrefetchLink({ to, productId, ...LinkProps }): 悬停预取详情数据和页面 chunk 的 Link 封装
      • SearchBar({ value, onChange }): 搜索输入,内部使用 useDeferredValue 平滑更新
      • Skeleton({ lines, height }): 骨架屏
      • ProductListPage:列表页
      • ProductDetailPage:详情页
    • API
      • fetchProducts(page?, pageSize?, q?, signal?): Promise<PagedResult>
      • fetchProductById(id, signal?): Promise
  • 返回值:

    • 页面组件按路由渲染,无显式返回值
    • store 的 loadProduct 返回已加载的 Product 或 undefined(发生错误/取消)

使用示例

// main.tsx 已展示标准挂载方式,以下为在应用中跳转与交互的示例:

// 1) 在任意组件中获取列表并分页
import React from "react";
import { useProductList, useProductStore } from "./store/products";

export const SimpleList: React.FC = () => {
  const { ids, productsById, status } = useProductList();
  const loadList = useProductStore(s => s.loadList);

  React.useEffect(() => {
    loadList({ page: 1, pageSize: 20 });
  }, [loadList]);

  if (status === "loading" && ids.length === 0) return <div>加载中...</div>;
  return (
    <ul>
      {ids.map(id => <li key={id}>{productsById.get(id)?.title}</li>)}
    </ul>
  );
};

// 2) 在任意位置使用预取链接
import { PrefetchLink } from "./components/PrefetchLink";
export const GoDetail: React.FC<{ id: string }> = ({ id }) => (
  <PrefetchLink to={`/products/${id}`} productId={id}>
    查看 {id} 的详情(悬停即预取)
  </PrefetchLink>
);

优化建议

  • 性能考虑:

    • 组件渲染
      • 使用 React.memo 对 ProductCard、Stars、Pager 进行包裹,减少重复渲染
      • 使用 Zustand selector + shallow 做选择性订阅,避免 store 变更触发整树刷新
      • SearchBar 使用 useDeferredValue 延迟搜索词传播,降低输入频繁变更带来的渲染
    • 数据加载
      • PrefetchLink 在鼠标悬停时预取详情数据 + 预加载详情页面 chunk,提升路由切换体验
      • 列表与详情数据规范化存储 Map,复用对象引用,减少 diff 成本
      • API 层支持 AbortSignal,真实接入时可在切换页面时中断未完成请求
    • 静态资源
      • 列表缩略图开启 loading="lazy",详情页主图使用 eager 以确保首屏直出
      • 可结合浏览器缓存/HTTP 缓存头优化图片与 JSON 数据
  • 扩展性:

    • 接入真实后端:将 api/products.ts 替换为实际 REST/GraphQL 请求,并保留同等返回结构
    • 添加分类筛选、价格区间、排序;在 store 中加入 filters,并在 loadList 参数体中透传
    • 列表虚拟滚动:当数据量很大时,可引入 react-window/react-virtual 优化渲染
    • 错误重试与边界:添加 ErrorBoundary,状态层提供 retry 方法,UI 提供重试按钮
    • 国际化与可访问性:引入 i18n;完善 aria-* 属性、键盘导航与焦点管理
  • 注意事项:

    • 路由懒加载路径需与 PrefetchLink 的 import 路径保持一致,否则无法命中预加载
    • Zustand Map 存储为不可序列化对象,若需持久化或 SSR,建议在写入/读出时转普通对象
    • 真实网络环境中请加上错误码分支与重试退避策略;注意超时与取消的处理
    • 图片资源请使用合适的尺寸与压缩格式(webp/avif),避免大图阻塞主线程与网络带宽
    • 如引入第三方 UI 库,请统一设计系统与主题变量,保证样式一致性与可维护性

代码实现

# text_classification_prototype.py
# 功能:可复现实验的文本分类原型,包含数据清洗、特征工程、训练/验证/推理脚本,支持小样本与评估。
# 依赖:Python >= 3.8, scikit-learn, pandas, numpy, joblib
# 使用示例见文末“使用示例”部分。

import argparse
import json
import os
import random
import re
import sys
from dataclasses import dataclass, asdict
from typing import List, Optional, Tuple, Dict, Any

import numpy as np
import pandas as pd
from joblib import dump, load
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.feature_extraction.text import TfidfVectorizer, HashingVectorizer
from sklearn.linear_model import LogisticRegression, SGDClassifier
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split, StratifiedKFold
from sklearn.metrics import (
    accuracy_score,
    f1_score,
    classification_report,
    confusion_matrix,
)
from sklearn.utils import shuffle as sk_shuffle


# =========================
# 可复现实验:统一设置随机种子
# =========================
def set_seed(seed: int = 42):
    os.environ["PYTHONHASHSEED"] = str(seed)
    random.seed(seed)
    np.random.seed(seed)


# =========================
# 数据清洗模块
# =========================
class TextCleaner(BaseEstimator, TransformerMixin):
    """
    文本清洗器:
    - 小写规范化
    - 移除URL/HTML
    - 移除标点、冗余空格
    - 可选:替换数字、保留字母数字
    注:此清洗器不做分词,保持语言通用性(英文/中文均可处理)。
    """

    def __init__(
        self,
        lower: bool = True,
        strip_urls: bool = True,
        strip_html: bool = True,
        remove_punct: bool = True,
        normalize_whitespace: bool = True,
        replace_digits_token: Optional[str] = None,  # 例如 "<NUM>"
    ):
        self.lower = lower
        self.strip_urls = strip_urls
        self.strip_html = strip_html
        self.remove_punct = remove_punct
        self.normalize_whitespace = normalize_whitespace
        self.replace_digits_token = replace_digits_token

        # 预编译正则
        self._url_re = re.compile(r"https?://\S+|www\.\S+")
        self._html_re = re.compile(r"<.*?>")
        # 常见中英文标点(不含空格)
        self._punct_re = re.compile(r"[^\w\s]|\_")
        self._digit_re = re.compile(r"\d+")

    def fit(self, X, y=None):
        return self

    def transform(self, X: List[str]):
        cleaned = []
        for text in X:
            if not isinstance(text, str):
                text = "" if text is None else str(text)
            t = text

            if self.lower:
                t = t.lower()

            if self.strip_urls:
                t = self._url_re.sub(" ", t)

            if self.strip_html:
                t = self._html_re.sub(" ", t)

            if self.replace_digits_token:
                t = self._digit_re.sub(self.replace_digits_token, t)

            if self.remove_punct:
                t = self._punct_re.sub(" ", t)

            if self.normalize_whitespace:
                t = re.sub(r"\s+", " ", t).strip()

            cleaned.append(t)
        return cleaned


# =========================
# 特征工程 + 分类器构建
# =========================
@dataclass
class ModelConfig:
    seed: int = 42
    vectorizer: str = "tfidf"  # ["tfidf", "hashing"]
    ngram_max: int = 2
    min_df: int = 2
    max_features: Optional[int] = 50000
    tfidf_sublinear: bool = True
    tfidf_use_idf: bool = True
    stop_words: Optional[str] = None  # 可选: 'english',对中文一般不使用
    classifier: str = "logreg"  # ["logreg", "linear_svm", "sgd"]
    class_weight: Optional[str] = "balanced"  # None 或 "balanced"
    C: float = 2.0  # LogisticRegression 正则强度
    max_iter: int = 200
    n_jobs: int = -1
    few_shot_k: int = 0  # 每类保留的样本数(<=0表示不启用)
    test_size: float = 0.2
    cv_folds: int = 0  # >0时执行交叉验证
    cleaner_replace_digits_token: Optional[str] = "<NUM>"


def build_pipeline(cfg: ModelConfig) -> Pipeline:
    cleaner = TextCleaner(replace_digits_token=cfg.cleaner_replace_digits_token)

    if cfg.vectorizer == "tfidf":
        vec = TfidfVectorizer(
            ngram_range=(1, cfg.ngram_max),
            min_df=cfg.min_df,
            max_features=cfg.max_features,
            sublinear_tf=cfg.tfidf_sublinear,
            use_idf=cfg.tfidf_use_idf,
            stop_words=cfg.stop_words,
        )
    elif cfg.vectorizer == "hashing":
        vec = HashingVectorizer(
            n_features=cfg.max_features or 2**20,
            ngram_range=(1, cfg.ngram_max),
            alternate_sign=False,
            norm="l2",
        )
    else:
        raise ValueError(f"Unsupported vectorizer: {cfg.vectorizer}")

    if cfg.classifier == "logreg":
        clf = LogisticRegression(
            C=cfg.C,
            max_iter=cfg.max_iter,
            class_weight=cfg.class_weight,
            n_jobs=cfg.n_jobs,
            random_state=cfg.seed,
        )
    elif cfg.classifier == "linear_svm":
        clf = LinearSVC(
            class_weight=cfg.class_weight,
            max_iter=cfg.max_iter,
        )
    elif cfg.classifier == "sgd":
        clf = SGDClassifier(
            loss="log_loss",  # 支持概率输出
            max_iter=cfg.max_iter,
            class_weight=cfg.class_weight,
            random_state=cfg.seed,
            n_jobs=cfg.n_jobs,
        )
    else:
        raise ValueError(f"Unsupported classifier: {cfg.classifier}")

    pipe = Pipeline(
        steps=[
            ("cleaner", cleaner),
            ("vectorizer", vec),
            ("clf", clf),
        ]
    )
    return pipe


# =========================
# 数据读取/采样/拆分
# =========================
def read_dataset(csv_path: str) -> Tuple[List[str], List[str]]:
    df = pd.read_csv(csv_path)
    if "text" not in df.columns or "label" not in df.columns:
        raise ValueError("CSV必须包含text和label两列")
    texts = df["text"].astype(str).tolist()
    labels = df["label"].astype(str).tolist()
    return texts, labels


def few_shot_sample(texts: List[str], labels: List[str], k: int, seed: int):
    if k <= 0:
        return texts, labels
    df = pd.DataFrame({"text": texts, "label": labels})
    sampled = (
        df.groupby("label", group_keys=False)
        .apply(lambda g: g.sample(n=min(k, len(g)), random_state=seed))
        .reset_index(drop=True)
    )
    return sampled["text"].tolist(), sampled["label"].tolist()


# =========================
# 评估指标
# =========================
def compute_metrics(y_true: List[str], y_pred: List[str]) -> Dict[str, Any]:
    acc = accuracy_score(y_true, y_pred)
    f1_macro = f1_score(y_true, y_pred, average="macro")
    f1_micro = f1_score(y_true, y_pred, average="micro")
    report = classification_report(y_true, y_pred, output_dict=True)
    cm = confusion_matrix(y_true, y_pred).tolist()
    return {
        "accuracy": acc,
        "f1_macro": f1_macro,
        "f1_micro": f1_micro,
        "classification_report": report,
        "confusion_matrix": cm,
    }


# =========================
# 训练
# =========================
def train_model(
    train_csv: str,
    val_csv: Optional[str],
    out_dir: str,
    cfg: ModelConfig,
):
    set_seed(cfg.seed)
    os.makedirs(out_dir, exist_ok=True)

    X, y = read_dataset(train_csv)
    # 小样本支持
    X, y = few_shot_sample(X, y, cfg.few_shot_k, cfg.seed)

    # 训练/验证拆分
    if val_csv is not None:
        X_val, y_val = read_dataset(val_csv)
    else:
        X, y = sk_shuffle(X, y, random_state=cfg.seed)
        X_train, X_val, y_train, y_val = train_test_split(
            X, y, test_size=cfg.test_size, stratify=y, random_state=cfg.seed
        )
        X, y = X_train, y_train

    pipe = build_pipeline(cfg)
    pipe.fit(X, y)

    # 验证集评估
    if val_csv is None:
        # X_val/y_val 已在拆分中定义
        val_metrics = compute_metrics(y_val, pipe.predict(X_val))
    else:
        val_metrics = compute_metrics(y_val, pipe.predict(X_val))

    # 可选交叉验证
    cv_results = None
    if cfg.cv_folds and cfg.cv_folds > 1:
        skf = StratifiedKFold(n_splits=cfg.cv_folds, shuffle=True, random_state=cfg.seed)
        cv_metrics = []
        X_all, y_all = read_dataset(train_csv)
        X_all, y_all = few_shot_sample(X_all, y_all, cfg.few_shot_k, cfg.seed)
        for train_idx, test_idx in skf.split(X_all, y_all):
            X_tr = [X_all[i] for i in train_idx]
            y_tr = [y_all[i] for i in train_idx]
            X_te = [X_all[i] for i in test_idx]
            y_te = [y_all[i] for i in test_idx]
            fold_pipe = build_pipeline(cfg)
            fold_pipe.fit(X_tr, y_tr)
            m = compute_metrics(y_te, fold_pipe.predict(X_te))
            cv_metrics.append(m)
        # 汇总指标
        cv_results = {
            "folds": cfg.cv_folds,
            "accuracy_mean": float(np.mean([m["accuracy"] for m in cv_metrics])),
            "f1_macro_mean": float(np.mean([m["f1_macro"] for m in cv_metrics])),
            "f1_micro_mean": float(np.mean([m["f1_micro"] for m in cv_metrics])),
        }

    # 保存模型与配置/指标
    dump(pipe, os.path.join(out_dir, "model.joblib"))
    with open(os.path.join(out_dir, "config.json"), "w", encoding="utf-8") as f:
        json.dump(asdict(cfg), f, ensure_ascii=False, indent=2)
    with open(os.path.join(out_dir, "val_metrics.json"), "w", encoding="utf-8") as f:
        json.dump(val_metrics, f, ensure_ascii=False, indent=2)
    if cv_results:
        with open(os.path.join(out_dir, "cv_metrics.json"), "w", encoding="utf-8") as f:
            json.dump(cv_results, f, ensure_ascii=False, indent=2)

    print("训练完成。模型与指标已保存至:", out_dir)


# =========================
# 验证/评估
# =========================
def validate_model(model_dir: str, eval_csv: str, out_path: Optional[str] = None):
    pipe: Pipeline = load(os.path.join(model_dir, "model.joblib"))
    X, y = read_dataset(eval_csv)
    y_pred = pipe.predict(X)
    metrics = compute_metrics(y, y_pred)
    if out_path:
        with open(out_path, "w", encoding="utf-8") as f:
            json.dump(metrics, f, ensure_ascii=False, indent=2)
    print(json.dumps(metrics, ensure_ascii=False, indent=2))
    return metrics


# =========================
# 推理
# =========================
def infer(
    model_dir: str,
    text: Optional[str] = None,
    input_file: Optional[str] = None,
    output_path: Optional[str] = None,
    show_prob: bool = True,
):
    pipe: Pipeline = load(os.path.join(model_dir, "model.joblib"))

    inputs = []
    if text:
        inputs = [text]
    elif input_file:
        with open(input_file, "r", encoding="utf-8") as f:
            inputs = [line.strip() for line in f if line.strip()]
    else:
        raise ValueError("必须提供 text 或 input_file 之一")

    preds = pipe.predict(inputs)

    # 兼容概率输出(若分类器支持)
    probs_out = None
    if show_prob:
        clf = pipe.named_steps["clf"]
        if hasattr(clf, "predict_proba"):
            probs = clf.predict_proba(pipe.named_steps["vectorizer"].transform(pipe.named_steps["cleaner"].transform(inputs)))
            # 获取标签顺序(若可用)
            if hasattr(clf, "classes_"):
                classes = list(map(str, clf.classes_))
            else:
                classes = None
            probs_out = [{"classes": classes, "prob": p.tolist()} for p in probs]
        else:
            probs_out = None

    results = []
    for i, inp in enumerate(inputs):
        item = {"text": inp, "pred": str(preds[i])}
        if probs_out:
            item["prob"] = probs_out[i]
        results.append(item)

    if output_path:
        with open(output_path, "w", encoding="utf-8") as f:
            json.dump(results, f, ensure_ascii=False, indent=2)

    print(json.dumps(results, ensure_ascii=False, indent=2))
    return results


# =========================
# Demo 数据集构造(无需外网)
# =========================
def build_demo_dataset(out_csv: str):
    """
    构造一个小型三分类数据集(tech/sports/finance),中英混合短句,便于快速试跑。
    """
    tech = [
        "New AI model improves accuracy in image recognition",
        "Database scaling with sharding techniques",
        "云计算平台推出新版本,支持容器编排",
        "前端框架升级,渲染性能提升",
        "Mobile app performance optimized using caching",
        "深度学习训练需要大量数据与算力",
    ]
    sports = [
        "球队本赛季取得五连胜",
        "The runner broke the world record in marathon",
        "足球俱乐部签下新前锋",
        "Basketball playoffs start next week",
        "教练强调防守与团队协作",
        "网球公开赛吸引了众多观众",
    ]
    finance = [
        "Stock market rises amid positive earnings reports",
        "央行宣布降息,刺激经济增长",
        "投资组合需要分散风险",
        "Cryptocurrency volatility increases",
        "季度财报显示收入稳步增长",
        "新基金专注绿色能源项目",
    ]
    rows = []
    for t in tech:
        rows.append({"text": t, "label": "tech"})
    for s in sports:
        rows.append({"text": s, "label": "sports"})
    for f in finance:
        rows.append({"text": f, "label": "finance"})
    df = pd.DataFrame(rows)
    df = sk_shuffle(df, random_state=42)
    df.to_csv(out_csv, index=False)
    print(f"Demo 数据集已写入:{out_csv}")


# =========================
# CLI
# =========================
def parse_args():
    parser = argparse.ArgumentParser(description="文本分类原型:训练/验证/推理")
    subparsers = parser.add_subparsers(dest="cmd", required=True)

    # train
    p_train = subparsers.add_parser("train", help="训练模型")
    p_train.add_argument("--train_csv", type=str, required=True)
    p_train.add_argument("--val_csv", type=str, default=None)
    p_train.add_argument("--out_dir", type=str, required=True)
    p_train.add_argument("--config", type=str, default=None)

    # validate
    p_val = subparsers.add_parser("validate", help="评估模型")
    p_val.add_argument("--model_dir", type=str, required=True)
    p_val.add_argument("--eval_csv", type=str, required=True)
    p_val.add_argument("--out_path", type=str, default=None)

    # infer
    p_infer = subparsers.add_parser("infer", help="模型推理")
    p_infer.add_argument("--model_dir", type=str, required=True)
    p_infer.add_argument("--text", type=str, default=None)
    p_infer.add_argument("--input_file", type=str, default=None)
    p_infer.add_argument("--output_path", type=str, default=None)
    p_infer.add_argument("--no_prob", action="store_true")

    # demo
    p_demo = subparsers.add_parser("demo", help="生成示例数据集")
    p_demo.add_argument("--out_csv", type=str, required=True)

    return parser.parse_args()


def load_or_default_config(cfg_path: Optional[str]) -> ModelConfig:
    if cfg_path is None:
        return ModelConfig()
    with open(cfg_path, "r", encoding="utf-8") as f:
        raw = json.load(f)
    # 兼容缺省字段
    fields = {k: raw.get(k, getattr(ModelConfig, k)) for k in asdict(ModelConfig())}
    return ModelConfig(**fields)


def main():
    args = parse_args()
    if args.cmd == "train":
        cfg = load_or_default_config(args.config)
        train_model(args.train_csv, args.val_csv, args.out_dir, cfg)
    elif args.cmd == "validate":
        validate_model(args.model_dir, args.eval_csv, args.out_path)
    elif args.cmd == "infer":
        infer(args.model_dir, args.text, args.input_file, args.output_path, show_prob=not args.no_prob)
    elif args.cmd == "demo":
        build_demo_dataset(args.out_csv)
    else:
        raise ValueError("Unknown command")


if __name__ == "__main__":
    main()

代码说明

  • 功能描述:

    • 提供文本分类原型,覆盖数据清洗(TextCleaner)、特征工程(TF-IDF/Hashing)、分类器(逻辑回归/线性SVM/SGD)。
    • 训练脚本支持小样本(few-shot)采样、验证集拆分、交叉验证、指标保存。
    • 验证脚本加载已训练模型,对指定数据集输出完整评估指标。
    • 推理脚本支持单文本或批量文件输入,输出预测标签与概率(分类器支持时)。
    • 提供Demo数据生成,无需外网,可快速试跑。
    • 可复现实验:统一随机种子、保存配置与指标。
  • 参数说明:

    • 训练(train):
      • --train_csv:训练数据CSV路径(需含text、label两列)
      • --val_csv:验证数据CSV路径(可选;未提供时自动按test_size拆分)
      • --out_dir:输出目录(保存model.joblib、config.json、val_metrics.json等)
      • --config:模型配置JSON路径(可选;不提供则使用默认配置)
    • 验证(validate):
      • --model_dir:已训练模型目录
      • --eval_csv:评估数据CSV路径
      • --out_path:评估指标保存路径(可选)
    • 推理(infer):
      • --model_dir:已训练模型目录
      • --text:单条文本(可选)
      • --input_file:文本文件路径(每行一条)(可选)
      • --output_path:推理结果保存路径(可选)
      • --no_prob:关闭概率输出(默认开启;若分类器不支持概率则自动不输出)
    • 配置文件(config.json结构,对应ModelConfig):
      • seed:随机种子(默认42)
      • vectorizer:特征类型(tfidf/hashing)
      • ngram_max:最大n-gram(默认2)
      • min_df:TF-IDF的最小文档频次(默认2)
      • max_features:最大特征数(默认50000;hashing未提供时为2^20)
      • tfidf_sublinear/use_idf:TF-IDF选项
      • stop_words:停用词(可设'english')
      • classifier:分类器(logreg/linear_svm/sgd)
      • class_weight:类别权重(None/balanced;小样本/不平衡推荐balanced)
      • C:Logistic回归正则强度(默认2.0)
      • max_iter:最大迭代(默认200)
      • n_jobs:并行(默认-1)
      • few_shot_k:每类样本数(<=0不启用)
      • test_size:自动验证拆分比例(默认0.2)
      • cv_folds:交叉验证折数(>1时启用)
      • cleaner_replace_digits_token:数字替换token(默认"")
  • 返回值:

    • 训练:在控制台打印完成信息,并在out_dir写入模型、配置、验证/交叉验证指标JSON。
    • 验证:打印并返回包含accuracy、f1_macro、f1_micro、classification_report、confusion_matrix的字典。
    • 推理:打印并返回每条文本的预测结果,若支持概率则包含各类别概率。

使用示例

# 1) 创建并查看示例数据
python text_classification_prototype.py demo --out_csv data_demo.csv
head -n 5 data_demo.csv

# 2) 训练(默认配置,自动拆分验证集)
python text_classification_prototype.py train \
  --train_csv data_demo.csv \
  --out_dir outputs/run1

# 3) 使用自定义配置(示例config.json)
# {
#   "seed": 123,
#   "vectorizer": "tfidf",
#   "ngram_max": 2,
#   "min_df": 1,
#   "max_features": 20000,
#   "classifier": "logreg",
#   "class_weight": "balanced",
#   "few_shot_k": 3,
#   "cv_folds": 5
# }
python text_classification_prototype.py train \
  --train_csv data_demo.csv \
  --out_dir outputs/run2 \
  --config config.json

# 4) 评估(对同一数据或独立测试集)
python text_classification_prototype.py validate \
  --model_dir outputs/run1 \
  --eval_csv data_demo.csv \
  --out_path outputs/run1/eval_on_demo.json

# 5) 推理(单条文本)
python text_classification_prototype.py infer \
  --model_dir outputs/run1 \
  --text "球队赢得了冠军,球迷欢呼雀跃"

# 6) 推理(文件,每行一条)
echo -e "新款芯片提升了计算性能\n投资组合应控制风险" > inputs.txt
python text_classification_prototype.py infer \
  --model_dir outputs/run1 \
  --input_file inputs.txt \
  --output_path outputs/run1/preds.json

优化建议

  • 性能考虑:
    • 大语料与高维特征会导致向量化与训练耗时/内存上升。建议:
      • 限制ngram_max与max_features,合理设置min_df以过滤低频噪声。
      • 使用HashingVectorizer替代TF-IDF可降低内存占用,训练更快(但不可逆特征,无法输出词表)。
      • 对极大数据采用SGDClassifier(增量学习),并使用分块读取与partial_fit。
      • 开启并行(n_jobs=-1)仅在支持时生效;注意并行带来的内存压力。
      • 缓存Pipeline中间结果(joblib.Memory)在多次调参时减少重复计算。
    • 小样本场景:
      • class_weight="balanced"能缓解类别不平衡。
      • 适当增大C(LogReg)或调整正则,避免欠拟合。
      • 使用字符级n-gram在跨语言/混合文本上更稳健,提升少样本鲁棒性。
  • 扩展性:
    • 更强表现:引入预训练模型(如Transformer/BERT),可用轻量蒸馏模型提升精度并控制资源。
    • 高级特征:加入词形还原/自定义停用词、领域词典、短语抽取,提高TF-IDF信噪比。
    • 调参自动化:使用GridSearchCV/Optuna进行超参搜索,保存最佳模型与日志。
    • 多语言支持:对中文引入分词器(如Jieba)或使用字/子词级特征;对混杂文本可采用联合特征。
    • 部署:导出为ONNX或封装为REST服务,增加批量/流式接口与并发控制。
  • 注意事项:
    • 数据格式需包含text与label两列;标签以字符串保存更通用。
    • 概率输出仅在分类器支持predict_proba时可用(LogReg/SGD log_loss);LinearSVC无概率。
    • 停用词设为'english'仅对英文有用;中文建议关闭或使用自定义停用词表。
    • 配置与随机种子会保存到输出目录,确保实验可复现。
    • 大规模训练前评估内存占用(max_features与ngram_max影响显著);必要时改用HashingVectorizer与分批训练。

示例详情

适用用户

企业后端开发工程师

快速搭建服务模块与数据操作;审查旧代码并给出可靠的优化重构建议;从需求到可运行示例一气呵成,缩短交付周期。

前端与跨端开发者

一键生成组件示例、状态管理与路由逻辑;自动优化现有实现的性能与可读性;减少样式与交互问题,提升体验。

数据科学与算法工程师

快速产出算法原型与数据处理流程;获得针对瓶颈的优化思路与代码解释;更高效地迭代模型与工具组件。

解决的问题

把你的自然语言需求快速转化为可运行、可维护、可扩展的高质量代码,一次提示即可完成“需求拆解—方案设计—代码实现—性能审查—使用示例—优化建议”的闭环交付;在多语言、多框架下稳定产出,自动识别潜在问题并给出改进路径,显著缩短从想法到上线的周期,降低返工与沟通成本,帮助个人与团队形成统一的工程标准与最佳实践。

特征总结

依据目标功能与语言,轻松生成可运行示例代码与注释,快速搭建模块与原型。
自动拆解任务步骤,理清输入输出与边界条件,保障代码逻辑严谨、结构清晰。
一键审查现有代码,识别潜在缺陷与冗余,给出可落地的优化建议与重构方案。
支持多语言与主流框架,跨栈场景一致体验,方便团队协作与技术迁移。
内置性能分析思路,定位瓶颈环节,提供效率优化路径与注意事项,提升运行表现。
模板化输出格式,自动生成使用示例、参数说明与注意点,上手与复用更顺畅。
面向学习与培训,附带代码解释与设计意图,帮助新人快速理解并独立扩展。
兼顾日常维护与项目重构,快速替换旧实现、提升可读性与可维护性,减少返工。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥10.00元 ¥20.00元
立减 50%
还剩 00:00:00
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 601 tokens
- 3 个可调节参数
{ 编程任务 } { 目标功能 } { 编程语言 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
限时免费

不要错过!

免费获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59