通用服务接口
core 模块定义了一系列通用服务接口,为系统各模块提供统一的数据访问和业务处理规范。这些接口采用依赖注入的方式,实现了模块间的松耦合设计。
设计理念
接口优先原则
所有业务逻辑都通过接口定义,具体实现由各业务模块提供,确保了:
- 模块解耦:core 模块不依赖具体实现
- 灵活扩展:可以轻松替换不同的实现方式
- 统一规范:所有模块遵循相同的接口契约
默认方法支持
接口中大量使用 Java 8+ 的默认方法特性,提供:
- 向后兼容:新增方法不会破坏现有实现
- 便捷重载:提供多种参数形式的便捷方法
- 降级处理:为可选功能提供默认实现
核心服务接口
用户服务 (UserService)
用户相关的数据查询和管理服务。
基础查询方法
java
/**
* 通过用户ID查询用户账户
*/
String getUserNameById(Long userId);
/**
* 通过用户ID查询用户昵称
*/
String getNickNameById(Long userId);
/**
* 通过用户ID查询用户头像
*/
String getAvatarById(Long userId);批量查询方法
java
/**
* 通过用户ID列表查询用户昵称列表
* @param userIds 用户ID,多个用逗号隔开
* @return 用户昵称,多个用逗号隔开
*/
String getNickNamesByIds(String userIds);
/**
* 通过用户ID查询用户列表
*/
List<UserDTO> listUsersByIds(List<Long> userIds);关联查询方法
java
/**
* 通过角色ID查询用户
*/
List<UserDTO> listUsersByRoleIds(List<Long> roleIds);
/**
* 通过部门ID查询用户
*/
List<UserDTO> listUsersByDeptIds(List<Long> deptIds);
/**
* 通过岗位ID查询用户
*/
List<UserDTO> listUsersByPostIds(List<Long> postIds);映射关系方法
java
/**
* 根据用户ID列表查询用户名称映射关系
* @return Map,key为用户ID,value为对应的用户名称
*/
Map<Long, String> mapUserNames(List<Long> userIds);
/**
* 根据角色ID列表查询角色名称映射关系
*/
Map<Long, String> mapRoleNames(List<Long> roleIds);使用示例
java
@Service
public class BusinessService {
@Autowired
private UserService userService;
public void processUserData() {
// 单个查询
String userName = userService.getUserNameById(1L);
// 批量查询
List<Long> userIds = Arrays.asList(1L, 2L, 3L);
List<UserDTO> users = userService.listUsersByIds(userIds);
// 映射关系
Map<Long, String> userNameMap = userService.mapUserNames(userIds);
// 关联查询
List<UserDTO> deptUsers = userService.listUsersByDeptIds(Arrays.asList(1L, 2L));
}
}字典服务 (DictService)
系统字典数据的查询和转换服务。
核心转换方法
java
/**
* 根据字典类型和字典值获取字典标签
* @param dictType 字典类型,如 "sys_user_sex"
* @param dictValue 字典值,如 "1"
* @return 字典标签,如 "男"
*/
String getDictLabel(String dictType, String dictValue);
/**
* 根据字典类型和字典标签获取字典值(反向查询)
* @param dictType 字典类型
* @param dictLabel 字典标签,如 "男"
* @return 字典值,如 "1"
*/
String getDictValue(String dictType, String dictLabel);批量转换方法
java
/**
* 支持多值转换,使用指定分隔符
* @param dictType 字典类型
* @param dictValue 字典值,支持多个值用分隔符分割,如 "1,2"
* @param separator 分隔符,默认为逗号
* @return 字典标签,如 "男,女"
*/
String getDictLabel(String dictType, String dictValue, String separator);数据获取方法
java
/**
* 获取字典下所有的字典值与标签映射
* @param dictType 字典类型
* @return Map,key为字典值,value为字典标签
*/
Map<String, String> getAllDictByDictType(String dictType);
/**
* 根据字典类型查询字典数据列表
*/
List<DictDataDTO> getDictData(String dictType);
/**
* 根据字典类型查询详细信息
*/
DictTypeDTO getDictType(String dictType);使用示例
java
@Service
public class UserBusinessService {
@Autowired
private DictService dictService;
public void processUserData() {
// 单值转换
String genderLabel = dictService.getDictLabel("sys_user_sex", "1");
// 结果: "男"
// 多值转换
String statusLabels = dictService.getDictLabel("sys_user_status", "1,2", ",");
// 结果: "正常,停用"
// 反向查询
String genderValue = dictService.getDictValue("sys_user_sex", "男");
// 结果: "1"
// 获取所有字典项
Map<String, String> genderDict = dictService.getAllDictByDictType("sys_user_sex");
// 结果: {"0": "女", "1": "男", "2": "未知"}
}
}常用字典类型
java
// 系统基础字典
sys_user_sex // 用户性别
sys_enable_status // 启用状态
sys_user_status // 用户状态
sys_boolean_flag // 逻辑标志
// 业务字典
sys_payment_method // 支付方式
sys_order_status // 订单状态
sys_message_type // 消息类型
sys_notice_type // 通知类型部门服务 (DeptService)
部门组织架构的查询服务。
java
public interface DeptService {
/**
* 通过部门ID查询部门名称
* @param deptIds 部门ID串,逗号分隔
* @return 部门名称串,逗号分隔
*/
String getDeptNameByIds(String deptIds);
/**
* 根据部门ID查询部门负责人
* @param deptId 部门ID
* @return 负责人用户ID
*/
Long getDeptLeaderById(Long deptId);
/**
* 查询所有正常状态的部门
* @return 部门列表
*/
List<DeptDTO> listNormalDepts();
}配置服务 (ConfigService)
系统参数配置的查询服务。
基础配置查询
java
/**
* 根据参数key获取参数值
* @param configKey 参数key,如 "sys.account.registerUser"
* @return 参数值
*/
String getConfigValue(String configKey);默认值支持
java
/**
* 根据参数key获取参数值,支持默认值
* @param configKey 参数key
* @param defaultValue 默认值
* @return 参数值,如果为空则返回默认值
*/
default String getConfigValue(String configKey, String defaultValue) {
String value = getConfigValue(configKey);
return StringUtils.isNotBlank(value) ? value : defaultValue;
}类型转换方法
java
/**
* 根据参数key获取布尔参数值
* @param configKey 参数key
* @return 布尔参数值
*/
default boolean getBooleanValue(String configKey) {
return BooleanUtil.toBoolean(getConfigValue(configKey));
}
/**
* 根据参数key获取布尔参数值,支持默认值
*/
default boolean getBooleanValue(String configKey, boolean defaultValue) {
String value = getConfigValue(configKey);
return StringUtils.isNotBlank(value) ? BooleanUtil.toBoolean(value) : defaultValue;
}使用示例
java
@Service
public class SystemConfigService {
@Autowired
private ConfigService configService;
public void checkSystemConfig() {
// 基础查询
String siteName = configService.getConfigValue("sys.index.skinName");
// 带默认值查询
String uploadPath = configService.getConfigValue("sys.uploadPath", "/upload");
// 布尔值查询
boolean registerEnabled = configService.getBooleanValue("sys.account.registerUser");
// 布尔值带默认值查询
boolean captchaEnabled = configService.getBooleanValue("sys.account.captchaEnabled", true);
}
}常用配置项
java
// 系统基础配置
sys.index.skinName // 系统皮肤
sys.account.registerUser // 是否允许注册
sys.account.captchaEnabled // 是否启用验证码
sys.uploadPath // 上传路径
// 安全配置
sys.user.initPassword // 用户初始密码
sys.account.maxRetryCount // 最大重试次数
sys.account.lockTime // 锁定时间(分钟)权限服务 (PermissionService)
用户权限信息的查询服务。
java
public interface PermissionService {
/**
* 获取角色数据权限列表
* @param userId 用户id
* @return 角色权限信息
*/
Set<String> listRolePermissions(Long userId);
/**
* 获取菜单数据权限列表
* @param userId 用户id
* @return 菜单权限信息
*/
Set<String> listMenuPermissions(Long userId);
}文件存储服务 (OssService)
对象存储服务的查询接口。
java
public interface OssService {
/**
* 通过ossId查询对应的url
* @param ossIds ossId串,逗号分隔
* @return url串,逗号分隔
*/
String getUrlsByIds(String ossIds);
/**
* 通过ossId查询列表
* @param ossIds ossId串,逗号分隔
* @return OSS对象列表
*/
List<OssDTO> listOssByIds(String ossIds);
/**
* 根据目录id查询目录名称
* @param directoryId 目录id
* @return 目录名称
*/
String getDirectoryNameById(Long directoryId);
}支付与平台服务
支付服务 (PaymentService)
支付配置的查询和管理服务。
核心查询方法
java
/**
* 根据商户号获取支付配置
* @param mchId 商户号
* @return 支付配置,如果不存在返回null
*/
PaymentDTO getByMchId(String mchId);
/**
* 根据支付配置ID获取支付配置
* @param paymentId 支付配置ID
* @return 支付配置,如果不存在返回null
*/
PaymentDTO getById(Long paymentId);分类查询方法
java
/**
* 根据支付类型获取支付配置列表
* @param type 支付类型(如:WECHAT, ALIPAY等)
* @param tenantId 租户id
* @return 支付配置列表
*/
List<PaymentDTO> listPaymentByType(String type, String tenantId);验证方法
java
/**
* 检查商户号是否存在且有效
* @param mchId 商户号
* @return true-存在且有效,false-不存在或无效
*/
boolean existsValidMchId(String mchId);
/**
* 获取支付配置总数
* @return 支付配置总数
*/
long countPayments();使用示例
java
@Service
public class PaymentBusinessService {
@Autowired
private PaymentService paymentService;
public void processPayment(String mchId) {
// 根据商户号查询配置
PaymentDTO payment = paymentService.getByMchId(mchId);
if (payment != null && payment.isEnabled()) {
// 处理支付逻辑
if (payment.isWechatPayment()) {
// 微信支付处理
} else if (payment.isAlipayPayment()) {
// 支付宝支付处理
}
}
// 查询租户下的微信支付配置
List<PaymentDTO> wechatPayments = paymentService.listPaymentByType("WECHAT", "000000");
// 验证商户号
boolean isValid = paymentService.existsValidMchId(mchId);
}
}支付类型说明
java
// 主要支付类型
WECHAT // 微信支付
ALIPAY // 支付宝支付
UNIONPAY // 银联支付
BALANCE // 余额支付平台服务 (PlatformService)
多平台应用配置的管理服务。
java
public interface PlatformService {
/**
* 根据平台类型获取平台配置列表
* @param type 平台类型(如:mp-weixin, mp-alipay)
* @param tenantId 租户id
* @return 平台配置列表
*/
default List<PlatformDTO> listPlatformsByType(String type, String tenantId) {
return Collections.emptyList();
}
/**
* 根据appid和平台类型获取平台配置
* @param appid 应用ID
* @param type 平台类型
* @return 平台配置
*/
default PlatformDTO getPlatformByAppidAndType(String appid, String type) {
return null;
}
/**
* 根据appid获取平台配置(跨租户查询)
* @param appid 应用ID
* @param tenantId 租户id
* @return 平台配置
*/
default PlatformDTO getPlatformByAppid(String appid, String tenantId) {
return null;
}
}租户服务 (TenantService)
多租户环境下的租户信息服务。
java
public interface TenantService {
/**
* 获取当前租户id
* @return 租户id
*/
String getTenantId();
/**
* 根据请求获取租户ID
* 专门处理从请求中提取租户信息的逻辑:域名识别 + 请求头获取
* @return 租户ID,如果获取不到返回null
*/
String getTenantIdByRequest();
}其他服务接口
角色服务 (RoleService)
java
public interface RoleService {
// 当前为空接口,预留扩展
}岗位服务 (PostService)
java
public interface PostService {
// 当前为空接口,预留扩展
}OSS配置服务 (OssConfigService)
java
public interface OssConfigService {
/**
* 初始化OSS配置
*/
void initOssConfig();
}使用指南
1. 依赖注入使用
java
@Service
@RequiredArgsConstructor
public class BusinessService {
private final UserService userService;
private final DictService dictService;
private final ConfigService configService;
// 业务逻辑实现
}2. 条件注入
java
@Service
@RequiredArgsConstructor
@ConditionalOnBean(PaymentService.class)
public class PaymentBusinessService {
private final PaymentService paymentService;
// 只有当PaymentService存在时才创建此服务
}3. 默认实现
如果某个服务接口没有具体实现,可以提供默认的空实现:
java
@Service
@ConditionalOnMissingBean(PaymentService.class)
public class DefaultPaymentService implements PaymentService {
@Override
public PaymentDTO getByMchId(String mchId) {
return null; // 默认返回空
}
// 其他方法的默认实现
}设计优势
- 松耦合:各模块只依赖接口,不依赖具体实现
- 可测试:便于进行单元测试和Mock
- 可扩展:新增功能只需扩展接口,不影响现有代码
- 统一规范:所有模块遵循相同的接口设计原则
- 向后兼容:使用默认方法确保接口演进的兼容性
服务层设计模式
门面模式 (Facade Pattern)
服务层采用门面模式,为复杂的业务逻辑提供统一的访问入口:
java
/**
* 订单门面服务
* 整合多个子服务,提供统一的订单处理入口
*/
@Service
@RequiredArgsConstructor
public class OrderFacadeService {
private final OrderService orderService;
private final PaymentService paymentService;
private final InventoryService inventoryService;
private final UserService userService;
private final DictService dictService;
/**
* 创建订单(门面方法)
* 整合订单创建、库存扣减、用户通知等多个操作
*/
@Transactional(rollbackFor = Exception.class)
public OrderResult createOrder(OrderCreateDTO createDTO) {
// 1. 验证用户
UserDTO user = userService.listUsersByIds(
Collections.singletonList(createDTO.getUserId())
).stream().findFirst().orElseThrow(() ->
new ServiceException("用户不存在")
);
// 2. 检查库存
boolean stockAvailable = inventoryService.checkStock(
createDTO.getProductId(),
createDTO.getQuantity()
);
if (!stockAvailable) {
throw new ServiceException("库存不足");
}
// 3. 扣减库存
inventoryService.deductStock(
createDTO.getProductId(),
createDTO.getQuantity()
);
// 4. 创建订单
Order order = orderService.create(createDTO);
// 5. 获取状态标签
String statusLabel = dictService.getDictLabel(
"sys_order_status",
order.getStatus()
);
return OrderResult.builder()
.orderId(order.getId())
.orderNo(order.getOrderNo())
.statusLabel(statusLabel)
.userName(user.getNickName())
.build();
}
}策略模式 (Strategy Pattern)
针对不同业务场景,服务层支持策略模式实现:
java
/**
* 支付策略接口
*/
public interface PayStrategy {
/**
* 获取支付类型
*/
String getPayType();
/**
* 执行支付
*/
PayResult pay(PayRequest request);
/**
* 查询支付结果
*/
PayQueryResult query(String orderNo);
/**
* 退款
*/
RefundResult refund(RefundRequest request);
}
/**
* 微信支付策略实现
*/
@Service
public class WechatPayStrategy implements PayStrategy {
@Override
public String getPayType() {
return "WECHAT";
}
@Override
public PayResult pay(PayRequest request) {
// 微信支付具体实现
return new PayResult();
}
@Override
public PayQueryResult query(String orderNo) {
// 查询微信支付结果
return new PayQueryResult();
}
@Override
public RefundResult refund(RefundRequest request) {
// 微信退款实现
return new RefundResult();
}
}
/**
* 支付宝支付策略实现
*/
@Service
public class AlipayStrategy implements PayStrategy {
@Override
public String getPayType() {
return "ALIPAY";
}
@Override
public PayResult pay(PayRequest request) {
// 支付宝支付具体实现
return new PayResult();
}
@Override
public PayQueryResult query(String orderNo) {
// 查询支付宝支付结果
return new PayQueryResult();
}
@Override
public RefundResult refund(RefundRequest request) {
// 支付宝退款实现
return new RefundResult();
}
}
/**
* 支付策略上下文
*/
@Component
public class PayStrategyContext {
private final Map<String, PayStrategy> strategyMap;
public PayStrategyContext(List<PayStrategy> strategies) {
this.strategyMap = strategies.stream()
.collect(Collectors.toMap(
PayStrategy::getPayType,
Function.identity()
));
}
/**
* 获取支付策略
*/
public PayStrategy getStrategy(String payType) {
PayStrategy strategy = strategyMap.get(payType);
if (strategy == null) {
throw new ServiceException("不支持的支付类型: " + payType);
}
return strategy;
}
/**
* 执行支付
*/
public PayResult pay(String payType, PayRequest request) {
return getStrategy(payType).pay(request);
}
}模板方法模式 (Template Method Pattern)
服务层基类定义通用处理流程:
java
/**
* 抽象业务服务基类
* 定义通用的业务处理模板
*/
public abstract class AbstractBusinessService<T, R> {
/**
* 执行业务处理(模板方法)
*/
public final R execute(T request) {
// 1. 前置校验
validate(request);
// 2. 前置处理
beforeExecute(request);
// 3. 核心业务逻辑
R result = doExecute(request);
// 4. 后置处理
afterExecute(request, result);
// 5. 返回结果
return result;
}
/**
* 参数校验(可重写)
*/
protected void validate(T request) {
// 默认无校验
}
/**
* 前置处理(可重写)
*/
protected void beforeExecute(T request) {
// 默认无处理
}
/**
* 核心业务逻辑(子类必须实现)
*/
protected abstract R doExecute(T request);
/**
* 后置处理(可重写)
*/
protected void afterExecute(T request, R result) {
// 默认无处理
}
}
/**
* 订单创建服务(模板方法实现)
*/
@Service
@RequiredArgsConstructor
public class OrderCreateService extends AbstractBusinessService<OrderCreateDTO, Order> {
private final OrderMapper orderMapper;
private final UserService userService;
@Override
protected void validate(OrderCreateDTO request) {
if (request.getProductId() == null) {
throw new ServiceException("商品ID不能为空");
}
if (request.getQuantity() <= 0) {
throw new ServiceException("购买数量必须大于0");
}
}
@Override
protected void beforeExecute(OrderCreateDTO request) {
// 设置用户信息
String userName = userService.getUserNameById(request.getUserId());
request.setUserName(userName);
}
@Override
protected Order doExecute(OrderCreateDTO request) {
Order order = new Order();
BeanUtils.copyProperties(request, order);
order.setOrderNo(generateOrderNo());
order.setStatus("0");
orderMapper.insert(order);
return order;
}
@Override
protected void afterExecute(OrderCreateDTO request, Order result) {
// 发送订单创建通知
log.info("订单创建成功: {}", result.getOrderNo());
}
private String generateOrderNo() {
return "ORD" + System.currentTimeMillis();
}
}服务组合与编排
服务组合器
java
/**
* 服务组合器
* 将多个服务调用组合成一个完整的业务流程
*/
@Component
@RequiredArgsConstructor
public class ServiceComposer {
private final UserService userService;
private final DictService dictService;
private final OssService ossService;
/**
* 组合用户详情信息
* 将用户基础信息、字典翻译、头像URL等组合在一起
*/
public UserDetailVO composeUserDetail(Long userId) {
// 并行获取各项数据
CompletableFuture<List<UserDTO>> userFuture = CompletableFuture.supplyAsync(() ->
userService.listUsersByIds(Collections.singletonList(userId))
);
UserDTO user = userFuture.join().stream()
.findFirst()
.orElseThrow(() -> new ServiceException("用户不存在"));
// 组合详情
UserDetailVO detail = new UserDetailVO();
detail.setUserId(user.getUserId());
detail.setUserName(user.getUserName());
detail.setNickName(user.getNickName());
// 字典翻译
detail.setSexLabel(dictService.getDictLabel("sys_user_sex", user.getSex()));
detail.setStatusLabel(dictService.getDictLabel("sys_user_status", user.getStatus()));
// 获取头像URL
if (StringUtils.isNotBlank(user.getAvatar())) {
detail.setAvatarUrl(ossService.getUrlsByIds(user.getAvatar()));
}
return detail;
}
/**
* 批量组合用户信息
*/
public List<UserDetailVO> composeUserDetails(List<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Collections.emptyList();
}
// 批量查询用户
List<UserDTO> users = userService.listUsersByIds(userIds);
// 预加载字典数据
Map<String, String> sexDict = dictService.getAllDictByDictType("sys_user_sex");
Map<String, String> statusDict = dictService.getAllDictByDictType("sys_user_status");
// 批量获取头像URL
String ossIds = users.stream()
.map(UserDTO::getAvatar)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining(","));
Map<String, String> ossUrlMap = new HashMap<>();
if (StringUtils.isNotBlank(ossIds)) {
List<OssDTO> ossList = ossService.listOssByIds(ossIds);
ossUrlMap = ossList.stream()
.collect(Collectors.toMap(
o -> String.valueOf(o.getOssId()),
OssDTO::getUrl
));
}
// 组合结果
Map<String, String> finalOssUrlMap = ossUrlMap;
return users.stream().map(user -> {
UserDetailVO detail = new UserDetailVO();
detail.setUserId(user.getUserId());
detail.setUserName(user.getUserName());
detail.setNickName(user.getNickName());
detail.setSexLabel(sexDict.get(user.getSex()));
detail.setStatusLabel(statusDict.get(user.getStatus()));
detail.setAvatarUrl(finalOssUrlMap.get(user.getAvatar()));
return detail;
}).collect(Collectors.toList());
}
}流程编排器
java
/**
* 业务流程编排器
* 支持复杂业务流程的编排执行
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class BusinessFlowOrchestrator {
private final ApplicationEventPublisher eventPublisher;
/**
* 编排执行业务流程
*/
public <T> FlowResult<T> orchestrate(BusinessFlow<T> flow) {
FlowContext context = new FlowContext();
context.setFlowId(UUID.randomUUID().toString());
context.setStartTime(LocalDateTime.now());
try {
// 执行前置步骤
for (FlowStep<T> step : flow.getPreSteps()) {
executeStep(step, context);
}
// 执行主要步骤
T result = null;
for (FlowStep<T> step : flow.getMainSteps()) {
result = executeStep(step, context);
}
// 执行后置步骤
for (FlowStep<T> step : flow.getPostSteps()) {
executeStep(step, context);
}
context.setEndTime(LocalDateTime.now());
context.setSuccess(true);
// 发布流程完成事件
eventPublisher.publishEvent(new FlowCompletedEvent(context));
return FlowResult.success(result, context);
} catch (Exception e) {
context.setEndTime(LocalDateTime.now());
context.setSuccess(false);
context.setErrorMessage(e.getMessage());
// 执行补偿步骤
for (FlowStep<T> step : flow.getCompensateSteps()) {
try {
executeStep(step, context);
} catch (Exception ex) {
log.error("补偿步骤执行失败", ex);
}
}
// 发布流程失败事件
eventPublisher.publishEvent(new FlowFailedEvent(context, e));
return FlowResult.failure(e.getMessage(), context);
}
}
private <T> T executeStep(FlowStep<T> step, FlowContext context) {
log.info("执行步骤: {}", step.getName());
StepResult<T> result = step.execute(context);
context.addStepResult(step.getName(), result);
if (!result.isSuccess()) {
throw new FlowException("步骤执行失败: " + step.getName());
}
return result.getData();
}
}
/**
* 业务流程定义
*/
@Data
@Builder
public class BusinessFlow<T> {
private String flowName;
private List<FlowStep<T>> preSteps;
private List<FlowStep<T>> mainSteps;
private List<FlowStep<T>> postSteps;
private List<FlowStep<T>> compensateSteps;
}
/**
* 流程步骤接口
*/
public interface FlowStep<T> {
String getName();
StepResult<T> execute(FlowContext context);
}服务事务管理
声明式事务
java
/**
* 服务事务管理示例
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class TransactionalService {
private final OrderMapper orderMapper;
private final OrderItemMapper orderItemMapper;
private final InventoryMapper inventoryMapper;
/**
* 默认事务传播行为(REQUIRED)
* 如果当前存在事务则加入,否则创建新事务
*/
@Transactional(rollbackFor = Exception.class)
public void createOrder(OrderDTO orderDTO) {
// 创建订单
Order order = new Order();
BeanUtils.copyProperties(orderDTO, order);
orderMapper.insert(order);
// 创建订单项
for (OrderItemDTO itemDTO : orderDTO.getItems()) {
OrderItem item = new OrderItem();
BeanUtils.copyProperties(itemDTO, item);
item.setOrderId(order.getId());
orderItemMapper.insert(item);
// 扣减库存
deductInventory(itemDTO.getProductId(), itemDTO.getQuantity());
}
}
/**
* 独立事务(REQUIRES_NEW)
* 始终创建新事务,挂起当前事务
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void deductInventory(Long productId, Integer quantity) {
Inventory inventory = inventoryMapper.selectById(productId);
if (inventory.getStock() < quantity) {
throw new ServiceException("库存不足");
}
inventory.setStock(inventory.getStock() - quantity);
inventoryMapper.updateById(inventory);
}
/**
* 嵌套事务(NESTED)
* 如果当前存在事务,则在嵌套事务内执行
*/
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void updateOrderStatus(Long orderId, String status) {
Order order = orderMapper.selectById(orderId);
order.setStatus(status);
orderMapper.updateById(order);
}
/**
* 只读事务(优化查询性能)
*/
@Transactional(readOnly = true)
public OrderVO getOrderDetail(Long orderId) {
Order order = orderMapper.selectById(orderId);
List<OrderItem> items = orderItemMapper.selectByOrderId(orderId);
return OrderVO.of(order, items);
}
/**
* 指定异常回滚
*/
@Transactional(
rollbackFor = {ServiceException.class, DataAccessException.class},
noRollbackFor = {BusinessWarningException.class}
)
public void processOrderWithSpecificRollback(OrderDTO orderDTO) {
// 业务逻辑
}
/**
* 超时事务
*/
@Transactional(timeout = 30, rollbackFor = Exception.class)
public void longRunningProcess() {
// 长时间运行的业务逻辑
// 超过30秒将抛出事务超时异常
}
}编程式事务
java
/**
* 编程式事务管理
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ProgrammaticTransactionService {
private final TransactionTemplate transactionTemplate;
private final PlatformTransactionManager transactionManager;
private final OrderMapper orderMapper;
/**
* 使用TransactionTemplate管理事务
*/
public Order createOrderWithTemplate(OrderDTO orderDTO) {
return transactionTemplate.execute(status -> {
try {
Order order = new Order();
BeanUtils.copyProperties(orderDTO, order);
orderMapper.insert(order);
return order;
} catch (Exception e) {
status.setRollbackOnly();
throw new ServiceException("创建订单失败", e);
}
});
}
/**
* 使用TransactionManager手动管理事务
*/
public void manualTransactionControl(List<OrderDTO> orders) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("batchOrderTransaction");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
TransactionStatus status = transactionManager.getTransaction(def);
try {
for (OrderDTO orderDTO : orders) {
Order order = new Order();
BeanUtils.copyProperties(orderDTO, order);
orderMapper.insert(order);
}
transactionManager.commit(status);
log.info("批量订单创建成功,共{}条", orders.size());
} catch (Exception e) {
transactionManager.rollback(status);
log.error("批量订单创建失败,已回滚", e);
throw new ServiceException("批量创建订单失败", e);
}
}
/**
* 事务回调模式
*/
public void executeInTransaction(Runnable action) {
transactionTemplate.executeWithoutResult(status -> {
try {
action.run();
} catch (Exception e) {
status.setRollbackOnly();
throw new RuntimeException(e);
}
});
}
}分布式事务
java
/**
* 分布式事务处理
* 使用本地消息表实现最终一致性
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class DistributedTransactionService {
private final OrderMapper orderMapper;
private final LocalMessageMapper messageMapper;
private final RabbitTemplate rabbitTemplate;
/**
* 带本地消息表的订单创建
* 保证订单创建和消息发送的原子性
*/
@Transactional(rollbackFor = Exception.class)
public Order createOrderWithMessage(OrderDTO orderDTO) {
// 1. 创建订单
Order order = new Order();
BeanUtils.copyProperties(orderDTO, order);
orderMapper.insert(order);
// 2. 写入本地消息表
LocalMessage message = new LocalMessage();
message.setMessageId(UUID.randomUUID().toString());
message.setMessageType("ORDER_CREATED");
message.setMessageContent(JsonUtils.toJsonString(order));
message.setStatus("PENDING");
message.setRetryCount(0);
messageMapper.insert(message);
return order;
}
/**
* 消息发送定时任务
* 定时扫描本地消息表,发送未处理的消息
*/
@Scheduled(fixedDelay = 5000)
public void sendPendingMessages() {
List<LocalMessage> messages = messageMapper.selectPendingMessages(100);
for (LocalMessage message : messages) {
try {
// 发送消息
rabbitTemplate.convertAndSend(
"order.exchange",
"order.created",
message.getMessageContent()
);
// 更新消息状态
message.setStatus("SENT");
message.setSentTime(LocalDateTime.now());
messageMapper.updateById(message);
} catch (Exception e) {
log.error("消息发送失败: {}", message.getMessageId(), e);
message.setRetryCount(message.getRetryCount() + 1);
if (message.getRetryCount() >= 3) {
message.setStatus("FAILED");
}
messageMapper.updateById(message);
}
}
}
}
/**
* TCC模式事务协调器
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class TccTransactionCoordinator {
private final List<TccParticipant> participants;
/**
* 执行TCC事务
*/
public void executeTcc(TccContext context) {
// Try阶段
List<TccParticipant> triedParticipants = new ArrayList<>();
try {
for (TccParticipant participant : participants) {
participant.tryExecute(context);
triedParticipants.add(participant);
}
// Confirm阶段
for (TccParticipant participant : triedParticipants) {
participant.confirm(context);
}
} catch (Exception e) {
log.error("TCC事务执行失败,开始Cancel", e);
// Cancel阶段(逆序执行)
Collections.reverse(triedParticipants);
for (TccParticipant participant : triedParticipants) {
try {
participant.cancel(context);
} catch (Exception ex) {
log.error("Cancel执行失败", ex);
}
}
throw new ServiceException("分布式事务执行失败", e);
}
}
}
/**
* TCC参与者接口
*/
public interface TccParticipant {
void tryExecute(TccContext context);
void confirm(TccContext context);
void cancel(TccContext context);
}服务缓存策略
基于注解的缓存
java
/**
* 服务缓存使用示例
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class CachedUserService {
private final UserMapper userMapper;
private final DictService dictService;
/**
* 缓存用户信息
* 使用用户ID作为缓存key
*/
@Cacheable(
cacheNames = "user:detail",
key = "#userId",
unless = "#result == null"
)
public UserDTO getUserById(Long userId) {
log.info("从数据库查询用户: {}", userId);
return userMapper.selectDtoById(userId);
}
/**
* 更新用户信息并清除缓存
*/
@CacheEvict(
cacheNames = "user:detail",
key = "#user.userId"
)
@Transactional(rollbackFor = Exception.class)
public void updateUser(UserDTO user) {
userMapper.updateById(user.toEntity());
}
/**
* 更新用户信息并刷新缓存
*/
@CachePut(
cacheNames = "user:detail",
key = "#user.userId"
)
@Transactional(rollbackFor = Exception.class)
public UserDTO updateAndRefreshCache(UserDTO user) {
userMapper.updateById(user.toEntity());
return user;
}
/**
* 批量清除缓存
*/
@CacheEvict(
cacheNames = "user:detail",
allEntries = true
)
public void clearAllUserCache() {
log.info("清除所有用户缓存");
}
/**
* 组合缓存操作
*/
@Caching(
cacheable = {
@Cacheable(cacheNames = "user:detail", key = "#userId")
},
evict = {
@CacheEvict(cacheNames = "user:list", allEntries = true)
}
)
public UserDTO getAndEvictList(Long userId) {
return userMapper.selectDtoById(userId);
}
/**
* 条件缓存
* 只缓存状态正常的用户
*/
@Cacheable(
cacheNames = "user:active",
key = "#userId",
condition = "#userId > 0",
unless = "#result == null || #result.status != '0'"
)
public UserDTO getActiveUser(Long userId) {
return userMapper.selectDtoById(userId);
}
}手动缓存管理
java
/**
* 手动缓存管理服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ManualCacheService {
private final RedissonClient redissonClient;
private final UserMapper userMapper;
private final DictService dictService;
private static final String USER_CACHE_KEY = "user:cache:";
private static final Duration DEFAULT_TTL = Duration.ofHours(2);
/**
* 获取用户(带缓存)
*/
public UserDTO getUserWithCache(Long userId) {
String cacheKey = USER_CACHE_KEY + userId;
// 尝试从缓存获取
RBucket<UserDTO> bucket = redissonClient.getBucket(cacheKey);
UserDTO cached = bucket.get();
if (cached != null) {
log.debug("从缓存获取用户: {}", userId);
return cached;
}
// 缓存未命中,从数据库查询
UserDTO user = userMapper.selectDtoById(userId);
if (user != null) {
bucket.set(user, DEFAULT_TTL);
log.debug("用户信息已缓存: {}", userId);
}
return user;
}
/**
* 批量获取用户(带缓存优化)
*/
public List<UserDTO> getUsersWithCache(List<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Collections.emptyList();
}
List<String> cacheKeys = userIds.stream()
.map(id -> USER_CACHE_KEY + id)
.collect(Collectors.toList());
// 批量获取缓存
RBuckets buckets = redissonClient.getBuckets();
Map<String, UserDTO> cachedMap = buckets.get(cacheKeys.toArray(new String[0]));
// 找出未命中的用户ID
List<Long> missedIds = new ArrayList<>();
List<UserDTO> result = new ArrayList<>();
for (Long userId : userIds) {
String key = USER_CACHE_KEY + userId;
UserDTO cached = cachedMap.get(key);
if (cached != null) {
result.add(cached);
} else {
missedIds.add(userId);
}
}
// 批量查询未命中的数据
if (!missedIds.isEmpty()) {
List<UserDTO> dbUsers = userMapper.selectDtoByIds(missedIds);
// 写入缓存
Map<String, UserDTO> toCache = new HashMap<>();
for (UserDTO user : dbUsers) {
toCache.put(USER_CACHE_KEY + user.getUserId(), user);
result.add(user);
}
// 批量设置缓存
if (!toCache.isEmpty()) {
buckets.set(toCache);
}
}
return result;
}
/**
* 缓存预热
*/
public void warmUpCache() {
log.info("开始预热用户缓存...");
List<UserDTO> hotUsers = userMapper.selectHotUsers(1000);
RBatch batch = redissonClient.createBatch();
for (UserDTO user : hotUsers) {
batch.getBucket(USER_CACHE_KEY + user.getUserId())
.setAsync(user, DEFAULT_TTL);
}
batch.execute();
log.info("用户缓存预热完成,共{}条", hotUsers.size());
}
/**
* 缓存失效处理
*/
public void evictUserCache(Long userId) {
String cacheKey = USER_CACHE_KEY + userId;
redissonClient.getBucket(cacheKey).delete();
log.debug("用户缓存已删除: {}", userId);
}
}服务异步处理
异步方法
java
/**
* 异步服务示例
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class AsyncService {
private final UserService userService;
private final EmailService emailService;
private final SmsService smsService;
/**
* 简单异步方法
*/
@Async
public void asyncSendEmail(String email, String content) {
log.info("异步发送邮件开始: {}", email);
emailService.send(email, content);
log.info("异步发送邮件完成: {}", email);
}
/**
* 异步方法返回Future
*/
@Async
public CompletableFuture<UserDTO> asyncGetUser(Long userId) {
log.info("异步获取用户: {}", userId);
List<UserDTO> users = userService.listUsersByIds(
Collections.singletonList(userId)
);
return CompletableFuture.completedFuture(
users.stream().findFirst().orElse(null)
);
}
/**
* 指定线程池的异步方法
*/
@Async("notificationExecutor")
public void asyncSendNotification(NotificationDTO notification) {
switch (notification.getType()) {
case "EMAIL":
emailService.send(notification.getTarget(), notification.getContent());
break;
case "SMS":
smsService.send(notification.getTarget(), notification.getContent());
break;
default:
log.warn("未知通知类型: {}", notification.getType());
}
}
/**
* 批量异步处理
*/
public List<UserDTO> batchAsyncGetUsers(List<Long> userIds) {
List<CompletableFuture<UserDTO>> futures = userIds.stream()
.map(this::asyncGetUser)
.collect(Collectors.toList());
return futures.stream()
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
/**
* 异步线程池配置
*/
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Bean("notificationExecutor")
public Executor notificationExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("notification-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
log.error("异步方法执行异常: {} - {}", method.getName(), throwable.getMessage(), throwable);
};
}
}响应式服务
java
/**
* 响应式服务示例
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ReactiveService {
private final UserService userService;
private final DictService dictService;
/**
* 响应式获取用户详情
*/
public Mono<UserDetailVO> getUserDetailReactive(Long userId) {
return Mono.fromSupplier(() ->
userService.listUsersByIds(Collections.singletonList(userId))
.stream()
.findFirst()
.orElseThrow(() -> new ServiceException("用户不存在"))
)
.map(user -> {
UserDetailVO detail = new UserDetailVO();
detail.setUserId(user.getUserId());
detail.setUserName(user.getUserName());
detail.setSexLabel(dictService.getDictLabel("sys_user_sex", user.getSex()));
return detail;
})
.subscribeOn(Schedulers.boundedElastic());
}
/**
* 响应式批量获取用户
*/
public Flux<UserDetailVO> getUserDetailsReactive(List<Long> userIds) {
return Flux.fromIterable(userIds)
.flatMap(userId -> getUserDetailReactive(userId))
.onErrorContinue((error, item) ->
log.error("获取用户失败: {}", item, error)
);
}
/**
* 响应式并行处理
*/
public Mono<AggregatedResult> aggregateDataReactive(Long userId) {
Mono<UserDTO> userMono = Mono.fromSupplier(() ->
userService.listUsersByIds(Collections.singletonList(userId))
.stream()
.findFirst()
.orElse(null)
).subscribeOn(Schedulers.boundedElastic());
Mono<Map<String, String>> dictMono = Mono.fromSupplier(() ->
dictService.getAllDictByDictType("sys_user_sex")
).subscribeOn(Schedulers.boundedElastic());
return Mono.zip(userMono, dictMono)
.map(tuple -> {
UserDTO user = tuple.getT1();
Map<String, String> sexDict = tuple.getT2();
AggregatedResult result = new AggregatedResult();
result.setUser(user);
result.setSexLabel(sexDict.get(user.getSex()));
return result;
});
}
}服务日志与审计
操作日志记录
java
/**
* 操作日志切面
*/
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class OperationLogAspect {
private final OperationLogMapper logMapper;
private final UserService userService;
@Around("@annotation(operationLog)")
public Object around(ProceedingJoinPoint point, OperationLog operationLog) throws Throwable {
long startTime = System.currentTimeMillis();
OperationLogEntity logEntity = new OperationLogEntity();
logEntity.setTitle(operationLog.title());
logEntity.setBusinessType(operationLog.businessType().ordinal());
logEntity.setMethod(point.getSignature().toString());
logEntity.setRequestMethod(getRequestMethod());
logEntity.setOperatorType(operationLog.operatorType().ordinal());
logEntity.setOperName(getCurrentUserName());
logEntity.setOperUrl(getRequestUrl());
logEntity.setOperIp(getRemoteIp());
logEntity.setOperParam(getOperParam(point, operationLog));
logEntity.setOperTime(new Date());
Object result = null;
try {
result = point.proceed();
logEntity.setStatus(0);
logEntity.setJsonResult(toJsonString(result, operationLog));
} catch (Exception e) {
logEntity.setStatus(1);
logEntity.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
throw e;
} finally {
logEntity.setCostTime(System.currentTimeMillis() - startTime);
// 异步保存日志
saveLogAsync(logEntity);
}
return result;
}
@Async
public void saveLogAsync(OperationLogEntity logEntity) {
try {
logMapper.insert(logEntity);
} catch (Exception e) {
log.error("保存操作日志失败", e);
}
}
private String getCurrentUserName() {
try {
Long userId = StpUtil.getLoginIdAsLong();
return userService.getUserNameById(userId);
} catch (Exception e) {
return "匿名用户";
}
}
// 其他辅助方法...
}
/**
* 操作日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
/**
* 模块
*/
String title() default "";
/**
* 业务类型
*/
BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类型
*/
OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求参数
*/
boolean isSaveRequestData() default true;
/**
* 是否保存响应数据
*/
boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
String[] excludeParamNames() default {};
}
/**
* 业务类型枚举
*/
public enum BusinessType {
OTHER, // 其他
INSERT, // 新增
UPDATE, // 修改
DELETE, // 删除
EXPORT, // 导出
IMPORT, // 导入
GRANT, // 授权
FORCE, // 强制
CLEAN // 清空
}审计服务
java
/**
* 数据审计服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class AuditService {
private final AuditLogMapper auditLogMapper;
private final DictService dictService;
/**
* 记录数据变更审计
*/
public void auditDataChange(AuditContext context) {
AuditLog auditLog = new AuditLog();
auditLog.setAuditId(IdUtil.getSnowflakeNextId());
auditLog.setTableName(context.getTableName());
auditLog.setRecordId(context.getRecordId());
auditLog.setOperationType(context.getOperationType());
auditLog.setOperator(context.getOperator());
auditLog.setOperateTime(LocalDateTime.now());
// 记录变更前后的数据
if (context.getOldData() != null) {
auditLog.setOldData(JsonUtils.toJsonString(context.getOldData()));
}
if (context.getNewData() != null) {
auditLog.setNewData(JsonUtils.toJsonString(context.getNewData()));
}
// 计算数据差异
if (context.getOldData() != null && context.getNewData() != null) {
Map<String, Object> diff = calculateDiff(context.getOldData(), context.getNewData());
auditLog.setDiffData(JsonUtils.toJsonString(diff));
}
auditLogMapper.insert(auditLog);
}
/**
* 计算数据差异
*/
private Map<String, Object> calculateDiff(Object oldData, Object newData) {
Map<String, Object> diff = new LinkedHashMap<>();
Map<String, Object> oldMap = BeanUtil.beanToMap(oldData);
Map<String, Object> newMap = BeanUtil.beanToMap(newData);
for (Map.Entry<String, Object> entry : newMap.entrySet()) {
String key = entry.getKey();
Object newValue = entry.getValue();
Object oldValue = oldMap.get(key);
if (!Objects.equals(oldValue, newValue)) {
Map<String, Object> change = new HashMap<>();
change.put("old", oldValue);
change.put("new", newValue);
diff.put(key, change);
}
}
return diff;
}
/**
* 查询审计记录
*/
public PageResult<AuditLogVO> queryAuditLogs(AuditQueryDTO query) {
Page<AuditLog> page = auditLogMapper.selectPage(
new Page<>(query.getPageNum(), query.getPageSize()),
new LambdaQueryWrapper<AuditLog>()
.eq(StringUtils.isNotBlank(query.getTableName()), AuditLog::getTableName, query.getTableName())
.eq(query.getRecordId() != null, AuditLog::getRecordId, query.getRecordId())
.eq(StringUtils.isNotBlank(query.getOperationType()), AuditLog::getOperationType, query.getOperationType())
.between(query.getStartTime() != null && query.getEndTime() != null,
AuditLog::getOperateTime, query.getStartTime(), query.getEndTime())
.orderByDesc(AuditLog::getOperateTime)
);
List<AuditLogVO> records = page.getRecords().stream()
.map(this::toVO)
.collect(Collectors.toList());
return PageResult.of(records, page.getTotal());
}
private AuditLogVO toVO(AuditLog auditLog) {
AuditLogVO vo = new AuditLogVO();
BeanUtils.copyProperties(auditLog, vo);
vo.setOperationTypeLabel(dictService.getDictLabel("sys_audit_type", auditLog.getOperationType()));
return vo;
}
}服务监控与健康检查
服务健康检查
java
/**
* 服务健康检查指示器
*/
@Component
public class ServiceHealthIndicator implements HealthIndicator {
@Autowired
private UserService userService;
@Autowired
private DictService dictService;
@Autowired
private ConfigService configService;
@Override
public Health health() {
Map<String, Object> details = new LinkedHashMap<>();
boolean allHealthy = true;
// 检查UserService
try {
userService.getUserNameById(1L);
details.put("userService", "UP");
} catch (Exception e) {
details.put("userService", "DOWN: " + e.getMessage());
allHealthy = false;
}
// 检查DictService
try {
dictService.getDictLabel("sys_user_sex", "1");
details.put("dictService", "UP");
} catch (Exception e) {
details.put("dictService", "DOWN: " + e.getMessage());
allHealthy = false;
}
// 检查ConfigService
try {
configService.getConfigValue("sys.index.skinName", "default");
details.put("configService", "UP");
} catch (Exception e) {
details.put("configService", "DOWN: " + e.getMessage());
allHealthy = false;
}
if (allHealthy) {
return Health.up().withDetails(details).build();
} else {
return Health.down().withDetails(details).build();
}
}
}服务指标收集
java
/**
* 服务指标收集器
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class ServiceMetricsCollector {
private final MeterRegistry meterRegistry;
private Counter serviceCallCounter;
private Timer serviceCallTimer;
private DistributionSummary responseSizeDistribution;
@PostConstruct
public void init() {
serviceCallCounter = Counter.builder("service.calls.total")
.description("服务调用总次数")
.register(meterRegistry);
serviceCallTimer = Timer.builder("service.calls.duration")
.description("服务调用耗时")
.register(meterRegistry);
responseSizeDistribution = DistributionSummary.builder("service.response.size")
.description("响应数据大小")
.baseUnit("bytes")
.register(meterRegistry);
}
/**
* 记录服务调用
*/
public void recordServiceCall(String serviceName, String methodName, long duration, boolean success) {
serviceCallCounter.increment();
Timer.builder("service.calls.duration")
.tag("service", serviceName)
.tag("method", methodName)
.tag("success", String.valueOf(success))
.register(meterRegistry)
.record(duration, TimeUnit.MILLISECONDS);
}
/**
* 记录响应大小
*/
public void recordResponseSize(String serviceName, int size) {
DistributionSummary.builder("service.response.size")
.tag("service", serviceName)
.baseUnit("bytes")
.register(meterRegistry)
.record(size);
}
/**
* 记录活跃用户数
*/
public void recordActiveUsers(int count) {
Gauge.builder("service.active.users", () -> count)
.description("活跃用户数")
.register(meterRegistry);
}
}
/**
* 服务调用监控切面
*/
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class ServiceMetricsAspect {
private final ServiceMetricsCollector metricsCollector;
@Around("execution(* org.dromara..*Service.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
String serviceName = point.getTarget().getClass().getSimpleName();
String methodName = point.getSignature().getName();
long startTime = System.currentTimeMillis();
boolean success = true;
try {
return point.proceed();
} catch (Exception e) {
success = false;
throw e;
} finally {
long duration = System.currentTimeMillis() - startTime;
metricsCollector.recordServiceCall(serviceName, methodName, duration, success);
}
}
}服务Mock测试
单元测试Mock
java
/**
* 服务单元测试示例
*/
@ExtendWith(MockitoExtension.class)
class BusinessServiceTest {
@Mock
private UserService userService;
@Mock
private DictService dictService;
@Mock
private ConfigService configService;
@InjectMocks
private BusinessService businessService;
@Test
void testProcessUserData() {
// Given
Long userId = 1L;
UserDTO mockUser = new UserDTO();
mockUser.setUserId(userId);
mockUser.setUserName("testUser");
mockUser.setNickName("测试用户");
mockUser.setSex("1");
when(userService.listUsersByIds(Collections.singletonList(userId)))
.thenReturn(Collections.singletonList(mockUser));
when(dictService.getDictLabel("sys_user_sex", "1"))
.thenReturn("男");
// When
UserDetailVO result = businessService.getUserDetail(userId);
// Then
assertNotNull(result);
assertEquals("testUser", result.getUserName());
assertEquals("测试用户", result.getNickName());
assertEquals("男", result.getSexLabel());
verify(userService).listUsersByIds(Collections.singletonList(userId));
verify(dictService).getDictLabel("sys_user_sex", "1");
}
@Test
void testProcessUserData_UserNotFound() {
// Given
Long userId = 999L;
when(userService.listUsersByIds(Collections.singletonList(userId)))
.thenReturn(Collections.emptyList());
// When & Then
assertThrows(ServiceException.class, () -> {
businessService.getUserDetail(userId);
});
}
@Test
void testBatchProcessUsers() {
// Given
List<Long> userIds = Arrays.asList(1L, 2L, 3L);
List<UserDTO> mockUsers = userIds.stream().map(id -> {
UserDTO user = new UserDTO();
user.setUserId(id);
user.setUserName("user" + id);
user.setSex("1");
return user;
}).collect(Collectors.toList());
when(userService.listUsersByIds(userIds))
.thenReturn(mockUsers);
when(dictService.getAllDictByDictType("sys_user_sex"))
.thenReturn(Map.of("1", "男", "0", "女"));
// When
List<UserDetailVO> results = businessService.getUserDetails(userIds);
// Then
assertEquals(3, results.size());
assertTrue(results.stream().allMatch(u -> "男".equals(u.getSexLabel())));
}
}集成测试
java
/**
* 服务集成测试示例
*/
@SpringBootTest
@ActiveProfiles("test")
@Transactional
class ServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private DictService dictService;
@Autowired
private ConfigService configService;
@Autowired
private BusinessService businessService;
@Test
void testUserServiceIntegration() {
// Given
Long userId = 1L;
// When
String userName = userService.getUserNameById(userId);
String nickName = userService.getNickNameById(userId);
// Then
assertNotNull(userName);
assertNotNull(nickName);
}
@Test
void testDictServiceIntegration() {
// Given
String dictType = "sys_user_sex";
// When
Map<String, String> allDict = dictService.getAllDictByDictType(dictType);
String label = dictService.getDictLabel(dictType, "1");
// Then
assertFalse(allDict.isEmpty());
assertNotNull(label);
}
@Test
void testConfigServiceIntegration() {
// Given
String configKey = "sys.index.skinName";
// When
String value = configService.getConfigValue(configKey);
String valueWithDefault = configService.getConfigValue("nonexistent.key", "default");
// Then
assertNotNull(value);
assertEquals("default", valueWithDefault);
}
@Test
void testFullBusinessFlow() {
// Given
Long userId = 1L;
// When
UserDetailVO detail = businessService.getUserDetail(userId);
// Then
assertNotNull(detail);
assertNotNull(detail.getUserName());
assertNotNull(detail.getSexLabel());
}
}服务降级与熔断
熔断器配置
java
/**
* 熔断器服务包装
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ResilientUserService {
private final UserService userService;
private final CircuitBreakerRegistry circuitBreakerRegistry;
/**
* 带熔断的用户查询
*/
public UserDTO getUserWithCircuitBreaker(Long userId) {
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("userService");
return Try.ofSupplier(
CircuitBreaker.decorateSupplier(circuitBreaker, () ->
userService.listUsersByIds(Collections.singletonList(userId))
.stream()
.findFirst()
.orElse(null)
)
).recover(throwable -> {
log.warn("用户服务调用失败,使用降级策略: {}", throwable.getMessage());
return getFallbackUser(userId);
}).get();
}
/**
* 降级用户信息
*/
private UserDTO getFallbackUser(Long userId) {
UserDTO fallback = new UserDTO();
fallback.setUserId(userId);
fallback.setUserName("用户" + userId);
fallback.setNickName("临时用户");
fallback.setStatus("1");
return fallback;
}
}
/**
* 熔断器配置
*/
@Configuration
public class CircuitBreakerConfig {
@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失败率阈值50%
.waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断30秒
.slidingWindowSize(10) // 滑动窗口大小10
.minimumNumberOfCalls(5) // 最少5次调用才计算失败率
.permittedNumberOfCallsInHalfOpenState(3) // 半开状态允许3次调用
.automaticTransitionFromOpenToHalfOpenEnabled(true)
.build();
return CircuitBreakerRegistry.of(config);
}
}限流与降级
java
/**
* 限流降级服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class RateLimitedService {
private final UserService userService;
private final RateLimiterRegistry rateLimiterRegistry;
/**
* 带限流的用户批量查询
*/
public List<UserDTO> getUsersWithRateLimit(List<Long> userIds) {
RateLimiter rateLimiter = rateLimiterRegistry.rateLimiter("userQueryRateLimiter");
return Try.ofSupplier(
RateLimiter.decorateSupplier(rateLimiter, () ->
userService.listUsersByIds(userIds)
)
).recover(RequestNotPermitted.class, throwable -> {
log.warn("请求被限流: {}", throwable.getMessage());
return Collections.emptyList();
}).get();
}
}
/**
* 限流器配置
*/
@Configuration
public class RateLimiterConfig {
@Bean
public RateLimiterRegistry rateLimiterRegistry() {
RateLimiterConfig config = RateLimiterConfig.custom()
.limitRefreshPeriod(Duration.ofSeconds(1)) // 每秒刷新
.limitForPeriod(100) // 每秒100个请求
.timeoutDuration(Duration.ofMillis(500)) // 等待超时500ms
.build();
return RateLimiterRegistry.of(config);
}
}注意事项
1. 服务接口设计原则
- 单一职责:每个服务接口只负责一个领域的功能
- 接口隔离:不要创建臃肿的通用接口,应该拆分成多个小接口
- 依赖倒置:高层模块不依赖低层模块,都依赖于抽象
- 默认方法:合理使用 default 方法提供默认实现,保证向后兼容
2. 事务管理注意事项
- 避免大事务:将大事务拆分成多个小事务
- 只读事务:查询操作使用
@Transactional(readOnly = true) - 事务传播:根据业务需求选择合适的传播行为
- 异常回滚:明确指定需要回滚的异常类型
3. 缓存使用建议
- 缓存穿透:使用空值缓存或布隆过滤器
- 缓存雪崩:设置随机过期时间,避免同时失效
- 缓存击穿:使用分布式锁保护热点数据
- 缓存一致性:使用延迟双删或订阅binlog方案
4. 异步处理注意事项
- 线程池配置:根据业务场景配置合适的线程池参数
- 异常处理:配置异步异常处理器,避免异常丢失
- 事务边界:异步方法中的事务是独立的
- 上下文传递:注意
SecurityContext和RequestContext的传递
5. 服务监控要点
- 关键指标:监控调用次数、成功率、响应时间等
- 日志规范:统一日志格式,包含请求ID便于追踪
- 告警阈值:设置合理的告警阈值,避免告警疲劳
- 健康检查:实现服务健康检查接口,配合容器编排使用
6. 服务测试策略
- 单元测试:使用 Mock 隔离外部依赖
- 集成测试:测试服务间的交互
- 契约测试:验证服务接口契约
- 性能测试:验证服务在高并发下的表现
这些通用服务接口构成了系统的基础服务层,为上层业务逻辑提供了统一、规范的数据访问方式。通过合理的设计模式、事务管理、缓存策略、异步处理和监控机制,可以构建出高可用、高性能的服务层架构。
