Commit c74881c8f0c9214515cce672ed62a60ade91a968
1 parent
13d6c42a
系统操作日志:集成 mzt-biz-log 3
Showing
11 changed files
with
145 additions
and
94 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
| ... | ... | @@ -7,12 +7,21 @@ package cn.iocoder.yudao.module.crm.enums; |
| 7 | 7 | */ |
| 8 | 8 | public interface LogRecordConstants { |
| 9 | 9 | |
| 10 | - String WHO = "【{getAdminUserById{#userId}}】"; | |
| 10 | + //======================= 客户模块类型 ======================= | |
| 11 | + // TODO puhui999: 确保模块命名方式为 module + 子模块名称的方式。统一定义模块名称是为了方便查询各自记录的操作日志,列如说:查询客户【张三的操作日志】就可以 module + bizId | |
| 12 | + String CRM_LEADS = "CRM-线索"; | |
| 13 | + String CRM_CUSTOMER = "CRM-客户"; | |
| 14 | + String CRM_CONTACT = "CRM-联系人"; | |
| 15 | + String CRM_BUSINESS = "CRM-商机"; | |
| 16 | + String CRM_CONTRACT = "CRM-合同"; | |
| 17 | + String CRM_PRODUCT = "CRM-产品"; | |
| 18 | + String CRM_RECEIVABLE = "CRM-回款"; | |
| 19 | + String CRM_RECEIVABLE_PLAN = "CRM-回款计划"; | |
| 11 | 20 | |
| 12 | 21 | //======================= 客户转移操作日志 ======================= |
| 13 | 22 | |
| 14 | - String TRANSFER_CUSTOMER_LOG_TYPE = "客户转移"; | |
| 15 | - String TRANSFER_CUSTOMER_LOG_SUCCESS = WHO + "把客户【{{#crmCustomer.name}}】负责人【{getAdminUserById{#crmCustomer.ownerUserId}}】转移给了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; | |
| 23 | + String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer == null ? '' : #crmCustomer.name}}】负责人从" + | |
| 24 | + "【{getAdminUserById{#crmCustomer == null ? '' : #crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; | |
| 16 | 25 | String TRANSFER_CUSTOMER_LOG_FAIL = ""; |
| 17 | 26 | |
| 18 | 27 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
| ... | ... | @@ -123,7 +123,6 @@ public class CrmCustomerController { |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | 125 | @PutMapping("/transfer") |
| 126 | - @Operation(summary = "客户转移") | |
| 127 | 126 | @PreAuthorize("@ss.hasPermission('crm:customer:update')") |
| 128 | 127 | public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { |
| 129 | 128 | customerService.transferCustomer(reqVO, getLoginUserId()); | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
| ... | ... | @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerUpdat |
| 9 | 9 | import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; |
| 10 | 10 | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| 11 | 11 | import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; |
| 12 | -import cn.iocoder.yudao.module.crm.enums.LogRecordConstants; | |
| 13 | 12 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| 14 | 13 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 15 | 14 | import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; |
| ... | ... | @@ -27,6 +26,8 @@ import java.util.*; |
| 27 | 26 | |
| 28 | 27 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 29 | 28 | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; |
| 29 | +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER; | |
| 30 | +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS; | |
| 30 | 31 | import static java.util.Collections.singletonList; |
| 31 | 32 | |
| 32 | 33 | /** |
| ... | ... | @@ -129,8 +130,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 129 | 130 | |
| 130 | 131 | @Override |
| 131 | 132 | @Transactional(rollbackFor = Exception.class) |
| 132 | - @LogRecord(success = LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS, | |
| 133 | - type = LogRecordConstants.TRANSFER_CUSTOMER_LOG_TYPE, bizNo = "{{#reqVO.id}}") | |
| 133 | + @LogRecord(success = TRANSFER_CUSTOMER_LOG_SUCCESS, type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}") | |
| 134 | 134 | @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) |
| 135 | 135 | public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { |
| 136 | 136 | // 1. 校验客户是否存在 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java
| 1 | 1 | package cn.iocoder.yudao.module.system.dal.dataobject.logger; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 4 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 6 | -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum; | |
| 7 | 5 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 8 | -import com.baomidou.mybatisplus.annotation.TableField; | |
| 9 | 6 | import com.baomidou.mybatisplus.annotation.TableId; |
| 10 | 7 | import com.baomidou.mybatisplus.annotation.TableName; |
| 11 | -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | |
| 12 | 8 | import lombok.Data; |
| 13 | 9 | import lombok.EqualsAndHashCode; |
| 14 | 10 | |
| 15 | -import java.time.LocalDateTime; | |
| 16 | -import java.util.Map; | |
| 17 | - | |
| 18 | 11 | /** |
| 19 | - * 操作日志表 | |
| 12 | + * 操作日志表 V2 | |
| 20 | 13 | * |
| 21 | 14 | * @author 芋道源码 |
| 22 | 15 | */ |
| ... | ... | @@ -27,16 +20,6 @@ import java.util.Map; |
| 27 | 20 | public class OperateLogV2DO extends BaseDO { |
| 28 | 21 | |
| 29 | 22 | /** |
| 30 | - * {@link #javaMethodArgs} 的最大长度 | |
| 31 | - */ | |
| 32 | - public static final Integer JAVA_METHOD_ARGS_MAX_LENGTH = 8000; | |
| 33 | - | |
| 34 | - /** | |
| 35 | - * {@link #resultData} 的最大长度 | |
| 36 | - */ | |
| 37 | - public static final Integer RESULT_MAX_LENGTH = 4000; | |
| 38 | - | |
| 39 | - /** | |
| 40 | 23 | * 日志主键 |
| 41 | 24 | */ |
| 42 | 25 | @TableId |
| ... | ... | @@ -68,22 +51,14 @@ public class OperateLogV2DO extends BaseDO { |
| 68 | 51 | */ |
| 69 | 52 | private String name; |
| 70 | 53 | /** |
| 71 | - * 操作分类 | |
| 72 | - * | |
| 73 | - * 枚举 {@link OperateTypeEnum} | |
| 54 | + * 操作模块业务编号 | |
| 74 | 55 | */ |
| 75 | - private Integer type; | |
| 56 | + private Long bizId; | |
| 76 | 57 | /** |
| 77 | 58 | * 操作内容,记录整个操作的明细 |
| 78 | 59 | * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 |
| 79 | 60 | */ |
| 80 | 61 | private String content; |
| 81 | - /** | |
| 82 | - * 拓展字段,有些复杂的业务,需要记录一些字段 | |
| 83 | - * 例如说,记录订单编号,则可以添加 key 为 "orderId",value 为订单编号 | |
| 84 | - */ | |
| 85 | - @TableField(typeHandler = JacksonTypeHandler.class) | |
| 86 | - private Map<String, Object> exts; | |
| 87 | 62 | |
| 88 | 63 | /** |
| 89 | 64 | * 请求方法名 |
| ... | ... | @@ -102,43 +77,4 @@ public class OperateLogV2DO extends BaseDO { |
| 102 | 77 | */ |
| 103 | 78 | private String userAgent; |
| 104 | 79 | |
| 105 | - /** | |
| 106 | - * Java 方法名 | |
| 107 | - */ | |
| 108 | - private String javaMethod; | |
| 109 | - /** | |
| 110 | - * Java 方法的参数 | |
| 111 | - * | |
| 112 | - * 实际格式为 Map<String, Object> | |
| 113 | - * 不使用 @TableField(typeHandler = FastjsonTypeHandler.class) 注解的原因是,数据库存储有长度限制,会进行裁剪,会导致 JSON 反序列化失败 | |
| 114 | - * 其中,key 为参数名,value 为参数值 | |
| 115 | - */ | |
| 116 | - private String javaMethodArgs; | |
| 117 | - /** | |
| 118 | - * 开始时间 | |
| 119 | - */ | |
| 120 | - private LocalDateTime startTime; | |
| 121 | - /** | |
| 122 | - * 执行时长,单位:毫秒 | |
| 123 | - */ | |
| 124 | - private Integer duration; | |
| 125 | - /** | |
| 126 | - * 结果码 | |
| 127 | - * | |
| 128 | - * 目前使用的 {@link CommonResult#getCode()} 属性 | |
| 129 | - */ | |
| 130 | - private Integer resultCode; | |
| 131 | - /** | |
| 132 | - * 结果提示 | |
| 133 | - * | |
| 134 | - * 目前使用的 {@link CommonResult#getMsg()} 属性 | |
| 135 | - */ | |
| 136 | - private String resultMsg; | |
| 137 | - /** | |
| 138 | - * 结果数据 | |
| 139 | - * | |
| 140 | - * 如果是对象,则使用 JSON 格式化 | |
| 141 | - */ | |
| 142 | - private String resultData; | |
| 143 | - | |
| 144 | 80 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java
| 1 | 1 | package cn.iocoder.yudao.module.system.dal.mysql.logger; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| ... | ... | @@ -16,14 +15,7 @@ public interface OperateLogV2Mapper extends BaseMapperX<OperateLogV2DO> { |
| 16 | 15 | default PageResult<OperateLogV2DO> selectPage(OperateLogPageReqVO reqVO, Collection<Long> userIds) { |
| 17 | 16 | LambdaQueryWrapperX<OperateLogV2DO> query = new LambdaQueryWrapperX<OperateLogV2DO>() |
| 18 | 17 | .likeIfPresent(OperateLogV2DO::getModule, reqVO.getModule()) |
| 19 | - .inIfPresent(OperateLogV2DO::getUserId, userIds) | |
| 20 | - .eqIfPresent(OperateLogV2DO::getType, reqVO.getType()) | |
| 21 | - .betweenIfPresent(OperateLogV2DO::getStartTime, reqVO.getStartTime()); | |
| 22 | - if (Boolean.TRUE.equals(reqVO.getSuccess())) { | |
| 23 | - query.eq(OperateLogV2DO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode()); | |
| 24 | - } else if (Boolean.FALSE.equals(reqVO.getSuccess())) { | |
| 25 | - query.gt(OperateLogV2DO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode()); | |
| 26 | - } | |
| 18 | + .inIfPresent(OperateLogV2DO::getUserId, userIds); | |
| 27 | 19 | query.orderByDesc(OperateLogV2DO::getId); // 降序 |
| 28 | 20 | return selectPage(reqVO, query); |
| 29 | 21 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/config/YudaoOperateLogV2Configuration.java
| ... | ... | @@ -3,14 +3,13 @@ package cn.iocoder.yudao.module.system.framework.bizlog.config; |
| 3 | 3 | import com.mzt.logapi.starter.annotation.EnableLogRecord; |
| 4 | 4 | import org.springframework.context.annotation.Configuration; |
| 5 | 5 | |
| 6 | - | |
| 7 | 6 | /** |
| 8 | - * | |
| 7 | + * mzt-biz-log 配置类 | |
| 9 | 8 | * |
| 10 | 9 | * @author HUIHUI |
| 11 | 10 | */ |
| 12 | 11 | @Configuration(proxyBeanMethods = false) |
| 13 | -@EnableLogRecord(tenant = "${yudao.info.base-package}") | |
| 12 | +@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦 | |
| 14 | 13 | public class YudaoOperateLogV2Configuration { |
| 15 | 14 | |
| 16 | 15 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AdminUserParseFunction.java
| ... | ... | @@ -34,11 +34,11 @@ public class AdminUserParseFunction implements IParseFunction { |
| 34 | 34 | @Override |
| 35 | 35 | public String apply(Object value) { |
| 36 | 36 | if (value == null) { |
| 37 | - log.warn("(getAdminUserById) 解析异常参数为 null"); | |
| 37 | + //log.warn("(getAdminUserById) 解析异常参数为 null"); | |
| 38 | 38 | return ""; |
| 39 | 39 | } |
| 40 | 40 | if (StrUtil.isEmpty(value.toString())) { |
| 41 | - log.warn("(getAdminUserById) 解析异常参数为空"); | |
| 41 | + //log.warn("(getAdminUserById) 解析异常参数为空"); | |
| 42 | 42 | return ""; |
| 43 | 43 | } |
| 44 | 44 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.system.framework.bizlog.service; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | |
| 5 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | |
| 3 | 6 | import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; |
| 7 | +import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO; | |
| 4 | 8 | import com.mzt.logapi.beans.LogRecord; |
| 5 | 9 | import com.mzt.logapi.service.ILogRecordService; |
| 6 | 10 | import jakarta.annotation.Resource; |
| 11 | +import jakarta.servlet.http.HttpServletRequest; | |
| 7 | 12 | import lombok.extern.slf4j.Slf4j; |
| 8 | 13 | import org.springframework.stereotype.Service; |
| 9 | 14 | |
| ... | ... | @@ -26,7 +31,42 @@ public class ILogRecordServiceImpl implements ILogRecordService { |
| 26 | 31 | |
| 27 | 32 | @Override |
| 28 | 33 | public void record(LogRecord logRecord) { |
| 29 | - log.info("【logRecord】log={}", logRecord); | |
| 34 | + OperateLogV2CreateReqBO reqBO = new OperateLogV2CreateReqBO(); | |
| 35 | + // 补全通用字段 | |
| 36 | + reqBO.setTraceId(TracerUtils.getTraceId()); | |
| 37 | + // 补充用户信息 | |
| 38 | + fillUserFields(reqBO); | |
| 39 | + // 补全模块信息 | |
| 40 | + fillModuleFields(reqBO, logRecord); | |
| 41 | + // 补全请求信息 | |
| 42 | + fillRequestFields(reqBO); | |
| 43 | + // 异步记录日志 | |
| 44 | + log.info("操作日志 ===> {}", reqBO); | |
| 45 | + } | |
| 46 | + | |
| 47 | + private static void fillUserFields(OperateLogV2CreateReqBO reqBO) { | |
| 48 | + reqBO.setUserId(WebFrameworkUtils.getLoginUserId()); | |
| 49 | + reqBO.setUserType(WebFrameworkUtils.getLoginUserType()); | |
| 50 | + } | |
| 51 | + | |
| 52 | + public static void fillModuleFields(OperateLogV2CreateReqBO reqBO, LogRecord logRecord) { | |
| 53 | + reqBO.setModule(logRecord.getType()); // 大模块类型如 crm-客户 | |
| 54 | + reqBO.setName(logRecord.getSubType());// 操作名称如 转移客户 | |
| 55 | + reqBO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号 | |
| 56 | + reqBO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 | |
| 57 | + } | |
| 58 | + | |
| 59 | + private static void fillRequestFields(OperateLogV2CreateReqBO reqBO) { | |
| 60 | + // 获得 Request 对象 | |
| 61 | + HttpServletRequest request = ServletUtils.getRequest(); | |
| 62 | + if (request == null) { | |
| 63 | + return; | |
| 64 | + } | |
| 65 | + // 补全请求信息 | |
| 66 | + reqBO.setRequestMethod(request.getMethod()); | |
| 67 | + reqBO.setRequestUrl(request.getRequestURI()); | |
| 68 | + reqBO.setUserIp(ServletUtils.getClientIP(request)); | |
| 69 | + reqBO.setUserAgent(ServletUtils.getUserAgent(request)); | |
| 30 | 70 | } |
| 31 | 71 | |
| 32 | 72 | @Override |
| ... | ... | @@ -38,4 +78,5 @@ public class ILogRecordServiceImpl implements ILogRecordService { |
| 38 | 78 | public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) { |
| 39 | 79 | return Collections.emptyList(); |
| 40 | 80 | } |
| 81 | + | |
| 41 | 82 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
| ... | ... | @@ -53,8 +53,6 @@ public class OperateLogServiceImpl implements OperateLogService { |
| 53 | 53 | @Override |
| 54 | 54 | public void createOperateLogV2(OperateLogCreateReqDTO createReqDTO) { |
| 55 | 55 | OperateLogV2DO log = BeanUtils.toBean(createReqDTO, OperateLogV2DO.class); |
| 56 | - log.setJavaMethodArgs(StrUtils.maxLength(log.getJavaMethodArgs(), JAVA_METHOD_ARGS_MAX_LENGTH)); | |
| 57 | - log.setResultData(StrUtils.maxLength(log.getResultData(), RESULT_MAX_LENGTH)); | |
| 58 | 56 | operateLogV2Mapper.insert(log); |
| 59 | 57 | } |
| 60 | 58 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.service.logger.bo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | |
| 4 | +import jakarta.validation.constraints.NotEmpty; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 系统操作日志 Create Req BO | |
| 9 | + * | |
| 10 | + * @author HUIHUI | |
| 11 | + */ | |
| 12 | +@Data | |
| 13 | +public class OperateLogV2CreateReqBO { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * 链路追踪编号 | |
| 17 | + * | |
| 18 | + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 | |
| 19 | + */ | |
| 20 | + private String traceId; | |
| 21 | + /** | |
| 22 | + * 用户编号 | |
| 23 | + * | |
| 24 | + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 | |
| 25 | + */ | |
| 26 | + @NotEmpty(message = "用户编号不能为空") | |
| 27 | + private Long userId; | |
| 28 | + /** | |
| 29 | + * 用户类型 | |
| 30 | + * | |
| 31 | + * 关联 {@link UserTypeEnum} | |
| 32 | + */ | |
| 33 | + @NotEmpty(message = "用户类型不能为空") | |
| 34 | + private Integer userType; | |
| 35 | + /** | |
| 36 | + * 操作模块 | |
| 37 | + */ | |
| 38 | + @NotEmpty(message = "操作模块不能为空") | |
| 39 | + private String module; | |
| 40 | + /** | |
| 41 | + * 操作名 | |
| 42 | + */ | |
| 43 | + @NotEmpty(message = "操作名不能为空") | |
| 44 | + private String name; | |
| 45 | + /** | |
| 46 | + * 操作模块业务编号 | |
| 47 | + */ | |
| 48 | + @NotEmpty(message = "操作模块业务编号不能为空") | |
| 49 | + private Long bizId; | |
| 50 | + /** | |
| 51 | + * 操作内容,记录整个操作的明细 | |
| 52 | + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 | |
| 53 | + */ | |
| 54 | + @NotEmpty(message = "操作内容不能为空") | |
| 55 | + private String content; | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * 请求方法名 | |
| 59 | + */ | |
| 60 | + @NotEmpty(message = "请求方法名不能为空") | |
| 61 | + private String requestMethod; | |
| 62 | + /** | |
| 63 | + * 请求地址 | |
| 64 | + */ | |
| 65 | + @NotEmpty(message = "请求地址不能为空") | |
| 66 | + private String requestUrl; | |
| 67 | + /** | |
| 68 | + * 用户 IP | |
| 69 | + */ | |
| 70 | + @NotEmpty(message = "用户 IP 不能为空") | |
| 71 | + private String userIp; | |
| 72 | + /** | |
| 73 | + * 浏览器 UA | |
| 74 | + */ | |
| 75 | + @NotEmpty(message = "浏览器 UA 不能为空") | |
| 76 | + private String userAgent; | |
| 77 | + | |
| 78 | +} | ... | ... |