热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词专为程序开发场景设计,能够根据用户需求智能生成高质量代码,并提供详细的代码解释和优化建议。通过任务分步法和链式思维分析,确保代码逻辑严谨、结构清晰。支持多种编程语言和开发框架,涵盖函数实现、类设计、算法优化等核心开发需求。具备代码审查和性能分析能力,能够识别潜在问题并提供改进方案,帮助开发者提升编码效率和代码质量,适用于日常开发、项目重构、技术学习等多种办公场景。
# 文件: 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
}
功能描述:
参数说明:
返回值:
# 启动服务
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"
性能考虑:
扩展性:
注意事项:
DAO层重构建议:
事务与连接池优化建议(总结):
// ===== 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; }
功能描述:
参数说明:
返回值:
// 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>
);
性能考虑:
扩展性:
注意事项:
# 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()
功能描述:
参数说明:
返回值:
# 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
快速搭建服务模块与数据操作;审查旧代码并给出可靠的优化重构建议;从需求到可运行示例一气呵成,缩短交付周期。
一键生成组件示例、状态管理与路由逻辑;自动优化现有实现的性能与可读性;减少样式与交互问题,提升体验。
快速产出算法原型与数据处理流程;获得针对瓶颈的优化思路与代码解释;更高效地迭代模型与工具组件。
把你的自然语言需求快速转化为可运行、可维护、可扩展的高质量代码,一次提示即可完成“需求拆解—方案设计—代码实现—性能审查—使用示例—优化建议”的闭环交付;在多语言、多框架下稳定产出,自动识别潜在问题并给出改进路径,显著缩短从想法到上线的周期,降低返工与沟通成本,帮助个人与团队形成统一的工程标准与最佳实践。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
免费获取高级提示词-优惠即将到期