目录
- 一、简介
- 二、参数列表
- 三、接口规范
- [四、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()">×</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()">×</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>