提交 ca9b9edc authored 作者: 窦馨雨's avatar 窦馨雨

合并分支 'dxy' 到 'master'

增加用户操作日志 查看合并请求 !118
......@@ -279,8 +279,15 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>
<!-- Dynamic DataSource -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
</dependencies>
<build>
<finalName>wangxiaolu-promotion-service</finalName>
<plugins>
......
package com.wangxiaolu.promotion.annotation;
import com.wangxiaolu.promotion.enums.log.BusinessType;
import com.wangxiaolu.promotion.enums.log.OperatorType;
import java.lang.annotation.*;
/**
* 自定义操作日志记录注解
*
* @author ruoyi
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}
package com.wangxiaolu.promotion.aspect;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
import com.wangxiaolu.promotion.annotation.Log;
import com.wangxiaolu.promotion.domain.log.dao.ISysOperLogDao;
import com.wangxiaolu.promotion.domain.log.mapper.entity.SysOperLog;
import com.wangxiaolu.promotion.enums.log.BusinessStatus;
import com.wangxiaolu.promotion.utils.AuthUtils;
import com.wangxiaolu.promotion.utils.ServletUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
/**
* 操作日志记录处理(@Log 注解切面实现)
* 与原版 RuoYi LogAspect 功能一致,依赖替换为项目现有类
*/
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"};
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<>("Cost Time");
@Autowired
private ISysOperLogDao sysOperLogDao;
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog) {
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
handleLog(joinPoint, controllerLog, null, jsonResult);
}
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
handleLog(joinPoint, controllerLog, e, null);
}
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try {
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 获取 IP(自己实现 getIpAddr 逻辑)
String ip = getIpAddr(ServletUtils.getRequest());
operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
// 获取当前用户名(从 token 中解析,需根据实际认证方式调整)
try {
// 如果已有 SecurityUtils 可替换此处;当前用 AuthUtils 解析 token
String token = AuthUtils.getToken();
if (StringUtils.isNotBlank(token)) {
String userName = AuthUtils.getUserName(token);
operLog.setOperName(userName);
}
} catch (Exception ex) {
log.debug("获取用户名失败: {}", ex.getMessage());
}
if (e != null) {
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// 设置请求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间
Long startTime = TIME_THREADLOCAL.get();
if (startTime != null) {
operLog.setCostTime(System.currentTimeMillis() - startTime);
}
// 操作时间
operLog.setOperTime(new Date());
// 保存数据库
sysOperLogDao.insertOperlog(operLog);
} catch (Exception exp) {
log.error("操作日志记录异常: {}", exp.getMessage(), exp);
} finally {
TIME_THREADLOCAL.remove();
}
}
/**
* 获取注解中对方法的描述信息(用于 Controller 层注解)
*/
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception {
// 设置业务类型
operLog.setBusinessType(log.businessType().ordinal());
// 设置标题
operLog.setTitle(log.title());
// 设置操作人类别
operLog.setOperatorType(log.operatorType().ordinal());
// 是否需要保存请求参数
if (log.isSaveRequestData()) {
setRequestValue(joinPoint, operLog, log.excludeParamNames());
}
// 是否需要保存响应结果
if (log.isSaveResponseData() && jsonResult != null) {
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
}
}
/**
* 获取请求的参数,放到 log 中
*/
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception {
String requestMethod = operLog.getRequestMethod();
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
if ((paramsMap == null || paramsMap.isEmpty())
&& (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
{
// PUT/POST 且没有 query 参数 → 从 body 中获取
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
} else {
// 有 query 参数 → 序列化 query 参数(排除敏感字段)
operLog.setOperParam(StringUtils.substring(
JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
}
}
/**
* 参数拼装(排除敏感属性和框架对象)
*/
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
{
for (Object o : paramsArray)
{
if (o != null && !isFilterObject(o))
{
try
{
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
params += jsonObj + " ";
}
catch (Exception e)
{
}
}
}
}
return params.trim();
}
/**
* 排除敏感属性的 Fastjson 过滤器
*/
public SimplePropertyPreFilter excludePropertyPreFilter(String[] excludeParamNames) {
SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
for (String exclude : ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)) {
filter.getExcludes().add(exclude);
}
return filter;
}
/**
* 判断是否需要过滤的对象(MultipartFile、Request、Response、BindingResult)
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
/**
* 获取 IP 地址(原版 IpUtils.getIpAddr 的实现)
*/
private String getIpAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 多个代理时取第一个 IP
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
}
\ No newline at end of file
package com.wangxiaolu.promotion.controller.activity.manage.tem;
import com.wangxiaolu.promotion.annotation.Log;
import com.wangxiaolu.promotion.enums.log.BusinessType;
import com.wangxiaolu.promotion.pojo.PageInfo;
import com.wangxiaolu.promotion.result.basedata.R;
import com.wangxiaolu.promotion.service.activity.manage.EmployeeQueryTemActivityService;
......@@ -26,6 +28,7 @@ public class EmployeeQueryTemActivityController {
* @param loginQcId 员工
* @return 活动数据
*/
@Log(title = "CP任务",businessType = BusinessType.QUERY)
@PostMapping("/page")
public R findActivitys(String loginQcId, @RequestBody PageInfo pageInfo) {
employeeQueryTemActivityService.getEmployeeApproveActivityPage(loginQcId, pageInfo);
......
package com.wangxiaolu.promotion.controller.activityplanv2;
import com.alibaba.fastjson2.JSONObject;
import com.wangxiaolu.promotion.annotation.Log;
import com.wangxiaolu.promotion.common.excel.FileUtils;
import com.wangxiaolu.promotion.enums.log.BusinessType;
import com.wangxiaolu.promotion.exception.DataException;
import com.wangxiaolu.promotion.exception.ParamException;
import com.wangxiaolu.promotion.pojo.activity.manage.vo.ActivityPlanVo;
......@@ -45,6 +47,7 @@ public class PromPlanCoreController {
* 城市经理 - 上传计划(新增)
* 当月只能上传次月的新增(当月需要新增需要交由职能角色上传)
*/
@Log(title = "CP计划", businessType = BusinessType.INSERT)
@PostMapping("/self/upload")
public R selfPlan(@RequestBody ActivityPlanVo activityPlanVo) {
// 判断当前账号是否是城市经理
......@@ -82,6 +85,8 @@ public class PromPlanCoreController {
/**
* 职能角色 - 上传计划(新增)
*/
@Log(title = "CP计划", businessType = BusinessType.INSERT)
@PostMapping("/auth/upload")
public R authPlan(@RequestBody ActivityPlanVo activityPlanVo) {
boolean isAuth = manageEmployeeQueryService.isAuth(activityPlanVo.getEmployeeNo());
......@@ -126,6 +131,7 @@ public class PromPlanCoreController {
/**
* 促销计划修改
*/
@Log(title = "CP计划", businessType = BusinessType.UPDATE)
@PutMapping("/put")
public R planPut(@RequestBody ActivityPlanVo activityPlanVo) {
// 判断当前账号是否正常
......@@ -156,6 +162,7 @@ public class PromPlanCoreController {
/**
* 删除计划 计划日期大于今日可直接删除,如果计划日期是今日则必需是10点之前,包含过去日期不可删除
*/
@Log(title = "CP计划", businessType = BusinessType.DELETE)
@DeleteMapping("/delete")
public R deletePlan(@RequestBody ActivityPlanVo activityPlanVo){
if (Collections.isEmpty(activityPlanVo.getPlanIds())){
......@@ -172,6 +179,7 @@ public class PromPlanCoreController {
return R.success();
}
@Log(title = "CP计划", businessType = BusinessType.INSERT)
@PostMapping("/save")
public R saveWebActivityPlan(@RequestBody ActivityPlanOperVo operVo){
boolean oneSelf = manageEmployeeQueryService.isOneSelf(operVo.getEmployeeNo());
......@@ -218,6 +226,7 @@ public class PromPlanCoreController {
/**
* 修改计划
*/
@Log(title = "CP计划", businessType = BusinessType.UPDATE)
@PutMapping("/one")
public R putActivityPlan(@RequestBody ActivityPlanOperVo operVo){
if (Objects.isNull(operVo.getId())){
......@@ -251,6 +260,7 @@ public class PromPlanCoreController {
/**
* 变更归属人
*/
@Log(title = "CP计划", businessType = BusinessType.UPDATE)
@PutMapping("/more")
public R putActivityPlans(@RequestBody ActivityPlanOperVo operVo){
if (Objects.isNull(operVo.getEmployeeId()) || Collections.isEmpty(operVo.getPlanIds())){
......
package com.wangxiaolu.promotion.controller.activityplanv2;
import cn.hutool.core.collection.CollectionUtil;
import com.wangxiaolu.promotion.annotation.Log;
import com.wangxiaolu.promotion.domain.activity.wrapperQo.TemporaryActivityWrapper;
import com.wangxiaolu.promotion.domain.activity.wrapperQo.TemporaryClockWrapper;
import com.wangxiaolu.promotion.enums.log.BusinessType;
import com.wangxiaolu.promotion.pojo.PageInfo;
import com.wangxiaolu.promotion.pojo.activity.examine.dto.ActivityExamineDto;
import com.wangxiaolu.promotion.pojo.activity.inspectionInfo.dto.InspectionInfoDto;
......@@ -40,6 +42,7 @@ public class PromPlanQueryController {
@Autowired
private ExaPlanQueryService exaPlanQueryService;
@Log(title = "CP计划",businessType = BusinessType.QUERY)
@PostMapping("/page")
public R queryPage(@RequestBody PageInfo pageInfo){
promPlanQueryService.queryPage(pageInfo);
......@@ -49,6 +52,7 @@ public class PromPlanQueryController {
/**
* 根据计划ID查询任务、打卡信息
*/
@Log(title = "CP计划",businessType = BusinessType.QUERY)
@GetMapping("/{id}")
public R queryPlanById(@PathParam("id") @PathVariable Long id){
// 查询计划
......
package com.wangxiaolu.promotion.controller.lottery;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryRecordDto;
import com.wangxiaolu.promotion.pojo.lottery.vo.LotteryRecordVo;
import com.wangxiaolu.promotion.result.basedata.R;
import com.wangxiaolu.promotion.service.lottery.impl.LotteryCoreServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/lottery")
@CrossOrigin // 允许前端跨域访问
public class LotteryController {
@Autowired
private LotteryCoreServiceImpl lotteryService;
/**
* 初始化
*/
@PostMapping("/init")
public LotteryRecordDto init(@RequestBody LotteryRecordVo lotteryRecordVo) {
return lotteryService.initLottery(lotteryRecordVo.getLongitude(), lotteryRecordVo.getLatitude(), lotteryRecordVo.getReceiptImageUrl(), lotteryRecordVo.getWxOpenId());
}
/**
* 抽奖
*/
@PostMapping("/draw")
public LotteryRecordDto draw(@RequestBody LotteryRecordVo lotteryRecordVo) {
return lotteryService.doDraw(lotteryRecordVo.getId(), lotteryRecordVo.getUserInfo());
}
@PostMapping("/upload-receipt")
public R uploadReceipt(@RequestBody LotteryRecordVo lotteryRecordVo) {
lotteryService.uploadReceipt(lotteryRecordVo.getId(), lotteryRecordVo.getReceiptImageUrl());
return R.success(lotteryRecordVo.getReceiptImageUrl());
}
@PostMapping("/getuserinfo")
public R getLotteryUserInfo(@RequestBody LotteryRecordVo lotteryRecordVo) {
return R.success(lotteryService.getLotteryUserInfo(lotteryRecordVo.getWxOpenId()));
}
}
\ No newline at end of file
package com.wangxiaolu.promotion.controller.user.tem;
import com.wangxiaolu.promotion.annotation.Log;
import com.wangxiaolu.promotion.common.redis.service.RedisCache;
import com.wangxiaolu.promotion.common.util.DataUtils;
import com.wangxiaolu.promotion.common.util.EnvUtil;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import com.wangxiaolu.promotion.enums.log.BusinessType;
import com.wangxiaolu.promotion.exception.ParamException;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto;
......@@ -75,6 +77,7 @@ public class TemporaryInfoCoreController {
return R.success(weChatUserCoreService.saveWxUserInfoTemporary(temporaryDto));
}
@Log(title = "CP促销员",businessType = BusinessType.UPDATE)
@PutMapping("/temporary/changeStoreInfo")
public R changeUserInfo(@RequestBody WxTemporaryEnrollVo wxTemporaryEnrollVo) {
if (wxTemporaryEnrollVo.getOpenId() == null) {
......
package com.wangxiaolu.promotion.controller.user.tem;
import com.wangxiaolu.promotion.annotation.Log;
import com.wangxiaolu.promotion.common.redis.service.RedisCache;
import com.wangxiaolu.promotion.common.util.DataUtils;
import com.wangxiaolu.promotion.enums.log.BusinessType;
import com.wangxiaolu.promotion.exception.ParamException;
import com.wangxiaolu.promotion.pojo.PageInfo;
import com.wangxiaolu.promotion.pojo.user.vo.LoginVo;
......@@ -93,6 +95,7 @@ public class TemporaryInfoQueryController {
* 促销员信息 - 分页查询
* @param
*/
@Log(title = "CP促销员",businessType = BusinessType.QUERY)
@PostMapping("/temporary/page")
public R findTemporaryInfo(@RequestBody PageInfo pageInfo) {
weChatUserQueryService.queryPage(pageInfo);
......
package com.wangxiaolu.promotion.domain.log.dao;
import com.wangxiaolu.promotion.domain.log.mapper.entity.SysOperLog;
import com.wangxiaolu.promotion.pojo.PageInfo;
/**
* 操作日志 服务层
*
* @author ruoyi
*/
public interface ISysOperLogDao
{
/**
* 新增操作日志
*
* @param operLog 操作日志对象
* @return 结果
*/
public int insertOperlog(SysOperLog operLog);
/**
* 批量删除系统操作日志
*
* @param operIds 需要删除的操作日志ID
* @return 结果
*/
public int deleteOperLogByIds(Long[] operIds);
/**
* 查询操作日志详细
*
* @param operId 操作ID
* @return 操作日志对象
*/
public SysOperLog selectOperLogById(Long operId);
/**
* 清空操作日志
*/
public void cleanOperLog();
}
package com.wangxiaolu.promotion.domain.log.dao.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wangxiaolu.promotion.domain.log.dao.ISysOperLogDao;
import com.wangxiaolu.promotion.domain.log.mapper.SysOperLogMapper;
import com.wangxiaolu.promotion.domain.log.mapper.entity.SysOperLog;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 操作日志 服务层处理
*
* @author ruoyi
*/
@Service
@DS("sfa")
public class SysOperLogDaoImpl implements ISysOperLogDao
{
@Autowired
private SysOperLogMapper operLogMapper;
/**
* 新增操作日志
*
* @param operLog 操作日志对象
* @return 结果
*/
@Override
public int insertOperlog(SysOperLog operLog)
{
return operLogMapper.insertOperlog(operLog);
}
private LambdaQueryWrapper<SysOperLog> buildWrapper(SysOperLog operLog) {
LambdaQueryWrapper<SysOperLog> qw =new LambdaQueryWrapper<>();
if (Objects.nonNull(operLog.getBusinessType())){
qw.eq(SysOperLog::getBusinessType,operLog.getBusinessType());
}
if (Objects.nonNull(operLog.getStatus())){
qw.eq(SysOperLog::getStatus,operLog.getStatus());
}
if (Objects.nonNull(operLog.getBusinessTypes())){
qw.in(SysOperLog::getBusinessType,operLog.getBusinessTypes());
}
if (StringUtils.isNotBlank(operLog.getOperIp())){
qw.like(SysOperLog::getOperIp,operLog.getOperIp());
}
if (StringUtils.isNotBlank(operLog.getTitle())){
qw.like(SysOperLog::getTitle,operLog.getTitle());
}
if (StringUtils.isNotBlank(operLog.getOperName())){
qw.like(SysOperLog::getOperName,operLog.getOperName());
}
if (Objects.nonNull(operLog.getBeginTime())&&Objects.nonNull(operLog.getEndTime())){
qw.between(SysOperLog::getOperTime, operLog.getBeginTime(),operLog.getEndTime());
}
qw.orderByDesc(SysOperLog::getOperId);
return qw;
}
/**
* 批量删除系统操作日志
*
* @param operIds 需要删除的操作日志ID
* @return 结果
*/
@Override
public int deleteOperLogByIds(Long[] operIds)
{
return operLogMapper.deleteOperLogByIds(operIds);
}
/**
* 查询操作日志详细
*
* @param operId 操作ID
* @return 操作日志对象
*/
@Override
public SysOperLog selectOperLogById(Long operId)
{
return operLogMapper.selectOperLogById(operId);
}
/**
* 清空操作日志
*/
@Override
public void cleanOperLog()
{
operLogMapper.cleanOperLog();
}
}
package com.wangxiaolu.promotion.domain.log.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wangxiaolu.promotion.domain.log.mapper.entity.SysOperLog;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* 操作日志 数据层
*
* @author ruoyi
*/
@Repository
@Mapper
public interface SysOperLogMapper extends BaseMapper<SysOperLog>
{
/**
* 新增操作日志
*
* @param operLog 操作日志对象
*/
public int insertOperlog(SysOperLog operLog);
/**
* 查询系统操作日志集合
*
* @param operLog 操作日志对象
* @return 操作日志集合
*/
public List<SysOperLog> selectOperLogList(SysOperLog operLog);
/**
* 批量删除系统操作日志
*
* @param operIds 需要删除的操作日志ID
* @return 结果
*/
public int deleteOperLogByIds(Long[] operIds);
/**
* 查询操作日志详细
*
* @param operId 操作ID
* @return 操作日志对象
*/
public SysOperLog selectOperLogById(Long operId);
/**
* 清空操作日志
*/
public void cleanOperLog();
}
package com.wangxiaolu.promotion.domain.log.mapper.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 操作日志记录表 oper_log
*
* @author ruoyi
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@TableName(value = "sys_oper_log")
public class SysOperLog implements Serializable
{
private static final long serialVersionUID = 1L;
/** 日志主键 */
@TableId(type = IdType.AUTO)
private Long operId;
/** 操作模块 */
private String title;
/** 业务类型(0其它 1新增 2修改 3删除) */
private Integer businessType;
/** 业务类型数组 */
@TableField(exist = false)
private Integer[] businessTypes;
/** 请求方法 */
private String method;
/** 请求方式 */
private String requestMethod;
/** 操作类别(0其它 1后台用户 2手机端用户) */
private Integer operatorType;
/** 操作人员 */
private String operName;
/** 部门名称 */
private String deptName;
/** 请求url */
private String operUrl;
/** 操作地址 */
private String operIp;
/** 请求参数 */
private String operParam;
/** 返回参数 */
private String jsonResult;
/** 操作状态(0正常 1异常) */
private Integer status;
/** 错误消息 */
private String errorMsg;
/** 操作时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date operTime;
/** 消耗时间 */
private Long costTime;
/** 操作开始时间 **/
@TableField(exist = false)
private Date beginTime;
/** 操作结束时间 **/
@TableField(exist = false)
private Date endTime;
}
package com.wangxiaolu.promotion.domain.lottery.dao;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryUserInfo;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryRecordDto;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryUserInfoDto;
/**
* @Author: DouXinYu
* @Date: 2026-04-07 17:41
* @Description:
*/
public interface LotteryCoreDao {
LotteryRecordDto doDraw(Long recordId, LotteryUserInfoDto userInfo);
LotteryRecordDto initLottery(Double longitude, Double latitude, String receiptImage, String wxOpenId);
LotteryUserInfoDto getLotteryUserInfo(String wxOpenId);
}
package com.wangxiaolu.promotion.domain.lottery.dao.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wangxiaolu.promotion.common.util.BeanUtils;
import com.wangxiaolu.promotion.domain.lottery.dao.LotteryCoreDao;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryRecord;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryUserInfo;
import com.wangxiaolu.promotion.domain.lottery.mapper.LotteryMapper;
import com.wangxiaolu.promotion.domain.lottery.mapper.LotteryUserInfoMapper;
import com.wangxiaolu.promotion.exception.ParamException;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryRecordDto;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryUserInfoDto;
import com.wangxiaolu.promotion.result.basedata.RCode;
import com.wangxiaolu.promotion.utils.TencentMapUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
/**
* @Author: DouXinYu
* @Date: 2026-04-07 17:42
* @Description: 抽奖核心DAO实现(动态概率:当月+同地址 中过一等奖则降低概率)
*/
@Service
public class LotteryCoreDaoImpl implements LotteryCoreDao {
@Autowired
private LotteryMapper lotteryMapper;
@Autowired
private TencentMapUtil tencentMapUtil;
@Autowired
private LotteryUserInfoMapper lotteryUserInfoMapper;
/**
* 初始化抽奖记录
*/
@Override
public LotteryRecordDto initLottery(Double longitude, Double latitude, String receiptImage, String wxOpenId) {
// 腾讯地图逆地址解析
Map<String, String> location = tencentMapUtil.getAddressMapByLngLat(longitude, latitude);
LotteryRecord record = new LotteryRecord();
record.setLongitude(longitude);
record.setLatitude(latitude);
// ====================== 自动拆分省市区 ======================
record.setProvince(location.get("province"));
record.setCity(location.get("city"));
record.setDistrict(location.get("district"));
record.setAddress(location.get("address"));
record.setReceiptImageUrl(receiptImage);
record.setStatus("pending");
record.setWxOpenId(wxOpenId);
lotteryMapper.insert(record);
LotteryRecordDto result = BeanUtils.transitionDto(record, LotteryRecordDto.class);
ArrayList<String> prizeList = new ArrayList<>();
prizeList.add("三等奖");
prizeList.add("一等奖");
prizeList.add("二等奖");
prizeList.add("三等奖");
prizeList.add("二等奖");
prizeList.add("三等奖");
prizeList.add("二等奖");
prizeList.add("三等奖");
result.setPrizes(prizeList);
return result;
}
/**
* 执行抽奖(动态概率核心)
*/
@Override
public LotteryRecordDto doDraw(Long recordId, LotteryUserInfoDto userInfo) {
LotteryRecord record = lotteryMapper.selectById(recordId);
if (record == null) {
throw new ParamException(RCode.LOTTERY_RECORD_NOT_EXIST);
}
if (record.getWxOpenId() != null && record.getPrizeLevel() != null) {
throw new ParamException(RCode.LOTTERY_RECORD_ERROR);
}
LotteryUserInfo user = lotteryUserInfoMapper.selectOne(new LambdaQueryWrapper<LotteryUserInfo>().eq(LotteryUserInfo::getOpenId, record.getWxOpenId()));
if (Objects.isNull(user)) {
LotteryUserInfo lotteryUserInfo = new LotteryUserInfo();
lotteryUserInfo = BeanUtils.transitionDto(userInfo, LotteryUserInfo.class);
lotteryUserInfo.setOpenId(record.getWxOpenId());
lotteryUserInfoMapper.insert(lotteryUserInfo);
}
// 动态概率
int prizeLevel = simpleDynamicPrize();
record.setPrizeLevel(prizeLevel);
lotteryMapper.updateById(record);
LotteryRecordDto result = new LotteryRecordDto();
result = BeanUtils.transitionDto(record, LotteryRecordDto.class);
result.setUserInfo(userInfo);
return result ;
}
@Override
public LotteryUserInfoDto getLotteryUserInfo(String wxOpenId) {
LotteryUserInfo lotteryUserInfo = lotteryUserInfoMapper.selectOne(new LambdaQueryWrapper<LotteryUserInfo>().eq(LotteryUserInfo::getOpenId, wxOpenId));
return BeanUtils.transitionDto(lotteryUserInfo, LotteryUserInfoDto.class);
}
/**
* 简单动态概率
*/
private int simpleDynamicPrize() {
int r = new Random().nextInt(100);
if (r < 10) {
return 1;
} else if (r < 30) {
return 2;
} else {
return 3;
}
}
}
\ No newline at end of file
package com.wangxiaolu.promotion.domain.lottery.entity;
import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
@Data
@TableName("lottery_record") // 对应数据库表名
public class LotteryRecord {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
/**
* 经度
*/
private Double longitude;
/**
* 纬度
*/
private Double latitude;
/**
* 省
*/
private String province;
/**
* 市
*/
private String city;
/**
* 区/县
*/
private String district;
/**
* 解析后的地址
*/
private String address;
/**
* 中奖等级 (0:一等奖, 1:二等奖, 2:三等奖)
*/
private Integer prizeLevel;
/**
* 小票图片URL
*/
private String receiptImageUrl;
/**
* 状态 (verified:已审核, pending:待审核)
*/
private String status;
private String wxOpenId;
/**
* 创建时间
*/
private Date createTime;
private Date updateTime;
private Integer isDeleted;
}
\ No newline at end of file
package com.wangxiaolu.promotion.domain.lottery.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("lottery_user_info")
public class LotteryUserInfo {
private Long id;
private String openId;
private String nickName;
private String avatarUrl;
private String gender;
private String language;
private String country;
private String province;
private String city;
private String phone;
private Date createTime;
private Date updateTime;
private Integer isDeleted;
}
\ No newline at end of file
package com.wangxiaolu.promotion.domain.lottery.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface LotteryMapper extends BaseMapper<LotteryRecord> {
}
\ No newline at end of file
package com.wangxiaolu.promotion.domain.lottery.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryUserInfo;
import org.apache.ibatis.annotations.Mapper;
/**
* @Author: DouXinYu
* @Date: 2026-04-24 11:29
* @Description:
*/
@Mapper
public interface LotteryUserInfoMapper extends BaseMapper<LotteryUserInfo> {
}
package com.wangxiaolu.promotion.enums.log;
/**
* 操作状态
*
* @author ruoyi
*
*/
public enum BusinessStatus
{
/**
* 成功
*/
SUCCESS,
/**
* 失败
*/
FAIL,
}
package com.wangxiaolu.promotion.enums.log;
/**
* 业务操作类型
*
* @author ruoyi
*/
public enum BusinessType
{
/**
* 其它
*/
OTHER,
/**
* 新增
*/
INSERT,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 生成代码
*/
GENCODE,
/**
* 清空数据
*/
CLEAN,
/**
* 查询
*/
QUERY,
}
package com.wangxiaolu.promotion.enums.log;
/**
* 操作人类别
*
* @author ruoyi
*/
public enum OperatorType
{
/**
* 其它
*/
OTHER,
/**
* 后台用户
*/
MANAGE,
/**
* 手机端用户
*/
MOBILE
}
package com.wangxiaolu.promotion.pojo.lottery.dto;
import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@Data
public class LotteryRecordDto {
private Long id;
/**
* 经度
*/
private Double longitude;
/**
* 纬度
*/
private Double latitude;
/**
* 省
*/
private String province;
/**
* 市
*/
private String city;
/**
* 区/县
*/
private String district;
/**
* 解析后的地址
*/
private String address;
/**
* 中奖等级 (0:一等奖, 1:二等奖, 2:三等奖, -1:未中奖)
*/
private Integer prizeLevel;
/**
* 创建时间
*/
private Date createTime;
/**
* 奖品列表
*/
private List<String> prizes;
/**
* 抽奖结果
*/
private String drawResult;
/**
* 用户的Open_id
*/
private String wxOpenId;
/**
* 用户信息
*/
private LotteryUserInfoDto userInfo;
}
\ No newline at end of file
package com.wangxiaolu.promotion.pojo.lottery.dto;
import lombok.Data;
/**
* @Author: DouXinYu
* @Date: 2026-04-27 17:25
* @Description:
*/
@Data
public class LotteryUserInfoDto {
private Long id;
private String openId;
private String nickName;
private String avatarUrl;
private String gender;
private String language;
private String country;
private String province;
private String city;
private String phone;
}
package com.wangxiaolu.promotion.pojo.lottery.vo;
import cn.hutool.system.UserInfo;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryUserInfo;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryUserInfoDto;
import lombok.Data;
import java.util.List;
@Data
public class LotteryRecordVo {
private Long id;
/**
* 经度
*/
private Double longitude;
/**
* 纬度
*/
private Double latitude;
/**
* 省
*/
private String province;
/**
* 市
*/
private String city;
/**
* 区/县
*/
private String district;
/**
* 解析后的地址
*/
private String address;
/**
* 中奖等级 (0:一等奖, 1:二等奖, 2:三等奖, -1:未中奖)
*/
private Integer prizeLevel;
/**
* 奖品列表
*/
private List<String> prizes;
/**
* 小票图片地址(OSS路径)
*/
private String receiptImageUrl;
/**
* 微信OpenId
*/
private String wxOpenId;
/**
* 用户信息
*/
private LotteryUserInfoDto userInfo;
}
\ No newline at end of file
package com.wangxiaolu.promotion.service.lottery;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryRecordDto;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryUserInfoDto;
/**
* @Author: DouXinYu
* @Date: 2026-04-07 17:38
* @Description:
*/
public interface LotteryCoreService {
LotteryRecordDto initLottery(Double longitude, Double latitude, String receiptImage, String wxOpenId);
LotteryRecordDto doDraw(Long recordId, LotteryUserInfoDto userInfo);
}
package com.wangxiaolu.promotion.service.lottery.impl;
import cn.hutool.system.UserInfo;
import com.wangxiaolu.promotion.domain.lottery.dao.LotteryCoreDao;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryRecord;
import com.wangxiaolu.promotion.domain.lottery.entity.LotteryUserInfo;
import com.wangxiaolu.promotion.domain.lottery.mapper.LotteryMapper;
import com.wangxiaolu.promotion.exception.ParamException;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryRecordDto;
import com.wangxiaolu.promotion.pojo.lottery.dto.LotteryUserInfoDto;
import com.wangxiaolu.promotion.result.basedata.RCode;
import com.wangxiaolu.promotion.service.lottery.LotteryCoreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Random;
@Service
public class LotteryCoreServiceImpl implements LotteryCoreService {
@Autowired
private LotteryMapper lotteryMapper;
@Autowired
private LotteryCoreDao lotteryCoreDao;
/**
* 上传小票
*/
public void uploadReceipt(Long recordId, String receiptImage) {
LotteryRecord record = lotteryMapper.selectById(recordId);
if (record == null) {
throw new ParamException(RCode.LOTTERY_RECORD_NOT_EXIST);
}
record.setReceiptImageUrl(receiptImage);
record.setStatus("verified"); // 促销员审核通过
lotteryMapper.updateById(record);
}
/**
* 1. 初始化:保存位置信息
*/
@Override
public LotteryRecordDto initLottery(Double longitude, Double latitude, String receiptImage, String wxOpenId) {
return lotteryCoreDao.initLottery(longitude, latitude, receiptImage, wxOpenId);
}
/**
* 2. 抽奖:根据地址动态计算概率
*/
@Override
public LotteryRecordDto doDraw(Long recordId, LotteryUserInfoDto userInfo) {
return lotteryCoreDao.doDraw(recordId,userInfo);
}
public LotteryUserInfoDto getLotteryUserInfo(String wxOpenId) {
return lotteryCoreDao.getLotteryUserInfo(wxOpenId);
}
}
\ No newline at end of file
......@@ -53,4 +53,7 @@ public class AuthUtils {
public static String getUserId(String token) {
return JwtTokenUtils.getUserId(parseToken(token));
}
public static String getUserName(String token) {
return JwtTokenUtils.getUserName(parseToken(token));
}
}
......@@ -13,6 +13,7 @@ import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
......@@ -40,6 +41,19 @@ public class TencentMapUtil {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 1.1 逆地理编码:经纬度转地址Map(province/city/district/address)
*/
public Map<String, String> getAddressMapByLngLat(double lng, double lat) {
AddressInfo addressInfo = getAddressByLngLat(lng, lat);
Map<String, String> result = new java.util.HashMap<>();
result.put("province", addressInfo.getProvince());
result.put("city", addressInfo.getCity());
result.put("district", addressInfo.getDistrict());
result.put("address", addressInfo.getProvince()+addressInfo.getCity()+addressInfo.getDistrict()+addressInfo.getStreet());
return result;
}
// ==================== 1. 逆地理编码实体(保留原有) ====================
public static class AddressInfo {
private String province; // 省份
......
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
# url: jdbc:mysql://bj-cdb-j8ppdy86.sql.tencentcdb.com:63569/promotion_dev?autoReconnect=true
url: jdbc:mysql://192.168.100.39:25301/promotion_dev?autoReconnect=true
username: root
password: Zt%68Dsuv&M
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
datasource:
master:
url: jdbc:mysql://192.168.100.39:25301/promotion_dev?autoReconnect=true
password: Zt%68Dsuv&M
username: root
driver-class-name: com.mysql.jdbc.Driver
bi:
url: jdbc:mysql://192.168.100.39:25301/market_bi?autoReconnect=true&useSSL=false&characterEncoding=utf8
password: Zt%68Dsuv&M
username: root
driver-class-name: com.mysql.jdbc.Driver
sfa:
url: jdbc:mysql://192.168.100.39:25301/sfa_bus?autoReconnect=true
password: Zt%68Dsuv&M
username: root
driver-class-name: com.mysql.jdbc.Driver
redis:
port: 6379
host: 192.168.100.40
......
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://rm-2ze28qp55mrm34g8b.mysql.rds.aliyuncs.com:3306/promotion?autoReconnect=true
username: sfabus
password: Wxl@325Pa91
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
datasource:
sfa:
url: jdbc:mysql://rm-2ze28qp55mrm34g8b.mysql.rds.aliyuncs.com:3306/sfa_bus?autoReconnect=true
password: Wxl@325Pa91
username: sfabus
driver-class-name: com.mysql.jdbc.Driver
master:
url: jdbc:mysql://rm-2ze28qp55mrm34g8b.mysql.rds.aliyuncs.com:3306/promotion?autoReconnect=true&useSSL=false&characterEncoding=utf8
password: Wxl@325Pa91
username: sfabus
driver-class-name: com.mysql.jdbc.Driver
bi:
url: jdbc:mysql://rm-2ze28qp55mrm34g8b.mysql.rds.aliyuncs.com:3306/market_bi?autoReconnect=true&useSSL=false&characterEncoding=utf8
password: Wxl@325Pa91
username: sfabus
driver-class-name: com.mysql.jdbc.Driver
redis:
port: 6379
host: r-2zehfktt1r34vn4qws.redis.rds.aliyuncs.com
......@@ -65,8 +80,8 @@ tengxunyun:
qince:
open_api: https://openapi.region2.qince.com
open_id: 8546408787259919799
app_key: oV0FHfMt81Tii2_kst
open_id: 5459563908315541679
app_key: MlqoWw9zcmY21htoHI
# mybatis-plus 打印sql日志
#mybatis-plus:
......
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
# url: jdbc:mysql://bj-cdb-j8ppdy86.sql.tencentcdb.com:63569/promotion_dev?autoReconnect=true
url: jdbc:mysql://192.168.100.39:25301/promotion_dev?autoReconnect=true
username: root
password: Zt%68Dsuv&M
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
datasource:
master:
url: jdbc:mysql://192.168.100.39:25301/promotion_dev?autoReconnect=true
password: Zt%68Dsuv&M
username: root
driver-class-name: com.mysql.jdbc.Driver
bi:
url: jdbc:mysql://192.168.100.39:25301/market_bi?autoReconnect=true&useSSL=false&characterEncoding=utf8
password: Zt%68Dsuv&M
username: root
driver-class-name: com.mysql.jdbc.Driver
sfa:
url: jdbc:mysql://192.168.100.39:25301/sfa_bus?autoReconnect=true
password: Zt%68Dsuv&M
username: root
driver-class-name: com.mysql.jdbc.Driver
redis:
port: 6379
host: 192.168.100.40
......
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangxiaolu.promotion.domain.log.mapper.SysOperLogMapper">
<resultMap type="com.wangxiaolu.promotion.domain.log.mapper.entity.SysOperLog" id="SysOperLogResult">
<id property="operId" column="oper_id" />
<result property="title" column="title" />
<result property="businessType" column="business_type" />
<result property="method" column="method" />
<result property="requestMethod" column="request_method" />
<result property="operatorType" column="operator_type" />
<result property="operName" column="oper_name" />
<result property="deptName" column="dept_name" />
<result property="operUrl" column="oper_url" />
<result property="operIp" column="oper_ip" />
<result property="operParam" column="oper_param" />
<result property="jsonResult" column="json_result" />
<result property="status" column="status" />
<result property="errorMsg" column="error_msg" />
<result property="operTime" column="oper_time" />
<result property="costTime" column="cost_time" />
</resultMap>
<sql id="selectOperLogVo">
select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_param, json_result, status, error_msg, oper_time, cost_time
from sys_oper_log
</sql>
<insert id="insertOperlog" parameterType="com.wangxiaolu.promotion.domain.log.mapper.entity.SysOperLog">
insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_param, json_result, status, error_msg, cost_time, oper_time)
values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate())
</insert>
<select id="selectOperLogById" parameterType="Long" resultMap="SysOperLogResult">
<include refid="selectOperLogVo"/>
where oper_id = #{operId}
</select>
<update id="cleanOperLog">
truncate table sys_oper_log
</update>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangxiaolu.promotion.domain.lottery.mapper.LotteryMapper">
<!-- 通用结果映射 -->
<resultMap id="LotteryRecordMap" type="com.wangxiaolu.promotion.domain.lottery.entity.LotteryRecord">
<id column="id" property="id"/>
<result column="longitude" property="longitude"/>
<result column="latitude" property="latitude"/>
<result column="province" property="province"/>
<result column="city" property="city"/>
<result column="district" property="district"/>
<result column="address" property="address"/>
<result column="prize_level" property="prizeLevel"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<result column="wx_open_id" property="wxOpenId"/>
<result column="receipt_image_url" property="receiptImageUrl"/>
<result column="status" property="status"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangxiaolu.promotion.domain.lottery.mapper.LotteryUserInfoMapper">
<!-- 通用结果映射 -->
<resultMap id="LotteryUserInfoMap" type="com.wangxiaolu.promotion.domain.lottery.entity.LotteryUserInfo">
<id column="id" property="id"/>
<result column="open_id" property="openId"/>
<result column="nick_name" property="nickName"/>
<result column="avatar_url" property="avatarUrl"/>
<result column="gender" property="gender"/>
<result column="language" property="language"/>
<result column="province" property="province"/>
<result column="city" property="city"/>
<result column="country" property="country"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论