【项目】【抽奖系统】活动创建

目录

  • 一、简介
  • 二、参数列表
  • 三、接口规范
  • [四、controller 层](#四、controller 层)
    • [4.1 CreateActivityResult :Controller层返回类](#4.1 CreateActivityResult :Controller层返回类)
    • [4.2 CreateActivityParam :传入参数](#4.2 CreateActivityParam :传入参数)
      • [4.2.1 CreatePrizeByActivityParam 活动-奖品关联参数](#4.2.1 CreatePrizeByActivityParam 活动-奖品关联参数)
      • [4.2.2 CreateUserByActivityParam 活动-人员关联参数](#4.2.2 CreateUserByActivityParam 活动-人员关联参数)
    • [4.3 convertTOCreateActivityResult :service层返回类型 转换 Controller层返回类型 方法](#4.3 convertTOCreateActivityResult :service层返回类型 转换 Controller层返回类型 方法)
    • [4.4 新增错误码](#4.4 新增错误码)
  • 五、service层
    • 5.1创建service接口
    • [5.2 实现service接口](#5.2 实现service接口)
      • [5.2.1 checkActivityInfo 校验活动信息](#5.2.1 checkActivityInfo 校验活动信息)
      • [5.2.2 convertActivityDetailDTO 整合完整活动信息: 活动信息 奖品信息 人员信息](#5.2.2 convertActivityDetailDTO 整合完整活动信息: 活动信息 奖品信息 人员信息)
    • [5.3 ActivityDetailDTO 活动送完整信息存储类](#5.3 ActivityDetailDTO 活动送完整信息存储类)
    • [5.4 枚举类型管理属性](#5.4 枚举类型管理属性)
    • [5.4 Redis缓存完整活动信息和拿到完整活动信息](#5.4 Redis缓存完整活动信息和拿到完整活动信息)
    • [5.5 返回数据](#5.5 返回数据)
    • [5.6 新增错误码](#5.6 新增错误码)
  • [六、 dao层](#六、 dao层)
    • [6.1 根据传入id列表,查找数据库存在的id列表](#6.1 根据传入id列表,查找数据库存在的id列表)
    • [6.2 根据传入List批量创建数据](#6.2 根据传入List批量创建数据)
    • [6.3 数据库字段对应类属性](#6.3 数据库字段对应类属性)
    • [6.3 入的id列表查询列表中存在数据库的奖品信息](#6.3 入的id列表查询列表中存在数据库的奖品信息)
  • 七、前端:

一、简介

创建的活动信息包含:

  • 活动名称
  • 活动描述
  • 圈选奖品:勾选对应奖品,并设置奖品等级(⼀⼆三等奖),及奖品数量
  • 圈选⼈员:勾选参与抽奖⼈员

时序图:

二、参数列表

参数名 描述 类型 默认值 条件
activityName 活动名称 Integer 必须
description 活动描述 Integer 必须
activityPrizeList 活动奖品关联列表 List 必须

三、接口规范

java 复制代码
请求] /activity/create POST
{
 "activityName": "抽奖测试",
 "description": "年会抽奖活动",
 "activityPrizeList": [
	 {
		 "prizeId": 13,
		 "prizeAmount": 1,
		 "prizeTiers": "FIRST_PRIZE"
	 },
	 {
		 "prizeId": 12,
	 	"prizeAmount": 1,
		"prizeTiers": "SECOND_PRIZE"
	 }
 ],
 "activityUserList": [
	 {
		 "userId": 25,
		 "userName": "郭靖"
	 },
	 {
		 "userId": 23,
		 "userName": "杨康"
	 }
 ]
}
[响应] 
{
	 "code": 200,
	 "data": {
	 	"activityId": 23
	 },
	 "msg": ""
}

四、controller 层

com.yj.lottery_system.controller 包下:

  • CreateActivityResult :Controller层返回数据
  • CreateActivityParam :传入参数,是JSON,使用@RequestBody转化,并且需要对属性进行校验加上@Validated
  • 调用service对应服务
  • convertTOCreateActivityResult :service层返回类型 转换 Controller层返回类型 方法
java 复制代码
package com.yj.lottery_system.controller;

import com.yj.lottery_system.common.errorcode.ControllerErrorCodeConstants;
import com.yj.lottery_system.common.exception.ControllerException;
import com.yj.lottery_system.common.pojo.CommonResult;
import com.yj.lottery_system.common.utils.JacksonUtil;
import com.yj.lottery_system.controller.param.CreateActivityParam;
import com.yj.lottery_system.controller.result.CreateActivityResult;
import com.yj.lottery_system.service.IActivityService;
import com.yj.lottery_system.service.dto.CreateActivityDTO;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController

public class ActivityController {
    private static final Logger log = LoggerFactory.getLogger(ActivityController.class);
    @Resource
    private IActivityService activityService;

    /**
     * 创建活动
     * @param param
     * @return
     */
    @RequestMapping("/activity/create")
    public CommonResult<CreateActivityResult> createActivity (@RequestBody @Validated CreateActivityParam param) {
        //日志打印
        log.info("createActivity CreateActivityParam: {}", JacksonUtil.writeValueAsString(param));
        //调用service服务
        CreateActivityDTO activityDTO  = activityService.createActivity(param);
        return CommonResult.success(convertTOCreateActivityResult(activityDTO));

    }

}

4.1 CreateActivityResult :Controller层返回类

com.yj.lottery_system.controller.result 包下:

  • 根据接口规范响应只需要返回活动id
java 复制代码
package com.yj.lottery_system.controller.result;

import lombok.Data;

import java.io.Serializable;

@Data
public class CreateActivityResult implements Serializable {
    //创建的活动id
    private Long activityId;
}

4.2 CreateActivityParam :传入参数

com.yj.lottery_system.controller.param 包下:

  • 根据接口规范,请求传入参数需要传 activityName,description,activityPrizeList,activityUserList,全都不能为空
  • 要想保证属性类List的参数不为空,加上@Valid注解
java 复制代码
package com.yj.lottery_system.controller.param;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

@Data
public class CreateActivityParam implements Serializable {
    //活动名称
    @NotBlank(message = "活动名称不能为空")
    private String activityName;

    //活动描述
    @NotBlank(message = "活动描述不能为空")
    private String description;

    //活动关联奖品列表
    @NotEmpty(message = "活动关联奖品列表不能为空")
    @Valid
    private List<CreatePrizeByActivityParam> activityPrizeList;

    //活动关联人员列表
    @NotEmpty(message = "活动关联人员列表不能为空")
    @Valid
    private List<CreateUserByActivityParam> activityUserList;
}

4.2.1 CreatePrizeByActivityParam 活动-奖品关联参数

com.yj.lottery_system.controller.param 包下:

  • 根据接口规范请求,有prizeId,prizeAmount,prizeTiers都不能为空。
java 复制代码
package com.yj.lottery_system.controller.param;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

import java.io.Serializable;

@Data
public class CreatePrizeByActivityParam implements Serializable {
    //活动关联奖品id
    @NotNull(message = "活动关联奖品id不能为空")
    private Long prizeId;

    //活动关联奖品数量
    @NotNull(message = "活动关联奖品数量不能为空")
    private Long prizeAmount;
    //活动关联奖品等级
    @NotBlank(message = "活动关联奖品等级不能为空")
    private String prizeTiers;

}

4.2.2 CreateUserByActivityParam 活动-人员关联参数

com.yj.lottery_system.controller.param 包下:

  • 根据接口规范 请求,有userId,userName都不能为空。
java 复制代码
package com.yj.lottery_system.controller.param;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

import java.io.Serializable;

@Data
public class CreateUserByActivityParam implements Serializable {
    //活动关联人员id
    @NotNull(message = "活动关联人员id不能为空")
    private Long userId;

    //活动关联人员名字
    @NotBlank(message = "活动关联人员名字不能为空")
    private String userName;
}

4.3 convertTOCreateActivityResult :service层返回类型 转换 Controller层返回类型 方法

com/yj/lottery_system/controller 包下 ActivityController类:

  • 非空校验
  • 转换,返回
java 复制代码
    private CreateActivityResult convertTOCreateActivityResult(CreateActivityDTO activityDTO) {
        if(null == activityDTO) {
            throw new ControllerException(ControllerErrorCodeConstants.CREATE_ACTIVITY_ERROR);
        }
        CreateActivityResult createActivityResult = new CreateActivityResult();
        createActivityResult.setActivityId(activityDTO.getActivityId());
        return createActivityResult;
    }

4.4 新增错误码

com/yj/lottery_system/common/errorcode 包下 ControllerErrorCodeConstants类

  • 新增活动创建失败错误码。
java 复制代码
    ErrorCode CREATE_ACTIVITY_ERROR = new ErrorCode(300,"创建活动失败");

五、service层

5.1创建service接口

com.yj.lottery_system.service 包下创建接口

java 复制代码
package com.yj.lottery_system.service;

import com.yj.lottery_system.controller.param.CreateActivityParam;
import com.yj.lottery_system.service.dto.CreateActivityDTO;
import org.springframework.stereotype.Service;

@Service
public interface IActivityService {
    /**
     * 创建活动
     * @param param
     * @return
     */
    CreateActivityDTO createActivity(CreateActivityParam param);
}

5.2 实现service接口

com.yj.lottery_system.service.impl 包下:ActivityServiceImpl 类:

  • 调用checkActivityInfo 校验活动信息存在和正确性
  • 保存 活动信息
  • 保存活动关联奖品信息
  • 保存活动关联人员信息
  • 整合完整活动信息: 活动信息 奖品信息 人员信息
  • 存入Redis
  • 构造返回
java 复制代码
package com.yj.lottery_system.service.impl;
import java.util.*;

import com.yj.lottery_system.common.errorcode.ServiceErrorCodeConstants;
import com.yj.lottery_system.common.exception.ServiceException;
import com.yj.lottery_system.common.utils.JacksonUtil;
import com.yj.lottery_system.common.utils.RedisUtil;
import com.yj.lottery_system.controller.param.CreateActivityParam;
import com.yj.lottery_system.controller.param.CreatePrizeByActivityParam;
import com.yj.lottery_system.controller.param.CreateUserByActivityParam;
import com.yj.lottery_system.dao.dataObject.ActivityDO;
import com.yj.lottery_system.dao.dataObject.ActivityPrizeDO;
import com.yj.lottery_system.dao.dataObject.ActivityUserDO;
import com.yj.lottery_system.dao.dataObject.PrizeDO;
import com.yj.lottery_system.dao.mapper.*;
import com.yj.lottery_system.service.IActivityService;
import com.yj.lottery_system.service.dto.ActivityDetailDTO;
import com.yj.lottery_system.service.dto.CreateActivityDTO;
import com.yj.lottery_system.service.enums.ActivityPrizeStatusEnum;
import com.yj.lottery_system.service.enums.ActivityPrizeTiresEnum;
import com.yj.lottery_system.service.enums.ActivityStatusEnum;
import com.yj.lottery_system.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)
public class ActivityServiceImpl implements IActivityService {
    private static final Logger log = LoggerFactory.getLogger(ActivityServiceImpl.class);
    //前缀
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    //过期时间 3 天
    private final Long ACTIVITY_TIMEOUT = (long)(60 * 60 * 24 * 3);
   @Autowired
   private UserMapper userMapper;
   @Autowired
   private PrizeMapper prizeMapper;
   @Autowired
   private ActivityMapper activityMapper;
   @Autowired
   private ActivityPrizeMapper activityPrizeMapper;
   @Autowired
   private ActivityUserMapper activityUserMapper;
   @Autowired
   private RedisUtil redisUtil;
 public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息
        checkActivityInfo(param);
        //保存 活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setDescription(param.getDescription());
        activityDO.setActivityName(param.getActivityName());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());

        activityMapper.insert(activityDO);

        //保存活动关联奖品信息
        List<CreatePrizeByActivityParam> prizeParamList = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParamList.stream()
                .map( prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());

                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        //保存活动关联人员信息
        List<CreateUserByActivityParam> userParamList = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParamList.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        //整合完整活动信息: 活动信息 奖品信息 人员信息
        //先拿到奖品id 获取奖品基本属性
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);

        // key = activityId value = ActivityDetailDTO
        ActivityDetailDTO activityDetailDTO =  convertActivityDetailDTO(activityDO,activityUserDOList, activityPrizeDOList, prizeDOList);

        // 存入Redis
        cacheActivity(activityDetailDTO);

        //构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDetailDTO.getActivityId());


        return createActivityDTO;
    }
}

5.2.1 checkActivityInfo 校验活动信息

com/yj/lottery_system/service/impl 包下:ActivityServiceImpl类

校验活动信息:

  • 传入参数非空校验
  • 校验传入参数的人员id,是否存在与数据库
    • 使用流拿到参数中的活动人员关联List中的人员id
    • 调用dao查询上面拿到的人员id列表,存在与数据库中的id有哪些
    • 遍历参数中的人员id列表,有元素不在数据库中查到的就抛出异常
  • 校验传入参数的奖品id,是否存在与数据库,与上一个步骤一样
  • 校验人员数量和奖品数量,保证奖品数量小于等于人员数量
  • 活动等级必须是我们的枚举类型中有的
java 复制代码
 private void checkActivityInfo(CreateActivityParam param) {
        if(null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //人员id在user表存在
        //拿参数里面的人员id
        List<Long> userIds = param.getActivityUserList().stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        //数据库存在的人员id
        List<Long> exitsUserIds = userMapper.selectExitsByIds(userIds);
        if(CollectionUtils.isEmpty(exitsUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        userIds.forEach(id -> {
            if(!exitsUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        //奖品id在prize表存在
        //拿参数里面的奖品id
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        //数据库存在的奖品id
        List<Long> exitsPrizeIds = prizeMapper.selectExitsByIds(prizeIds);
        if(CollectionUtils.isEmpty(exitsPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id -> {
            if(!exitsPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //人员数量大于等于奖品数量
        int userCount = param.getActivityUserList().size();
        long prizeCount = param.getActivityPrizeList().stream()
                .mapToLong(CreatePrizeByActivityParam :: getPrizeAmount)
                .sum();
        if( userCount < prizeCount) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_PRIZE_COUNT_ERROR);
        }
        //校验活动奖品等级
        param.getActivityPrizeList().forEach(prize -> {
            if(null == ActivityPrizeTiresEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }

5.2.2 convertActivityDetailDTO 整合完整活动信息: 活动信息 奖品信息 人员信息

java 复制代码
private ActivityDetailDTO convertActivityDetailDTO(ActivityDO activityDO,
                                                       List<ActivityUserDO> activityUserDOList,
                                                       List<ActivityPrizeDO> activityPrizeDOList,
                                                       List<PrizeDO> prizeDOList) {
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(activityPrizeDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
                   Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(activityPrizeDO.getPrizeId()))
                            .findFirst();
                   //如果PrizeDO 空,不会走下面逻辑
                   optionalPrizeDO.ifPresent(prizeDO -> {
                       prizeDTO.setDescription(prizeDO.getDescription());
                       prizeDTO.setImageUrl(prizeDO.getImageUrl());
                       prizeDTO.setName(prizeDO.getName());
                       prizeDTO.setPrice(prizeDO.getPrice());
                   });

                    prizeDTO.setTires(ActivityPrizeTiresEnum.forName(activityPrizeDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(activityPrizeDO.getStatus()));


                    return prizeDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                        .map(activityUserDO -> {
                            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(activityUserDO.getUserId());
                            userDTO.setUserName(activityUserDO.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDO.getStatus()));

                            return userDTO;
                        }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList);
        return activityDetailDTO;
    }

5.3 ActivityDetailDTO 活动送完整信息存储类

根据接口规范的响应,将活动信息,人员信息,奖品信息全部整合到一个类中

com.yj.lottery_system.service.dto 包下:

java 复制代码
package com.yj.lottery_system.service.dto;

import com.yj.lottery_system.service.enums.*;
import lombok.Data;

import java.math.BigDecimal;
import java.util.List;

@Data
public class ActivityDetailDTO {
    /**
     *活动信息
     */
    //活动id
    private Long activityId;

    //活动名
    private String activityName;

    //活动描述
    private String desc;

    //活动状态
    private ActivityStatusEnum status;

    /**
     * 判断状态是否有效
     * @return
     */
    public Boolean valid() {
        return status.equals(ActivityStatusEnum.RUNNING);
    }

    //奖品信息列表
    private  List<PrizeDTO> prizeDTOList;

    //人员信息列表
    private  List<UserDTO> userDTOList;

    @Data
    public static class PrizeDTO {
        //奖品id
        private Long PrizeId;
        //奖品描述
        private String description;

        //图片索引
        private String imageUrl;

        //奖品名
        private String name;

        //价格
        private BigDecimal price;

        //等级
        private ActivityPrizeTiresEnum tires;

        //数量
        private Long prizeAmount;

        //状态
        private ActivityPrizeStatusEnum status;
        /**
         * 判断状态是否有效
         * @return
         */
        public Boolean valid() {
            return status.equals(ActivityPrizeStatusEnum.INIT);
        }
    }
    @Data
    public static class UserDTO {

        //用户id
        private Long userId;

        //用户名
        private String userName;

        //状态
        private ActivityUserStatusEnum status;
        /**
         * 判断状态是否有效
         * @return
         */
        public Boolean valid() {
            return status.equals(ActivityUserStatusEnum.INIT);
        }
    }
}

5.4 枚举类型管理属性

com/yj/lottery_system/service/enums 包下 :ActivityPrizeTiresEnum

  • 奖品等级只有一二三三个等级。
java 复制代码
package com.yj.lottery_system.service.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum ActivityPrizeTiresEnum {
    FIRST_PRIZE(1,"一等奖"),
    SECOND_PRIZE(2,"二等奖"),
    THIRD_PRIZE(3,"三等奖");

    private final Integer code;
    private final String message;
    public static ActivityPrizeTiresEnum forName(String name) {
        for(ActivityPrizeTiresEnum activityPrizeTiresEnum : ActivityPrizeTiresEnum.values()) {
            if(activityPrizeTiresEnum.name().equalsIgnoreCase(name)) {
                return activityPrizeTiresEnum;
            }
        }
        return null;
    }
}

com/yj/lottery_system/service/enums 包下:ActivityPrizeStatusEnum.

  • 活动关联奖品只有已被抽取和初始化两个状态
java 复制代码
package com.yj.lottery_system.service.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum ActivityPrizeStatusEnum {
    INIT(1,"初始化"),
    COMPLETED(2,"已被抽取");
    private final Integer code;
    private final String message;
    public static ActivityPrizeStatusEnum forName(String name) {
        for(ActivityPrizeStatusEnum activityPrizeStatusEnum : ActivityPrizeStatusEnum.values()) {
            if(activityPrizeStatusEnum.name().equalsIgnoreCase(name)) {
                return activityPrizeStatusEnum;
            }
        }
        return null;
    }
}

com.yj.lottery_system.service.enums 包下:ActivityStatusEnum

  • 活动只有进行中和活动已完成两个状态
java 复制代码
package com.yj.lottery_system.service.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum ActivityStatusEnum {
    RUNNING(1,"活动进行中"),
    COMPLETED(2,"活动已完成");
    private final Integer code;
    private final String message;
    public static ActivityStatusEnum forName(String name) {
        for(ActivityStatusEnum activityStatusEnum : ActivityStatusEnum.values()) {
            if(activityStatusEnum.name().equalsIgnoreCase(name)) {
                return activityStatusEnum;
            }
        }
        return null;
    }

}

com.yj.lottery_system.service.enums 包下:ActivityUserStatusEnum

  • 活动关联用户只有初始化和已被抽取两个状态
java 复制代码
package com.yj.lottery_system.service.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum ActivityUserStatusEnum {
    INIT(1,"初始化"),
    COMPLETED(2,"已被抽取");
    private final Integer code;
    private final String message;
    public static ActivityUserStatusEnum forName(String name) {
        for(ActivityUserStatusEnum activityUserStatusEnum : ActivityUserStatusEnum.values()) {
            if(activityUserStatusEnum.name().equalsIgnoreCase(name)) {
                return activityUserStatusEnum;
            }
        }
        return null;
    }
}

5.4 Redis缓存完整活动信息和拿到完整活动信息

com/yj/lottery_system/service/impl 包下:ActivityServiceImpl 类中:

  • 我们自己捕获异常,当缓存 失败我们去数据库中拿。
java 复制代码
/**
     * 缓存完整信息
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        // key = ACTIVITY_activityId value = ActivityDetailDTO(转为JSON)
        if(null == activityDetailDTO || null == activityDetailDTO.getActivityId()) {
            log.warn("要缓存的活动信息不存在");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX+ activityDetailDTO.getActivityId(), JacksonUtil.writeValueAsString(activityDetailDTO), ACTIVITY_TIMEOUT);
        }catch (Exception e) {
            log.error("缓存活动异常 activityDetailDTO:{}", JacksonUtil.writeValueAsString(activityDetailDTO),e);
        }

    }
    /**
     * 根据活动id获取缓存中的完整信息
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if(null == activityId) {
            log.warn("从缓存获取活动详细数据的活动id为空 activityId: {}",activityId);
            return null;
        }
        try {
            String s = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if(!StringUtils.hasText(s)) {
                log.info("从缓存获取活动详细数据为空  key: {}",ACTIVITY_PREFIX + activityId);
                return null;
            }
            return  JacksonUtil.readValue(s,ActivityDetailDTO.class);
        }catch (Exception e) {
            log.error("从缓存获取活动详细数据异常  key: {}",ACTIVITY_PREFIX + activityId,e);
            return null;
        }
    }

5.5 返回数据

com.yj.lottery_system.service.dto 包下:

-只需要返回活动id

java 复制代码
package com.yj.lottery_system.service.dto;

import lombok.Data;

@Data

public class CreateActivityDTO {
    //活动id
    private Long activityId;
}

5.6 新增错误码

com/yj/lottery_system/common/errorcode 包下 ServiceErrorCodeConstants层:

java 复制代码
    ErrorCode CREATE_ACTIVITY_INFO_IS_EMPTY = new ErrorCode(300,"创建活动信息为空");
    ErrorCode ACTIVITY_USER_ERROR = new ErrorCode(301,"活动关联人员异常");
    ErrorCode ACTIVITY_PRIZE_ERROR = new ErrorCode(302,"活动关联奖品异常");

    ErrorCode ACTIVITY_USER_PRIZE_COUNT_ERROR = new ErrorCode(302,"人员数量小于奖品数量");
    ErrorCode ACTIVITY_PRIZE_TIERS_ERROR = new ErrorCode(303,"活动奖品等级设置错误");

六、 dao层

6.1 根据传入id列表,查找数据库存在的id列表

com/yj/lottery_system/dao/mapper 包下 UserMapper 类:

java 复制代码
    /**
     * 根据前端传入的id列表查询列表中存在数据库的值
     * @param ids id列表
     * @return id列表中存在数据库的id值
     */
    @Select("<script> " +
            "select id from user  " +
            "where id in " +
            "<foreach item = 'item' collection='items' open='(' separator=',' close=')' > " +
            " #{item} " +
            "</foreach >" +
            "</script>")
    List<Long> selectExitsByIds(@Param("items") List<Long> ids);

com/yj/lottery_system/dao/mapper 包下:PrizeMapper类

java 复制代码
    /**
     * 根据前端传入的id列表查询列表中存在数据库的值
     * @param ids id列表
     * @return id列表中存在数据库的id值
     */
    @Select("<script> " +
            "select id from prize  " +
            "where id in " +
            "<foreach item = 'item' collection='items' open='(' separator=',' close=')' > " +
            " #{item} " +
         	"</foreach >" +
            "</script>")
    List<Long> selectExitsByIds(@Param("items") List<Long> ids);

6.2 根据传入List批量创建数据

com.yj.lottery_system.dao.mapper 包下:

java 复制代码
package com.yj.lottery_system.dao.mapper;

import com.yj.lottery_system.dao.dataObject.ActivityPrizeDO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface ActivityPrizeMapper {

    @Insert("<script> " +
            "insert into activity_prize  " +
            "(activity_id, prize_amount, prize_id, prize_tiers,status) " +
            "values <foreach item = 'item' collection='items' index='index' separator=','  > " +
            " (#{item.activityId},#{item.prizeAmount},#{item.prizeId},#{item.prizeTiers},#{item.status}) " +
            "</foreach>" +
            "</script>")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    int batchInsert(@Param("items") List<ActivityPrizeDO> activityPrizeDOList);

}

com.yj.lottery_system.dao.mapper 包下:

java 复制代码
package com.yj.lottery_system.dao.mapper;

import com.yj.lottery_system.dao.dataObject.ActivityUserDO;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface ActivityUserMapper {
    @Insert("<script> " +
            "insert into activity_user  " +
            "(activity_id, user_id, user_name, status) " +
            "values <forench item = 'item' collection='items' index='index' separator=','  > " +
            " (#{item.activityId},#{item.userId},#{item.userName},#{item.status}) " +
            "</forench>" +
            "</script>")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    int batchInsert(@Param("items")List<ActivityUserDO> activityUserDOList);
}

6.3 数据库字段对应类属性

com.yj.lottery_system.dao.dataObject 包下:

  • activity_prize表对应类
java 复制代码
package com.yj.lottery_system.dao.dataObject;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class ActivityPrizeDO extends BaseDO{
    //关联的活动id
    private Long activityId;

    //关联的奖品的数量
    private Long prizeAmount;

    //关联的奖品id
    private Long prizeId;

    //关联的奖品等级
    private String prizeTiers;

    //关联的奖品状态
    private String status;
}

com.yj.lottery_system.dao.dataObject 包下:

  • activity-user表对应类
java 复制代码
package com.yj.lottery_system.dao.dataObject;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class ActivityUserDO extends BaseDO{
    //关联的活动id
    private Long activityId;

    //关联的人员id
    private Long userId;

    //关联的人员姓名
    private String userName;

    //关联的人员状态
    private String status;
}

6.3 入的id列表查询列表中存在数据库的奖品信息

com/yj/lottery_system/dao 包下 :mapper/PrizeMapper类中

java 复制代码
    /**
     * 根据前端传入的id列表查询列表中存在数据库的奖品信息
     * @param prizeIds id列表
     * @return id列表中对应的数据库完整字段信息
     */
    @Select("<script> " +
            "select * from prize  " +
            "where id in " +
            "<foreach item = 'item' collection='items' open='(' separator=',' close=')' > " +
            " #{item} " +
             "</foreach>" +
            "</script>")
    List<PrizeDO> batchSelectByIds(@Param("items") List<Long> prizeIds);

七、前端:

static/create-activity.html :

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>创建抽奖活动</title>
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.5.2/css/bootstrap.min.css">
    <style>
        body{font-family:Arial,sans-serif;background:#fff;margin:0;padding:0}
        .container{max-width:800px;margin:30px auto;padding:20px 30px;background:#fff}
        .modal{display:none;position:fixed;z-index:1;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,.1);overflow-y:auto}
        .modal-content{background:#fefefe;margin:5% auto;padding:20px;border:1px solid #888;width:610px}
        .modal-content h2{display:flex;justify-content:space-between;align-items:center;font-weight:600;font-size:18px;border-bottom:1px solid #dedede;margin-bottom:20px}
        .close{font-size:28px;cursor:pointer}
        #prizesContainer{height:306px;overflow-y:auto;padding:0 26px;margin-bottom:10px}
        .prize-item{display:flex;align-items:center;margin-bottom:10px}
        .prize-item input[type=checkbox]{margin-right:20px}
        .prize-item label{margin-right:11px;width:160px;margin-bottom:0}
        .prize-item select{margin-left:auto}
        .pagination{display:flex;justify-content:center;align-items:center;margin-top:10px}
        .pagination button{margin:0 5px;border-radius:5px;border:1px solid #007bff;background:#fff;padding:2px 8px;cursor:pointer;font-size:13px}
        .pagination input{width:60px;text-align:center}
    </style>
</head>
<body>
<div class="container">
    <h2 style="text-align:center;margin-bottom:30px">创建抽奖活动</h2>
    <form id="activityForm">
        <div class="form-group">
            <label>活动名称</label>
            <input type="text" class="form-control" id="activityName" name="activityName" placeholder="请输入活动名称" required>
        </div>
        <div class="form-group">
            <label>活动描述</label>
            <textarea class="form-control" id="description" name="description" rows="5" placeholder="请输入活动描述" required></textarea>
        </div>
        <div class="form-group text-center">
            <button type="button" class="btn btn-primary" onclick="showPrizesModal()">圈选奖品</button>
            <button type="button" class="btn btn-primary" onclick="showUsersModal()">圈选人员</button>
            <button type="submit" class="btn btn-success">创建活动</button>
        </div>
    </form>
</div>

<!-- 圈选奖品模态框 -->
<div id="prizesModal" class="modal">
    <div class="modal-content">
        <h2>选择奖品<span class="close" onclick="hidePrizesModal()">&times;</span></h2>
        <div id="prizesContainer"></div>
        <div class="pagination">
            <button onclick="fetchPrizesModal(1)">首页</button>
            <button onclick="prevPrizePage()">上一页</button>
            <span>第<input id="modalPageInput" type="number" min="1" value="1">页</span>
            <button onclick="nextPrizePage()">下一页</button>
            <button onclick="fetchPrizesModal(totalPrizePages)">尾页</button>
        </div>
        <div class="text-center mt-3">
            <button class="btn btn-secondary" onclick="hidePrizesModal()">取消</button>
            <button class="btn btn-primary" onclick="confirmPrizes()">确定</button>
        </div>
    </div>
</div>

<!-- 圈选人员模态框 -->
<div id="usersModal" class="modal">
    <div class="modal-content">
        <h2>选择人员<span class="close" onclick="hideUsersModal()">&times;</span></h2>
        <div id="usersContainer"></div>
        <div class="text-center mt-3">
            <button class="btn btn-secondary" onclick="hideUsersModal()">取消</button>
            <button class="btn btn-primary" onclick="confirmUsers()">确定</button>
        </div>
    </div>
</div>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
    const userToken = localStorage.getItem("user_token");
    let selectedPrizes = [], selectedUsers = [];

    /* ========== 奖品分页 ========== */
    let modalCurrentPage = 1;
    const modalPageSize = 5;
    let totalPrizePages = 1;

    function showPrizesModal() {
        $('#prizesModal').show();
        fetchPrizesModal(1);
    }
    function hidePrizesModal() { $('#prizesModal').hide(); }

    function fetchPrizesModal(page) {
        if (page < 1) page = 1;
        modalCurrentPage = page;
        $.get('/prize/find-list', { currentPage: modalCurrentPage, pageSize: modalPageSize }, function (res) {
            if (res.code !== 200) { alert(res.msg); return; }
            const list = res.data.records;
            totalPrizePages = Math.ceil(res.data.total / modalPageSize);
            $('#modalPageInput').val(modalCurrentPage);
            const box = $('#prizesContainer').empty();
            list.forEach(p => {
                const checked = selectedPrizes.some(i => i.prizeId === p.prizeId) ? 'checked' : '';
                box.append(`
                    <div class="prize-item">
                        <input type="checkbox" value="${p.prizeId}" ${checked}>
                        <label>${p.prizeName}</label>
                        <input type="number" class="form-control" value="1" min="1" style="width:80px">
                        <select class="form-control" style="width:100px">
                            <option value="FIRST_PRIZE">一等奖</option>
                            <option value="SECOND_PRIZE">二等奖</option>
                            <option value="THIRD_PRIZE">三等奖</option>
                        </select>
                    </div>`);
            });
        }, 'json').fail(function () { alert("加载奖品失败"); });
    }
    function prevPrizePage() {
        if (modalCurrentPage > 1) fetchPrizesModal(modalCurrentPage - 1);
        else alert("已是第一页");
    }
    function nextPrizePage() {
        if (modalCurrentPage < totalPrizePages) fetchPrizesModal(modalCurrentPage + 1);
        else alert("已是最后一页");
    }
    $('#modalPageInput').on('keypress', function (e) {
        if (e.key === 'Enter') {
            const p = parseInt(this.value, 10);
            if (!isNaN(p) && p >= 1 && p <= totalPrizePages) fetchPrizesModal(p);
        }
    });

    function confirmPrizes() {
        selectedPrizes = [];
        $('#prizesContainer .prize-item').each(function () {
            const chk = $(this).find('input[type=checkbox]');
            if (chk.is(':checked')) {
                selectedPrizes.push({
                    prizeId: chk.val(),
                    prizeAmount: $(this).find('input[type=number]').val(),
                    prizeTiers: $(this).find('select').val()
                });
            }
        });
        hidePrizesModal();
    }

    /* ========== 人员选择(原逻辑不变) ========== */
    function showUsersModal() {
        $('#usersModal').show();
        $.get('/base-user/find-list', { identity: 'NORMAL' }, function (res) {
            if (res.code !== 200) { alert(res.msg); return; }
            const box = $('#usersContainer').empty();
            res.data.forEach(u => {
                box.append(`
                    <div class="user-item">
                        <input type="checkbox" value="${u.userId}" ${selectedUsers.some(i => i.userId === u.userId) ? 'checked' : ''}>
                        <label>${u.userName}</label>
                    </div>`);
            });
        }, 'json').fail(() => alert("加载人员失败"));
    }
    function hideUsersModal() { $('#usersModal').hide(); }
    function confirmUsers() {
        selectedUsers = [];
        $('#usersContainer .user-item').each(function () {
            const chk = $(this).find('input[type=checkbox]');
            if (chk.is(':checked')) selectedUsers.push({ userId: chk.val(), userName: $(this).find('label').text() });
        });
        hideUsersModal();
    }

    /* ========== 提交活动 ========== */
    $('#activityForm').on('submit', function (e) {
        e.preventDefault();
        if (selectedPrizes.length === 0) { alert("请至少选择一个奖品"); return; }
        if (selectedUsers.length === 0) { alert("请至少选择一个人员"); return; }
        const data = {
            activityName: $('#activityName').val(),
            description: $('#description').val(),
            activityPrizeList: selectedPrizes,
            activityUserList: selectedUsers
        };
        $.ajax({
            url: '/activity/create',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(data),
            headers: { user_token: userToken },
            success: function (r) {
                if (r.code === 200) {
                    alert("创建成功!");
                    window.parent.postMessage({ from: 'create-activity.html' }, '*');
                } else alert("创建失败:" + r.msg);
            },
            error: () => alert("网络异常")
        });
    });
</script>
</body>
</html>
相关推荐
不平衡的叉叉树2 小时前
Mybatis常用动态SQL标签
java·mybatis
电子_咸鱼3 小时前
动态规划经典题解:单词拆分(LeetCode 139)
java·数据结构·python·算法·leetcode·线性回归·动态规划
李慕婉学姐3 小时前
【开题答辩过程】以《割草机器人工作管理系统的设计与开发》为例,不会开题答辩的可以进来看看
java·spring·机器人
青衫码上行3 小时前
【Java Web学习 | 第七篇】JavaScript(1) 基础知识1
java·开发语言·前端·javascript·学习
堕落年代3 小时前
Spring三级缓存通俗易懂讲解
java·spring·缓存
披着羊皮不是狼4 小时前
多用户博客系统搭建(1):表设计+登录注册接口
java·开发语言·springboot
WX-bisheyuange7 小时前
基于Spring Boot的教师个人成果管理系统的设计与实现
java·spring boot·后端
xunyan62348 小时前
面向对象(上)-封装性的引入
java·开发语言
脸大是真的好~8 小时前
黑马JAVAWeb-05 JDBC入门-预编译SQL-Mybatis入门-Mybatis日志输出-数据库连接池-增删改查-XML映射配置
java