refactor LoggerService.excute

This commit is contained in:
Timi
2026-09-09 13:04:16 +08:00
parent 2ea06fb5c0
commit c122c35376
20 changed files with 533 additions and 250 deletions
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourcePerFileMappings">
<file url="file://$PROJECT_DIR$/src/main/java/com/imyeyu/api/modules/common/mapper/GroovyScriptVersionMapper.java" value="05742bdc-a11a-4147-9168-ef976a70258a" />
</component>
</project>
@@ -32,4 +32,22 @@ public class ThreadPoolConfig {
executor.initialize();
return executor;
}
/// 创建操作审计线程池
///
/// @return 操作审计线程池
@Bean(name = "auditTaskExecutor")
public ThreadPoolTaskExecutor auditTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(2000);
executor.setKeepAliveSeconds(60);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.setThreadNamePrefix("audit-task-executor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.common.controller;
import com.imyeyu.api.modules.common.vo.ClientLogRequest;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.spring.annotation.AOPLog;
@@ -29,7 +30,9 @@ public class LoggerController {
@AOPLog
@RequiredToken
@PostMapping("/create")
public void create(@Valid @RequestBody Logger logger) {
public void create(@Valid @RequestBody ClientLogRequest request) {
Logger logger = Logger.info(Logger.Module.COMMON, "CLIENT_LOG");
logger.setContent(request.getContent());
service.create(logger);
}
}
}
@@ -0,0 +1,18 @@
package com.imyeyu.api.modules.common.vo;
import jakarta.validation.constraints.Size;
import lombok.Data;
/// 客户端诊断日志请求
///
/// 客户端日志不是操作审计,操作编码、模块、级别、用户和 IP 均由服务端决定
///
/// @author Codex
/// @since 2026-09-09
@Data
public class ClientLogRequest {
/// 日志内容
@Size(max = 32768, message = "日志内容不能超过 32 KB")
private String content;
}
@@ -24,8 +24,8 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePurchaseItem;
import com.imyeyu.api.modules.gao.vo.GaoPackageOrderCreateData;
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.user.service.PermissionChecker;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.java.TimiJava;
@@ -69,7 +69,6 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
private final AttachmentService attachmentService;
private final UserLoginService userLoginService;
private final PermissionChecker permissionChecker;
private final LoggerService loggerService;
@Override
protected BaseMapper<GaoCustomerPackage, String> mapper() {
@@ -128,10 +127,11 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
return super.get(id);
}
@OperationAudit(AuditOperation.GAO_CUSTOMER_PACKAGE_PURCHASE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoCustomerPackage purchase(GaoCustomerPackagePurchaseRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_PURCHASE", request, () -> purchaseInternal(request));
return purchaseInternal(request);
}
private GaoCustomerPackage purchaseInternal(GaoCustomerPackagePurchaseRequest request) {
@@ -206,10 +206,11 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
return result;
}
@OperationAudit(AuditOperation.GAO_CUSTOMER_PACKAGE_CANCEL)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoCustomerPackage cancel(GaoCustomerPackageCancelRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_CANCEL", request, () -> cancelInternal(request));
return cancelInternal(request);
}
private GaoCustomerPackage cancelInternal(GaoCustomerPackageCancelRequest request) {
@@ -31,8 +31,8 @@ import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentPrepareStockRequest;
import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentRevokeRequest;
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.user.service.RoleChecker;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.java.TimiJava;
@@ -79,7 +79,6 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
private final RoleChecker roleChecker;
private final UserLoginService userLoginService;
private final AttachmentService attachmentService;
private final LoggerService loggerService;
@Override
protected BaseMapper<GaoOrderFulfillment, String> mapper() {
@@ -147,10 +146,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
return mapper.countPendingBySalesOrderId(salesOrderId);
}
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_CREATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoOrderFulfillment create(GaoOrderFulfillmentCreateRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CREATE", request, () -> createInternal(request));
return createInternal(request);
}
private GaoOrderFulfillment createInternal(GaoOrderFulfillmentCreateRequest request) {
@@ -183,10 +183,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
return detail(fulfillment.getId());
}
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_PREPARE_STOCK)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoOrderFulfillment prepareStock(GaoOrderFulfillmentPrepareStockRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_PREPARE_STOCK", request, () -> prepareStockInternal(request));
return prepareStockInternal(request);
}
private GaoOrderFulfillment prepareStockInternal(GaoOrderFulfillmentPrepareStockRequest request) {
@@ -217,10 +218,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
return detail(fulfillment.getId());
}
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_VERIFY)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoOrderFulfillment verify(GaoOrderFulfillmentActionRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_VERIFY", request, () -> verifyInternal(request));
return verifyInternal(request);
}
private GaoOrderFulfillment verifyInternal(GaoOrderFulfillmentActionRequest request) {
@@ -254,16 +256,18 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
return detail(fulfillment.getId());
}
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_REJECT)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoOrderFulfillment reject(GaoOrderFulfillmentActionRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REJECT", request, () -> releaseInternal(request, GaoOrderFulfillment.Status.REJECTED));
return releaseInternal(request, GaoOrderFulfillment.Status.REJECTED);
}
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_CANCEL)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoOrderFulfillment cancel(GaoOrderFulfillmentActionRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CANCEL", request, () -> cancelInternal(request));
return cancelInternal(request);
}
private GaoOrderFulfillment cancelInternal(GaoOrderFulfillmentActionRequest request) {
@@ -306,10 +310,11 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
return detail(fulfillment.getId());
}
@OperationAudit(AuditOperation.GAO_ORDER_FULFILLMENT_REVOKE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoOrderFulfillment revoke(GaoOrderFulfillmentRevokeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REVOKE", request, () -> revokeInternal(request));
return revokeInternal(request);
}
private GaoOrderFulfillment revokeInternal(GaoOrderFulfillmentRevokeRequest request) {
@@ -16,8 +16,8 @@ import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentNoRefundRequest;
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentReceiveRequest;
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRefundRequest;
import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRevokeRequest;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.api.modules.user.service.PermissionChecker;
import com.imyeyu.java.TimiJava;
@@ -44,12 +44,12 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
private final AttachmentService attachmentService;
private final UserLoginService userLoginService;
private final PermissionChecker permissionChecker;
private final LoggerService loggerService;
@OperationAudit(AuditOperation.GAO_SALES_ORDER_PAYMENT_RECEIVE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoFundRecord receive(GaoOrderPaymentReceiveRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_RECEIVE", request, () -> receiveInternal(request));
return receiveInternal(request);
}
private GaoFundRecord receiveInternal(GaoOrderPaymentReceiveRequest request) {
@@ -80,11 +80,11 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
return record;
}
@OperationAudit(AuditOperation.GAO_SALES_ORDER_PAYMENT_VERIFY)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoFundRecord verify(String fundRecordId) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_VERIFY", fundRecordId, () -> {
GaoFundRecord record = lockOrderRecord(fundRecordId);
GaoFundRecord record = lockOrderRecord(fundRecordId);
TimiException.requiredTrue(record.getOperationType() == GaoFundRecord.OperationType.RECEIVE && record.getStatus() == GaoFundRecord.Status.PENDING, "收款记录不是待核销状态");
GaoSalesOrder order = orderService.getForUpdate(record.getBizId());
Long paidAtBefore = order.getPaidAt();
@@ -102,7 +102,6 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
pointTriggerService.onSalesOrderPaid(order);
}
return record;
});
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@@ -140,10 +139,11 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
return record;
}
@OperationAudit(AuditOperation.GAO_SALES_ORDER_REFUND)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoFundRecord refund(GaoOrderPaymentRefundRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_REFUND", request, () -> refundInternal(request));
return refundInternal(request);
}
private GaoFundRecord refundInternal(GaoOrderPaymentRefundRequest request) {
@@ -177,11 +177,11 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
return record;
}
@OperationAudit(AuditOperation.GAO_SALES_ORDER_CONFIRM_NO_REFUND)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoFundRecord confirmNoRefund(GaoOrderPaymentNoRefundRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_CONFIRM_NO_REFUND", request, () -> {
TimiException.required(request, "not found no refund request");
TimiException.required(request, "not found no refund request");
GaoSalesOrder order = orderService.getForUpdate(request.getSalesOrderId());
String requestNo = requiredText(request.getRequestNo(), "not found requestNo");
String reason = requiredText(request.getReason(), "确认无需退款原因不能为空");
@@ -199,13 +199,13 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
order.setRefundStatus(GaoSalesOrder.RefundStatus.REFUNDED);
orderService.updatePayment(order);
return record;
});
}
@OperationAudit(AuditOperation.GAO_SALES_ORDER_FUND_REVOKE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoFundRecord revoke(GaoOrderPaymentRevokeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_FUND_REVOKE", request, () -> revokeInternal(request));
return revokeInternal(request);
}
private GaoFundRecord revokeInternal(GaoOrderPaymentRevokeRequest request) {
@@ -6,8 +6,8 @@ import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
import com.imyeyu.api.modules.gao.entity.GaoProduct;
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigItemMapper;
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigMapper;
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
@@ -44,7 +44,6 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
private final GaoPackageConfigItemMapper itemMapper;
private final GaoProductSkuService skuService;
private final UserLoginService userLoginService;
private final LoggerService loggerService;
@Override
protected BaseMapper<GaoPackageConfig, String> mapper() {
@@ -90,13 +89,11 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
.collect(java.util.stream.Collectors.groupingBy(GaoPackageConfigItem::getPackageConfigId));
}
@OperationAudit(AuditOperation.GAO_PACKAGE_CONFIG_CREATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(GaoPackageConfig config) {
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_CREATE", config, () -> {
createInternal(config);
return config;
});
createInternal(config);
}
private void createInternal(GaoPackageConfig config) {
@@ -109,13 +106,11 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
saveItems(config.getId(), config.getItemList());
}
@OperationAudit(AuditOperation.GAO_PACKAGE_CONFIG_UPDATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void update(GaoPackageConfig request) {
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_UPDATE", request, () -> {
updateInternal(request);
return request;
});
updateInternal(request);
}
private void updateInternal(GaoPackageConfig request) {
@@ -141,15 +136,13 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
saveItems(dbConfig.getId(), request.getItemList());
}
@OperationAudit(AuditOperation.GAO_PACKAGE_CONFIG_DELETE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void delete(String id) {
loggerService.execute(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_DELETE", id, () -> {
GaoPackageConfig config = get(id);
TimiException.required(config, "not found package config");
super.delete(id);
return id;
});
GaoPackageConfig config = get(id);
TimiException.required(config, "not found package config");
super.delete(id);
}
/// 套餐配置只允许逻辑删除
@@ -6,8 +6,8 @@ import com.imyeyu.api.modules.gao.mapper.GaoProductCategoryMapper;
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
import com.imyeyu.api.modules.gao.vo.GaoProductCategoryPage;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException;
@@ -37,7 +37,6 @@ public class GaoProductCategoryServiceImplement extends AbstractEntityService<Ga
private final GaoProductCategoryMapper mapper;
private final GaoStoreScopeService storeScopeService;
private final UserLoginService userLoginService;
private final LoggerService loggerService;
@Override
protected BaseMapper<GaoProductCategory, String> mapper() {
@@ -59,6 +58,7 @@ public class GaoProductCategoryServiceImplement extends AbstractEntityService<Ga
return mapper.selectByStoreId(storeId);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_SORT)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void sort(List<String> idList) {
@@ -66,58 +66,50 @@ public class GaoProductCategoryServiceImplement extends AbstractEntityService<Ga
return;
}
List<String> distinctIdList = new ArrayList<>(new LinkedHashSet<>(idList));
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_SORT", distinctIdList, () -> {
Map<String, GaoProductCategory> categoryMap = mapper.selectByIdList(new LinkedHashSet<>(distinctIdList)).stream()
.collect(Collectors.toMap(GaoProductCategory::getId, Function.identity()));
TimiException.requiredTrue(categoryMap.size() == distinctIdList.size(), "产品分类列表包含不存在的分类");
for (GaoProductCategory category : categoryMap.values()) {
storeScopeService.checkStoreId(category.getStoreId());
}
mapper.updateSort(distinctIdList, Time.now());
});
Map<String, GaoProductCategory> categoryMap = mapper.selectByIdList(new LinkedHashSet<>(distinctIdList)).stream()
.collect(Collectors.toMap(GaoProductCategory::getId, Function.identity()));
TimiException.requiredTrue(categoryMap.size() == distinctIdList.size(), "产品分类列表包含不存在的分类");
for (GaoProductCategory category : categoryMap.values()) {
storeScopeService.checkStoreId(category.getStoreId());
}
mapper.updateSort(distinctIdList, Time.now());
}
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_CREATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(GaoProductCategory category) {
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_CREATE", category, () -> {
prepare(category, null);
super.create(category);
return category;
});
prepare(category, null);
super.create(category);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_UPDATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void update(GaoProductCategory request) {
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_UPDATE", request, () -> {
TimiException.required(request, "not found category");
TimiException.required(request.getId(), "not found category.id");
GaoProductCategory category = get(request.getId());
TimiException.required(category, "not found category");
storeScopeService.checkStoreId(category.getStoreId());
category.setName(request.getName());
category.setSort(request.getSort());
category.setStatus(request.getStatus());
prepare(category, category.getId());
super.update(category);
return category;
});
TimiException.required(request, "not found category");
TimiException.required(request.getId(), "not found category.id");
GaoProductCategory category = get(request.getId());
TimiException.required(category, "not found category");
storeScopeService.checkStoreId(category.getStoreId());
category.setName(request.getName());
category.setSort(request.getSort());
category.setStatus(request.getStatus());
prepare(category, category.getId());
super.update(category);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_CATEGORY_DISABLE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void disable(String id) {
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_DISABLE", id, () -> {
GaoProductCategory category = get(id);
TimiException.required(category, "not found category");
storeScopeService.checkStoreId(category.getStoreId());
category.setStatus(GaoProductCategory.Status.INACTIVE);
category.setUpdatedAt(Time.now());
category.setSort(TimiJava.defaultIfNull(category.getSort(), 0));
super.update(category);
return category;
});
GaoProductCategory category = get(id);
TimiException.required(category, "not found category");
storeScopeService.checkStoreId(category.getStoreId());
category.setStatus(GaoProductCategory.Status.INACTIVE);
category.setUpdatedAt(Time.now());
category.setSort(TimiJava.defaultIfNull(category.getSort(), 0));
super.update(category);
}
@Override
@@ -14,8 +14,8 @@ import com.imyeyu.api.modules.gao.service.GaoProductService;
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
import com.imyeyu.api.modules.gao.service.GaoProductSpecService;
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
@@ -52,7 +52,6 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
private final ObjectProvider<GaoProductSkuService> skuServiceProvider;
private final ObjectProvider<GaoProductSpecService> specServiceProvider;
private final UserLoginService userLoginService;
private final LoggerService loggerService;
@Override
protected BaseMapper<GaoProduct, String> mapper() {
@@ -100,62 +99,56 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
return list.stream().filter(product -> !product.getSkuList().isEmpty()).toList();
}
@OperationAudit(AuditOperation.GAO_PRODUCT_CREATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(GaoProduct product) {
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CREATE", product, () -> {
prepare(product, null);
super.create(product);
Map<String, String> specValueIdMap = saveSpecs(product, product.getSpecList());
normalizeSkuSpecValueIds(product.getSkuList(), specValueIdMap);
saveSkus(product, product.getSkuList());
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList());
return product;
});
prepare(product, null);
super.create(product);
Map<String, String> specValueIdMap = saveSpecs(product, product.getSpecList());
normalizeSkuSpecValueIds(product.getSkuList(), specValueIdMap);
saveSkus(product, product.getSkuList());
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList());
}
@OperationAudit(AuditOperation.GAO_PRODUCT_UPDATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void update(GaoProduct request) {
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_UPDATE", request, () -> {
TimiException.required(request, "not found product");
TimiException.required(request.getId(), "not found product.id");
GaoProduct product = get(request.getId());
TimiException.required(product, "not found product");
product.setCategoryId(request.getCategoryId());
product.setCode(TimiJava.defaultIfNull(request.getCode(), product.getCode()));
product.setName(TimiJava.defaultIfNull(request.getName(), product.getName()));
product.setProductType(TimiJava.defaultIfNull(request.getProductType(), product.getProductType()));
product.setStockEnabled(TimiJava.defaultIfNull(request.getStockEnabled(), product.getStockEnabled()));
product.setStatus(TimiJava.defaultIfNull(request.getStatus(), product.getStatus()));
product.setSort(TimiJava.defaultIfNull(request.getSort(), product.getSort()));
product.setRemark(request.getRemark());
product.setSkuList(request.getSkuList() == null ? skuServiceProvider.getObject().listByProductId(product.getId()) : request.getSkuList());
prepare(product, product.getId());
super.update(product);
if (request.getSkuList() != null) {
saveSkus(product, request.getSkuList());
}
if (request.getAttachmentList() != null) {
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), request.getAttachmentList());
}
return product;
});
TimiException.required(request, "not found product");
TimiException.required(request.getId(), "not found product.id");
GaoProduct product = get(request.getId());
TimiException.required(product, "not found product");
product.setCategoryId(request.getCategoryId());
product.setCode(TimiJava.defaultIfNull(request.getCode(), product.getCode()));
product.setName(TimiJava.defaultIfNull(request.getName(), product.getName()));
product.setProductType(TimiJava.defaultIfNull(request.getProductType(), product.getProductType()));
product.setStockEnabled(TimiJava.defaultIfNull(request.getStockEnabled(), product.getStockEnabled()));
product.setStatus(TimiJava.defaultIfNull(request.getStatus(), product.getStatus()));
product.setSort(TimiJava.defaultIfNull(request.getSort(), product.getSort()));
product.setRemark(request.getRemark());
product.setSkuList(request.getSkuList() == null ? skuServiceProvider.getObject().listByProductId(product.getId()) : request.getSkuList());
prepare(product, product.getId());
super.update(product);
if (request.getSkuList() != null) {
saveSkus(product, request.getSkuList());
}
if (request.getAttachmentList() != null) {
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), request.getAttachmentList());
}
}
@OperationAudit(AuditOperation.GAO_PRODUCT_DISABLE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void disable(String id) {
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_DISABLE", id, () -> {
GaoProduct product = get(id);
TimiException.required(product, "not found product");
TimiException.requiredTrue(skuServiceProvider.getObject().listByProductId(id).stream().noneMatch(sku -> sku.getStatus() == com.imyeyu.api.modules.gao.entity.GaoProductSku.Status.ACTIVE), "请先停用产品的全部 SKU");
product.setStatus(GaoProduct.Status.INACTIVE);
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
product.setUpdatedAt(Time.now());
super.update(product);
return product;
});
GaoProduct product = get(id);
TimiException.required(product, "not found product");
TimiException.requiredTrue(skuServiceProvider.getObject().listByProductId(id).stream().noneMatch(sku -> sku.getStatus() == com.imyeyu.api.modules.gao.entity.GaoProductSku.Status.ACTIVE), "请先停用产品的全部 SKU");
product.setStatus(GaoProduct.Status.INACTIVE);
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
product.setUpdatedAt(Time.now());
super.update(product);
}
@Override
@@ -4,24 +4,24 @@ import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.gao.entity.GaoProduct;
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper;
import com.imyeyu.api.modules.gao.service.GaoProductService;
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
import com.imyeyu.api.modules.gao.vo.GaoProductStockProductView;
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
@@ -59,7 +59,6 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
private final GaoProductSkuService skuService;
private final GaoStoreScopeService storeScopeService;
private final UserLoginService userLoginService;
private final LoggerService loggerService;
@Override
public PageResult<GaoProductStock> pageByQuery(GaoProductStockPage page) {
@@ -112,40 +111,46 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
return stock;
}
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_INITIAL_IN)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoProductStock initialIn(GaoProductStockChangeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_INITIAL_IN", request, () -> change(request, GaoProductStockRecord.OperationType.INITIAL_IN));
return change(request, GaoProductStockRecord.OperationType.INITIAL_IN);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_MANUAL_IN)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoProductStock manualIn(GaoProductStockChangeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_IN", request, () -> change(request, GaoProductStockRecord.OperationType.MANUAL_IN));
return change(request, GaoProductStockRecord.OperationType.MANUAL_IN);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_MANUAL_OUT)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoProductStock manualOut(GaoProductStockChangeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_OUT", request, () -> change(request, GaoProductStockRecord.OperationType.MANUAL_OUT));
return change(request, GaoProductStockRecord.OperationType.MANUAL_OUT);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_MANUAL_REVOKE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoProductStock manualRevoke(GaoProductStockManualRevokeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_REVOKE", request, () -> manualRevokeInternal(request));
return manualRevokeInternal(request);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_STOCKTAKE_GAIN)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoProductStock stocktakeGain(GaoProductStockChangeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_GAIN", request, () -> change(request, GaoProductStockRecord.OperationType.STOCKTAKE_GAIN));
return change(request, GaoProductStockRecord.OperationType.STOCKTAKE_GAIN);
}
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_STOCKTAKE_LOSS)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoProductStock stocktakeLoss(GaoProductStockChangeRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_LOSS", request, () -> change(request, GaoProductStockRecord.OperationType.STOCKTAKE_LOSS));
return change(request, GaoProductStockRecord.OperationType.STOCKTAKE_LOSS);
}
@Override
@@ -216,7 +221,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
GaoStockChangeItem item = GaoStockChangeItem.of(product, sku, unit, quantity, baseQuantity);
item.setSkuSpecSnapshot(skuService.getSkuSpecSnapshot(sku));
List<GaoProductStock> result = executeInternal(storeId, sku.getId(), List.of(item), requestNo,
userLoginService.getRequireLoginUserId(), operationType, request.getRemark(), request.getAttachmentList());
userLoginService.getRequireLoginUserId(), operationType, request.getOccurredAt(), request.getRemark(), request.getAttachmentList());
return result.getFirst();
}
@@ -295,11 +300,11 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
private List<GaoProductStock> executeInternal(String storeId, String bizId, List<GaoStockChangeItem> sourceList, String requestNo,
String operatorUserId, GaoProductStockRecord.OperationType operationType, String remark) {
return executeInternal(storeId, bizId, sourceList, requestNo, operatorUserId, operationType, remark, null);
return executeInternal(storeId, bizId, sourceList, requestNo, operatorUserId, operationType, null, remark, null);
}
private List<GaoProductStock> executeInternal(String storeId, String bizId, List<GaoStockChangeItem> sourceList, String requestNo,
String operatorUserId, GaoProductStockRecord.OperationType operationType, String remark, List<Attachment> attachmentList) {
String operatorUserId, GaoProductStockRecord.OperationType operationType, Long occurredAt, String remark, List<Attachment> attachmentList) {
TimiException.required(storeId, "not found storeId");
TimiException.required(bizId, "not found stock bizId");
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "库存变更至少需要一个产品");
@@ -360,7 +365,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
record.setSourceRecordId(item.getSourceRecordId());
record.setRequestNo(normalizedRequestNo);
record.setOperatorUserId(operatorUserId);
record.setOccurredAt(Time.now());
record.setOccurredAt(TimiJava.defaultIfNull(occurredAt, Time.now()));
record.setRemark(remark);
recordService.createOperationRecord(record, attachmentList);
stock.setOnHandQuantity(onHandAfter);
@@ -34,8 +34,8 @@ import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderItemRequest;
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage;
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.system.audit.AuditOperation;
import com.imyeyu.api.modules.system.audit.OperationAudit;
import com.imyeyu.api.modules.user.service.PermissionChecker;
import com.imyeyu.api.modules.user.service.RoleChecker;
import com.imyeyu.api.modules.user.service.UserLoginService;
@@ -91,7 +91,6 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
private final PermissionChecker permissionChecker;
private final RoleChecker roleChecker;
private final UserLoginService userLoginService;
private final LoggerService loggerService;
@Override
protected BaseMapper<GaoSalesOrder, String> mapper() {
@@ -139,23 +138,23 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
return order;
}
@OperationAudit(AuditOperation.GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoSalesOrder createDirectProductOrder(GaoDirectProductOrderCreateRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE", request, () -> createDirectInternal(request));
return createDirectInternal(request);
}
@OperationAudit(AuditOperation.GAO_SALES_ORDER_UPDATE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoSalesOrder updateRemarkAndAttachments(GaoSalesOrderUpdateRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_UPDATE", request, () -> {
TimiException.required(request, "not found sales order update request");
TimiException.required(request.getId(), "not found sales order.id");
GaoSalesOrder order = getForUpdate(request.getId());
TimiException.requiredTrue(1 == mapper.updateRemark(order.getId(), request.getRemark()), "订单备注更新失败");
attachmentService.updateByBizId(Attachment.BizType.GAO_SALES_ORDER, order.getId(), request.getAttachmentList());
return detail(order.getId());
});
TimiException.required(request, "not found sales order update request");
TimiException.required(request.getId(), "not found sales order.id");
GaoSalesOrder order = getForUpdate(request.getId());
TimiException.requiredTrue(1 == mapper.updateRemark(order.getId(), request.getRemark()), "订单备注更新失败");
attachmentService.updateByBizId(Attachment.BizType.GAO_SALES_ORDER, order.getId(), request.getAttachmentList());
return detail(order.getId());
}
private GaoSalesOrder createDirectInternal(GaoDirectProductOrderCreateRequest request) {
@@ -222,10 +221,11 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
data.getSaleAmount(), data.getItemList());
}
@OperationAudit(AuditOperation.GAO_SALES_ORDER_CANCEL)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public GaoSalesOrder cancel(GaoSalesOrderCancelRequest request) {
return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_CANCEL", request, () -> cancelInternal(request));
return cancelInternal(request);
}
private GaoSalesOrder cancelInternal(GaoSalesOrderCancelRequest request) {
@@ -28,6 +28,9 @@ public class GaoProductStockChangeRequest {
/// 业务数量
private BigDecimal quantity;
/// 库存发生时间,毫秒时间戳
private Long occurredAt;
/// 备注
private String remark;
@@ -0,0 +1,66 @@
package com.imyeyu.api.modules.system.audit;
import com.imyeyu.api.modules.system.entity.Logger;
/// 操作审计编码
///
/// @author Codex
/// @since 2026-09-09
public enum AuditOperation {
GAO_CUSTOMER_PACKAGE_PURCHASE(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_PURCHASE"),
GAO_CUSTOMER_PACKAGE_CANCEL(Logger.Module.GAO, "GAO_CUSTOMER_PACKAGE_CANCEL"),
GAO_SALES_ORDER_PAYMENT_RECEIVE(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_RECEIVE"),
GAO_SALES_ORDER_PAYMENT_VERIFY(Logger.Module.GAO, "GAO_SALES_ORDER_PAYMENT_VERIFY"),
GAO_SALES_ORDER_REFUND(Logger.Module.GAO, "GAO_SALES_ORDER_REFUND"),
GAO_SALES_ORDER_CONFIRM_NO_REFUND(Logger.Module.GAO, "GAO_SALES_ORDER_CONFIRM_NO_REFUND"),
GAO_SALES_ORDER_FUND_REVOKE(Logger.Module.GAO, "GAO_SALES_ORDER_FUND_REVOKE"),
GAO_ORDER_FULFILLMENT_CREATE(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CREATE"),
GAO_ORDER_FULFILLMENT_PREPARE_STOCK(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_PREPARE_STOCK"),
GAO_ORDER_FULFILLMENT_VERIFY(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_VERIFY"),
GAO_ORDER_FULFILLMENT_REJECT(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REJECT"),
GAO_ORDER_FULFILLMENT_CANCEL(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_CANCEL"),
GAO_ORDER_FULFILLMENT_REVOKE(Logger.Module.GAO, "GAO_ORDER_FULFILLMENT_REVOKE"),
GAO_PACKAGE_CONFIG_CREATE(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_CREATE"),
GAO_PACKAGE_CONFIG_UPDATE(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_UPDATE"),
GAO_PACKAGE_CONFIG_DELETE(Logger.Module.GAO, "GAO_PACKAGE_CONFIG_DELETE"),
GAO_PRODUCT_CATEGORY_SORT(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_SORT"),
GAO_PRODUCT_CATEGORY_CREATE(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_CREATE"),
GAO_PRODUCT_CATEGORY_UPDATE(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_UPDATE"),
GAO_PRODUCT_CATEGORY_DISABLE(Logger.Module.GAO, "GAO_PRODUCT_CATEGORY_DISABLE"),
GAO_PRODUCT_CREATE(Logger.Module.GAO, "GAO_PRODUCT_CREATE"),
GAO_PRODUCT_UPDATE(Logger.Module.GAO, "GAO_PRODUCT_UPDATE"),
GAO_PRODUCT_DISABLE(Logger.Module.GAO, "GAO_PRODUCT_DISABLE"),
GAO_PRODUCT_STOCK_INITIAL_IN(Logger.Module.GAO, "GAO_PRODUCT_STOCK_INITIAL_IN"),
GAO_PRODUCT_STOCK_MANUAL_IN(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_IN"),
GAO_PRODUCT_STOCK_MANUAL_OUT(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_OUT"),
GAO_PRODUCT_STOCK_MANUAL_REVOKE(Logger.Module.GAO, "GAO_PRODUCT_STOCK_MANUAL_REVOKE"),
GAO_PRODUCT_STOCK_STOCKTAKE_GAIN(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_GAIN"),
GAO_PRODUCT_STOCK_STOCKTAKE_LOSS(Logger.Module.GAO, "GAO_PRODUCT_STOCK_STOCKTAKE_LOSS"),
GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE(Logger.Module.GAO, "GAO_SALES_ORDER_DIRECT_PRODUCT_CREATE"),
GAO_SALES_ORDER_UPDATE(Logger.Module.GAO, "GAO_SALES_ORDER_UPDATE"),
GAO_SALES_ORDER_CANCEL(Logger.Module.GAO, "GAO_SALES_ORDER_CANCEL");
private final Logger.Module module;
private final String code;
AuditOperation(Logger.Module module, String code) {
this.module = module;
this.code = code;
}
/// 获取日志模块
///
/// @return 日志模块
public Logger.Module getModule() {
return module;
}
/// 获取操作编码
///
/// @return 操作编码
public String getCode() {
return code;
}
}
@@ -0,0 +1,128 @@
package com.imyeyu.api.modules.system.audit;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.imyeyu.spring.entity.IDEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Array;
import java.util.Locale;
import java.util.Set;
/// 操作审计参数和结果序列化器
///
/// @author Codex
/// @since 2026-09-09
@Component
@RequiredArgsConstructor
public class AuditPayloadSerializer {
private static final int MAX_LENGTH = 32 * 1024;
private static final Set<String> SECRET_FIELD_SET = Set.of(
"password",
"passwordconfirm",
"token",
"accesstoken",
"refreshtoken",
"secret",
"credential",
"authorization"
);
private final ObjectMapper objectMapper;
/// 序列化方法参数
///
/// @param arguments 方法参数
/// @return 脱敏后的参数 JSON
public String serializeArguments(Object[] arguments) {
if (arguments == null || 0 == arguments.length) {
return null;
}
return serialize(arguments.length == 1 ? arguments[0] : arguments);
}
/// 序列化业务结果
///
/// @param result 方法返回值
/// @param arguments 方法参数
/// @return 结果摘要
public String serializeResult(Object result, Object[] arguments) {
if (result instanceof IDEntity<?> entity && entity.getId() != null) {
return truncate(String.valueOf(entity.getId()));
}
if (result != null) {
return summarize(result);
}
if (arguments != null) {
for (Object argument : arguments) {
if (argument instanceof IDEntity<?> entity && entity.getId() != null) {
return truncate(String.valueOf(entity.getId()));
}
}
if (1 == arguments.length && arguments[0] instanceof CharSequence value) {
return truncate(value.toString());
}
}
return null;
}
private String serialize(Object value) {
try {
JsonNode node = objectMapper.valueToTree(value);
mask(node);
return truncate(objectMapper.writeValueAsString(node));
} catch (Exception e) {
return value == null ? null : truncate(value.getClass().getName());
}
}
private void mask(JsonNode node) {
if (node == null) {
return;
}
if (node.isObject()) {
ObjectNode objectNode = (ObjectNode) node;
objectNode.fieldNames().forEachRemaining(fieldName -> {
JsonNode field = objectNode.get(fieldName);
if (SECRET_FIELD_SET.contains(fieldName.toLowerCase(Locale.ROOT))) {
objectNode.put(fieldName, "***");
} else {
mask(field);
}
});
} else if (node.isArray()) {
node.forEach(this::mask);
}
}
private String summarize(Object value) {
if (value instanceof CharSequence || value instanceof Number || value instanceof Boolean || value instanceof Enum<?>) {
return truncate(String.valueOf(value));
}
if (value instanceof Iterable<?> iterable) {
int size = 0;
for (Object ignored : iterable) {
size++;
}
return "%s[%s]".formatted(value.getClass().getSimpleName(), size);
}
if (value instanceof java.util.Map<?, ?> map) {
return "%s[%s]".formatted(value.getClass().getSimpleName(), map.size());
}
if (value.getClass().isArray()) {
return "%s[%s]".formatted(value.getClass().getComponentType().getSimpleName(), Array.getLength(value));
}
return value.getClass().getSimpleName();
}
private String truncate(String value) {
if (value == null || value.length() <= MAX_LENGTH) {
return value;
}
return value.substring(0, MAX_LENGTH) + "...";
}
}
@@ -0,0 +1,17 @@
package com.imyeyu.api.modules.system.audit;
import com.imyeyu.api.modules.system.entity.Logger;
/// 操作审计记录器
///
/// 业务切面只依赖该写入端口,不直接依赖日志查询服务
///
/// @author Codex
/// @since 2026-09-09
public interface AuditRecorder {
/// 记录操作审计
///
/// @param logger 审计记录
void record(Logger logger);
}
@@ -0,0 +1,22 @@
package com.imyeyu.api.modules.system.audit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/// 标记需要记录操作审计的应用服务方法
///
/// 只应标记对外的业务操作入口,不要标记被入口调用的内部方法
///
/// @author Codex
/// @since 2026-09-09
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationAudit {
/// 操作编码
///
/// @return 操作编码
AuditOperation value();
}
@@ -0,0 +1,90 @@
package com.imyeyu.api.modules.system.audit;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.java.bean.timi.TimiException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/// 操作审计切面
///
/// 该切面放在事务拦截器外层,确保成功日志只在事务提交成功后生成
///
/// @author Codex
/// @since 2026-09-09
@Aspect
@Component
@Order(Ordered.LOWEST_PRECEDENCE - 100)
@RequiredArgsConstructor
@Slf4j
public class OperationAuditAspect {
private final AuditRecorder auditRecorder;
private final AuditPayloadSerializer payloadSerializer;
/// 执行并记录操作审计
///
/// @param joinPoint 连接点
/// @param operationAudit 审计配置
/// @return 业务返回值
/// @throws Throwable 业务异常
@Around("@annotation(operationAudit)")
public Object around(ProceedingJoinPoint joinPoint, OperationAudit operationAudit) throws Throwable {
AuditOperation operation = operationAudit.value();
Object[] arguments = joinPoint.getArgs();
Logger logger = new Logger(operation.getModule(), operation.getCode());
logger.setContent(payloadSerializer.serializeArguments(arguments));
long startNanos = System.nanoTime();
try {
Object result = joinPoint.proceed();
logger.setLevel(Logger.Level.INFO);
logger.setResult(payloadSerializer.serializeResult(result, arguments));
logger.setDuration(duration(startNanos));
record(logger);
return result;
} catch (TimiException e) {
logger.setLevel(Logger.Level.WARN);
logger.setException(e.getMessage());
logger.setDuration(duration(startNanos));
record(logger);
throw e;
} catch (Throwable e) {
logger.setLevel(Logger.Level.ERROR);
logger.setException(serializeThrowable(e));
logger.setDuration(duration(startNanos));
log.error("业务操作失败: {}", operation.getCode(), e);
record(logger);
throw e;
}
}
private void record(Logger logger) {
try {
auditRecorder.record(logger);
} catch (Exception e) {
log.error("记录操作审计失败: {}", logger.getOperation(), e);
}
}
private int duration(long startNanos) {
long millis = (System.nanoTime() - startNanos) / 1_000_000L;
if (Integer.MAX_VALUE < millis) {
return Integer.MAX_VALUE;
}
return (int) Math.max(0L, millis);
}
private String serializeThrowable(Throwable throwable) {
java.io.StringWriter stringWriter = new java.io.StringWriter();
try (java.io.PrintWriter printWriter = new java.io.PrintWriter(stringWriter)) {
throwable.printStackTrace(printWriter);
}
return stringWriter.toString();
}
}
@@ -6,8 +6,6 @@ import com.imyeyu.spring.service.DestroyableService;
import com.imyeyu.spring.service.GettableService;
import com.imyeyu.spring.service.PageableService;
import java.util.function.Supplier;
/**
* 日志服务
*
@@ -18,51 +16,6 @@ public interface LoggerService extends GettableService<Logger, String>, Pageable
void createSync(Logger logger);
/// 执行并记录有返回值的业务操作
///
/// @param logger 日志对象,调用方负责设置模块、操作和请求内容
/// @param action 业务操作
/// @param <T> 返回值类型
/// @return 业务操作返回值
<T> T execute(Logger logger, Supplier<T> action);
/// 执行并记录无返回值的业务操作
///
/// @param logger 日志对象,调用方负责设置模块、操作和请求内容
/// @param action 业务操作
default void execute(Logger logger, Runnable action) {
execute(logger, () -> {
action.run();
return null;
});
}
/// 创建日志并执行有返回值的业务操作
///
/// @param module 日志模块
/// @param operation 操作描述
/// @param content 请求内容
/// @param action 业务操作
/// @param <T> 返回值类型
/// @return 业务操作返回值
default <T> T execute(Logger.Module module, String operation, Object content, Supplier<T> action) {
Logger logger = new Logger(module, operation);
logger.setContent(content == null ? null : content.toString());
return execute(logger, action);
}
/// 创建日志并执行无返回值的业务操作
///
/// @param module 日志模块
/// @param operation 操作描述
/// @param content 请求内容
/// @param action 业务操作
default void execute(Logger.Module module, String operation, Object content, Runnable action) {
Logger logger = new Logger(module, operation);
logger.setContent(content == null ? null : content.toString());
execute(logger, action);
}
void create(Logger.Module module, Logger.Level level, String operation, String content, String result);
default void info(Logger.Module module, String operation) {
@@ -1,37 +1,41 @@
package com.imyeyu.api.modules.system.service.implement;
import com.imyeyu.api.modules.system.entity.Logger;
import com.imyeyu.api.modules.system.audit.AuditRecorder;
import com.imyeyu.api.modules.system.mapper.LoggerMapper;
import com.imyeyu.api.modules.system.service.LoggerService;
import com.imyeyu.api.modules.user.entity.User;
import com.imyeyu.api.modules.user.service.UserLoginService;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.TimiSpring;
import com.imyeyu.spring.entity.IDEntity;
import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.service.AbstractEntityService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.util.function.Supplier;
/**
* 日志服务实现
*
* @author 夜雨
* @since 2026-05-13 14:46
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class LoggerServiceImplement extends AbstractEntityService<Logger, String> implements LoggerService {
public class LoggerServiceImplement extends AbstractEntityService<Logger, String> implements LoggerService, AuditRecorder {
private final UserLoginService userLoginService;
private final LoggerMapper mapper;
private final ThreadPoolTaskExecutor auditTaskExecutor;
public LoggerServiceImplement(UserLoginService userLoginService, LoggerMapper mapper,
@Qualifier("auditTaskExecutor") ThreadPoolTaskExecutor auditTaskExecutor) {
this.userLoginService = userLoginService;
this.mapper = mapper;
this.auditTaskExecutor = auditTaskExecutor;
}
@Override
protected BaseMapper<Logger, String> mapper() {
return mapper;
@@ -39,9 +43,13 @@ public class LoggerServiceImplement extends AbstractEntityService<Logger, String
@Override
public void create(Logger logger) {
// 默认异步
fillContext(logger);
Thread.startVirtualThread(() -> createSync(logger));
auditTaskExecutor.execute(() -> createSync(logger));
}
@Override
public void record(Logger logger) {
create(logger);
}
@Override
@@ -50,32 +58,6 @@ public class LoggerServiceImplement extends AbstractEntityService<Logger, String
super.create(logger);
}
@Override
public <T> T execute(Logger logger, Supplier<T> action) {
try {
T result = action.get();
logger.setLevel(Logger.Level.INFO);
if (result instanceof IDEntity<?> entity) {
logger.setResult(String.valueOf(entity.getId()));
} else if (result != null) {
logger.setResult(result.toString());
}
create(logger);
return result;
} catch (TimiException e) {
logger.setLevel(Logger.Level.WARN);
logger.setException(e.getMessage());
create(logger);
throw e;
} catch (RuntimeException e) {
logger.setLevel(Logger.Level.ERROR);
logger.setException(TimiJava.serializeThrowable(e));
log.error("业务操作失败: {}", logger.getOperation(), e);
create(logger);
throw e;
}
}
public void create(Logger.Module module, Logger.Level level, String operation, String content, String result) {
Logger log = new Logger(module, level, operation);
log.setContent(content);