用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/zhangloveyan/backend-skill --skill proj-common命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | proj-common |
| description | 查看公共类规范和使用方式。包括R响应类、ErrorCode错误码、异常处理、事务、缓存、并发控制、日志等。 |
| 类 | 包路径 | 用途 |
|---|---|---|
| R | common.core | 统一响应类 |
| ErrorCode | common.core | 错误码枚举 |
| PageResult | common.core | 分页结果 |
| BusinessException | common.exception | 业务异常 |
| GlobalExceptionHandler | common.exception | 全局异常处理 |
| Constants | common.constant | 全局常量 |
| RedisKeys | common.constant | Redis Key 常量 |
| RedisUtils | common.utils | Redis 工具类 |
| AssertUtils | common.utils | 断言工具类 |
| LoginUserHolder | common.security | 登录用户上下文 |
return R.success(); // 成功,无数据
return R.success(data); // 成功,有数据
return R.fail(ErrorCode.NOT_FOUND); // 失败
throw new BusinessException(ErrorCode.NOT_FOUND);
throw new BusinessException(ErrorCode.PARAM_ERROR, "用户名不能为空");
AssertUtils.notNull(user, ErrorCode.NOT_FOUND);
AssertUtils.isTrue(condition, ErrorCode.PARAM_ERROR, "条件不满足");
Long userId = LoginUserHolder.getUserId();
LoginUser loginUser = LoginUserHolder.get();
| 场景 | 是否需要事务 |
|---|---|
| 单表写操作 | 否(MyBatis-Plus 自动处理) |
| 多表写操作 | 是 |
| 先查后改 | 是(需保证一致性时) |
| 纯查询 | 否 |
// 写操作
@Transactional(rollbackFor = Exception.class)
public void bindUserRole(Long userId, List<Long> roleIds) {
// 多表操作
}
// 只读事务(查询多表需一致性快照)
@Transactional(readOnly = true)
public UserDetailResponse getDetail(Long id) {
// 多表查询
}
@Transactional{项目}:{模块}:{业务}:{标识}
示例:toy:user:info:123、toy:order:detail:456
| 数据类型 | 过期时间 | 常量 |
|---|---|---|
| 热点数据 | 1小时 | Constants.CACHE_1_HOUR |
| 普通数据 | 24小时 | Constants.CACHE_1_DAY |
| 配置数据 | 7天 | Constants.CACHE_7_DAY |
| 验证码 | 5分钟 | Constants.CACHE_5_MIN |
// 查询:先缓存后数据库
public User getById(Long id) {
String key = RedisKeys.USER_INFO + id;
User user = redisUtils.get(key, User.class);
if (user == null) {
user = userMapper.selectById(id);
if (user != null) {
redisUtils.set(key, user, Constants.CACHE_1_HOUR);
}
}
return user;
}
// 更新:先数据库后删缓存
public void update(User user) {
userMapper.updateById(user);
redisUtils.delete(RedisKeys.USER_INFO + user.getId());
}
| 问题 | 解决方案 |
|---|---|
| 缓存穿透 | 空值缓存(短过期)或布隆过滤器 |
| 缓存击穿 | 分布式锁或永不过期+异步更新 |
| 缓存雪崩 | 过期时间加随机值 |
// Entity 添加版本字段
@Version
private Integer version;
// 更新时自动检查版本
userMapper.updateById(user); // WHERE version = ?
适用:冲突概率低、读多写少
@Select("SELECT * FROM user WHERE id = #{id} FOR UPDATE")
User selectForUpdate(Long id);
适用:冲突概率高、必须成功
String lockKey = RedisKeys.LOCK_ORDER + orderId;
boolean locked = redisUtils.tryLock(lockKey, 10, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException(ErrorCode.SYSTEM_BUSY);
}
try {
// 业务逻辑
} finally {
redisUtils.unlock(lockKey);
}
| 异常类型 | 处理方式 |
|---|---|
| BusinessException | 返回错误码和消息 |
| MethodArgumentNotValidException | 返回参数校验错误 |
| 其他异常 | 返回系统错误,记录日志 |
// 业务校验失败
if (user == null) {
throw new BusinessException(ErrorCode.USER_NOT_FOUND);
}
// 带自定义消息
throw new BusinessException(ErrorCode.PARAM_ERROR, "手机号格式不正确");
// 使用断言(推荐)
AssertUtils.notNull(user, ErrorCode.USER_NOT_FOUND);
log.info("[类名.方法名] 操作描述, 参数={}", value);
| 级别 | 场景 |
|---|---|
| ERROR | 系统错误、影响业务的异常 |
| WARN | 潜在问题、可恢复异常 |
| INFO | 关键业务流程、重要操作 |
| DEBUG | 调试信息 |
System.out.println@Async("taskExecutor")
public void sendNotification(Long userId, String message) {
// 异步执行
}