当前位置: 首页 > article >正文

Spring项目-抽奖系统(实操项目-用户管理接口)(THREE)

  ^__^
 (oo)\______

 (__)\      )\/\
     ||----w |
     ||     ||  

一、:前言:

        登录接口博客链接:Spring项目-抽奖系统(实操项目-登录接口)(TWO)-CSDN博客

        写完注册和登录接口后,接下来就做抽奖奖品、抽奖活动相应的创建展示过程。

        UI页面如下:
        

接下俩将一一完成下列这些接口。

二:用户管理:

2.1:人员列表展示:

        我们在参与抽奖的人员列表中,必须能够做到展示所有抽奖人员,时序图如下:


2.1.1:Controller层:

        该层需要注意:
        1.参数identity(表示具体要展示什么身份),当不传时,依旧可以访问,只要JWT令牌通过!!

        2.当传入参数时,需要判断传入的身份是否在预知范围内,用UserIdentityEnum进行简单判断

@RequestMapping("/base-user/find-list")
    public CommonResult<List<UserBaseInfoResult>> findUserBaseInfoList(String identity) {
        log.info("findUserBaseInfoList FindUsersParam:{}",identity);
        List<UserDTO> userDTOList = null;
        if(!StringUtils.hasText(identity)) {
            userDTOList = userService.findUserList(null);
        }else if(UserIdentityEnum.forname(identity) != null) {
            userDTOList = userService.findUserList(identity);
        }
        return CommonResult.succcess(covertToFindUserListResult(userDTOList));
    }

    private List<UserBaseInfoResult> covertToFindUserListResult(List<UserDTO> userDTOList) {
            if(CollectionUtil.isEmpty(userDTOList)) {
                return Arrays.asList();
            }
            return userDTOList.stream().map(userDTO -> {
                UserBaseInfoResult userBaseInfoResult = new UserBaseInfoResult();
                userBaseInfoResult.setUserName(userDTO.getUserName());
                userBaseInfoResult.setIdentity(userDTO.getIdentity());
                userBaseInfoResult.setUserId(userDTO.getUserId());
                return userBaseInfoResult;
            }).collect(Collectors.toList());
    }

2.1.2:Service层:

    
@Service
public interface UserService {
    
    /**
    * 查找用户列表
    * 
    */
    List<UserDTO> findUserList(String identity);
}
ServiceImpl层: 
    @Override
    public List<UserDTO> findUserList(String identity) {
      /*  if(param == null) {
            throw new ServiceException(ServiceErrorCodeConstatns.IDENTITY_ERROR);
        }*/
        List<UserDO> userDOList = userMapper.selectUserListByIdentity(identity);
        return userDOList.stream().map(userDO -> {
            UserDTO userDTO = new UserDTO();
            userDTO.setUserId(userDO.getId());
            userDTO.setUserName(userDO.getUserName());
            userDTO.setIdentity(userDO.getIdentity());
            userDTO.setEmail(userDO.getEmail());
            userDTO.setPhoneNumber(userDO.getPhoneNumber().getPhoneNumber());
            return userDTO;
        }).collect(Collectors.toList());
    }

2.1.3:Dao层:

        由于identity不是必传项,因此此次需要用到动态sql,选用mybaits的xml形式实现动态sql:

        具体的实现方法如下:

步骤一:

          设置配置项:

# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件
mybatis.mapper-locations=classpath:mapper/**Mapper.xml

步骤二:

        创建mapper文件与xxxMapper.xml文件;

                   

步骤三:

填写xml文件中基础代码:

<?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.example.lotterysystemplus.dao.mapper.UserMapper">
    <select id="selectUserListByIdentity"  resultType="com.example.lotterysystemplus.dao.dataobject.UserDO">
        select * from user
    <if test="identity != null">
        where identity = #{identity}
    </if>
        order by id desc
    </select>
</mapper>

步骤五:

        编写UserMapper包中的相关代码:

   List<UserDO> selectUserListByIdentity(@Param("identity") String identity);

三:奖品模块:

        写完人员管理模块之后,就开始写奖品模块,

奖品模块分为两个板块:

1.设置奖品模块

2.奖品展示模块

UI界面如下:

        


首先编写创建奖品模块:

3.1:创建奖品:

首先写保存图片相关接口:

         添加配置项:

## 图⽚服务 ##
pic.local-path=D:/PIC
# spring boot3 升级配置名
spring.web.resources.static-locations=classpath:/static/,file:${pic.local-path}
3.1.1:controller层:
    @RequestMapping("/pic/upload")
    public String upLodePic(MultipartFile file) {
        return pictureService.savePicture(file);
    }
3.1.2:Service层:

存放图片的服务是为了后期创建奖品时发挥作用!!
创建PictureService:

public interface PictureService {

    /**
     * 保存图片
     * @param pic
     * @return
     */
    String savePicture(MultipartFile pic);
}
3.1.3:创建PictureServiceImpl:        
@Service
public class PictureServiceImpl implements PictureService {

    /**
     * 设置图片存放路径
     */
    @Value("${pic.local-path}")
    private String picLocalPath;
    @Override
    public String savePicture(MultipartFile pic) {
        File dir = new File(picLocalPath);
        if(!dir.exists()) {
            dir.mkdirs();
        }

        //获取文件名
        String fileName = pic.getOriginalFilename();
        assert fileName != null;

        //获取文件后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //生成随机数防止图片重名
        fileName = UUID.randomUUID()+suffixName;
        try {
            //设置图片路径
            pic.transferTo(new File(picLocalPath+"/"+fileName));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return fileName;
    }
}

3.2:创建奖品相关接口:

3.2.1:controller层:
        
    /**
     * RequestPart:用于接收表单数据 multipart/form-data
     * 创建奖品
     * @param param
     * @param picFile
     * @return
     */
    @RequestMapping("/prize/create")
    public CommonResult<Integer> createPrize(@RequestPart("param") @Valid CreatePrizeParam param,
                                          @RequestPart("prizePic") MultipartFile picFile) {
        log.info("createPrize CreatePrizeParam:{} MultipartFile:{}",
                JacksonUtil.writeValueAsString(param),picFile);
        return CommonResult.succcess(prizeService.createPrize(param,picFile));
    }

 注意:

        @RequestPart注解用于form-data格式的发送,但是对于文件的上传只有一个@RequestPart是远远不够的,还需要加一个序列化与发序列化的转化器:
        如果没有这个转换器,当我们在前端测试时,就会报此类错误:

        大概意思就是:

        前端给我们传过来的是数据类型是application/octet-stream,但是由于我们写了@RequestPart注解,希望接收到的是form-data表单格式数据,因此报错!!

        所以对于文件的传输前端传过来的默认是octet-stream格式,我们需要手动写一个转换器,代码如下:

/**
 * 实现二进制到对象的转换
 */
@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
    protected MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper)
    {
        // MediaType.APPLICATION_OCTET_STREAM 表⽰这个转换器⽤于处理⼆进制流数据,通常⽤于⽂件上传。
        super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
    }
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return false;
    }
    @Override
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
        return false;
    }
    @Override
    protected boolean canWrite(MediaType mediaType) {
        return false;
    }
}
3.2.2:service层:
@Service
public interface PrizeService {
    Integer createPrize(CreatePrizeParam param, MultipartFile file);
}
3.2.3:serviceimpl层:

        service层的具体实现。

@Service
public class PrizeServiceImpl implements PrizeService {
    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private PictureService pictureService;
    @Override
    public Integer createPrize(CreatePrizeParam param, MultipartFile file) {
        String fileName = pictureService.savePicture(file);
        PrizeDO prizeDO = new PrizeDO();
        prizeDO.setName(param.getPrizeName());
        prizeDO.setDescription(param.getDescription());
        prizeDO.setPrice(param.getPrice());
        prizeDO.setImageUrl(fileName);
        prizeMapper.insert(prizeDO);
        return prizeDO.getId();
    }
}

3.3:展示奖品+翻页功能:

        将创建好的奖品进行展示,从数据库获取信息,发送给前端进行展示,并且带有翻页功能:

        翻页功能我们通过sql中的limit语句进行操作。需要传入两个参数:offset、pagesize完相应的功能。

3.3.1:controller层:   
    @RequestMapping("/prize/find-list")
    public CommonResult<PrizeFindListResult> prizeFindList(@Valid PageListParam param) {
        log.info("prizeFindList PageListParam:{}",
                JacksonUtil.writeValueAsString(param));
        PrizeListDTO<PrizeDTO> prizeDTOList = prizeService.findPrizeList(param);
        return CommonResult.succcess(convertToPrizeList(prizeDTOList));
    }
3.3.2:service层:
@Service
public interface PrizeService {
    Integer createPrize(CreatePrizeParam param, MultipartFile file);

    PrizeListDTO<PrizeDTO> findPrizeList(PageListParam param);
}
3.3.3:  serviceImpl层:

        service具体实现:

    @Override
    public PrizeListDTO<PrizeDTO> findPrizeList(PageListParam param) {
        int count = prizeMapper.count();
        List<PrizeDO> prizeDOList = prizeMapper.queryPrizeByPage(param.offset(), param.getPageSize());
        List<PrizeDTO> prizeDTOList = new ArrayList<>();
        for (PrizeDO prizeDO:prizeDOList) {
            PrizeDTO prizeDTO = new PrizeDTO();
            prizeDTO.setId(prizeDO.getId());
            prizeDTO.setName(prizeDO.getName());
            prizeDTO.setDescription(prizeDO.getDescription());
            prizeDTO.setPrice(prizeDO.getPrice());
            prizeDTO.setImageUrl(prizeDO.getImageUrl());
            prizeDTOList.add(prizeDTO);
        }
        return new PrizeListDTO<>(count,prizeDTOList);
    }
3.3.4:Dao层:
    @Select("select count(1) from prize")
    int count();
    @Select("select * from prize order by id desc limit #{offset} , #{pageSize}")
    List<PrizeDO> queryPrizeByPage(@Param("offset") Integer offset,
                                   @Param("pageSize") Integer pageSize);

四:活动模块:

        创建完奖品之后,接下俩就需要设置好活动内容,最后开始仅从抽奖:

 

4.1:创建活动:

        前后端约定:   

[ 请求 ] /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": ""
}

4.1.1:controller层:

        有了前后端约定,就可以直接定义后端controller接口,接收类型、返回类型都表达的非常清楚:

public class ActivityController {
    private static final Logger logger = LoggerFactory.getLogger(ActivityController.class);
    @Resource
    private ActivityService createActivityService;

    /**
     * 创建活动
     * @param param
     * @return
     */
    @RequestMapping("/activity/create")
    public CommonResult<Long> createActivity(@Valid @RequestBody CreateActivityParam param) {
        logger.info("createActivity CreateActivityParam: " +
                JacksonUtil.writeValueAsString(param));
        CreateActivityDTO createActivit = createActivityService.createActivity(param);
        return CommonResult.succcess(createActivit.getActivityid());
    }
}

注:大家也可以根据自己的喜好可以对返回值在进行一层封装和判断!!

4.1.2: service层:


@Service
public interface ActivityService {
    public CreateActivityDTO createActivity(CreateActivityParam param);
}

serviceImpl:

        需要注意一个非常重要的一点:

        我们需要将创建好的数据放入redis缓存当中,提高后期抽奖后活动奖品展示的效率!!

        service层接口的具体实现:

 @Override
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息
        checkActivityInfo(param);
        //创建活动,并保存至数据库
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
        //关联奖品信息,并保存至数据库
        List<CreateActivityParam.CreatePrizeByActivityParam> createActivityParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = new ArrayList<>();
        createActivityParams.forEach(item->{
            ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
            activityPrizeDO.setActivityId(activityDO.getId());
            activityPrizeDO.setPrizeId(item.getPrizeId());
            activityPrizeDO.setPrizeAmount(item.getPrizeAmount());
            activityPrizeDO.setPrizeTiers(item.getPrizeTiers());
            activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
            activityPrizeDOList.add(activityPrizeDO);
        });
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //关联人员信息,并保存至数据库
        List<CreateActivityParam.CreateUserByActivityParam> createUserByActivityParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = new ArrayList<>();
        createUserByActivityParams.forEach(item->{
            ActivityUserDO activityUserDO = new ActivityUserDO();
            activityUserDO.setActivityId(activityDO.getId());
            activityUserDO.setUserId(item.getUserId());
            activityUserDO.setUserName(item.getUserName());
            activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
            activityUserDOList.add(activityUserDO);
        });
        activityUserMapper.batchInsert(activityUserDOList);
        //查询奖品信息列表
        //使用stream中的map方法映射:提取ActivityPrizeDO类型中的每个prizeid映射,并且使用distinct方法去重
        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .distinct().toList();
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //将活动以及关联的奖品缓存至redis
        cacheActivity(convertToActivityDetilDTO(activityDO,activityPrizeDOList,activityUserDOList,prizeDOList));
        //返回活动id
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityid(activityDO.getId());
        return createActivityDTO;
    }

//校验活动信息

private void checkActivityInfo(CreateActivityParam param) {
        //判断创建的活动是否为空
    if(param == null) {
        throw new ServiceException(ServiceErrorCodeConstatns.CREATE_ACTIVITY_IS_EMPTY);
    }
        //校验所选人员id是否存在
        List<Long> userIds = param.getActivityUserList()
                .stream().map(CreateActivityParam.CreateUserByActivityParam::getUserId)
                .distinct().toList();
        List<Long> existUserIds = userMapper.selectByIdsList(userIds);
        if(existUserIds == null) {
            throw new ServiceException(ServiceErrorCodeConstatns.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id->{
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstatns.ACTIVITY_USER_ERROR);
            }
        });

        //奖品id在奖品表中是否存在
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreateActivityParam.CreatePrizeByActivityParam::getPrizeId)
                .distinct().toList();
        List<Long> existPrizeIds = prizeMapper.selectByIdsList(prizeIds);
        if(existPrizeIds == null) {
            throw new ServiceException(ServiceErrorCodeConstatns.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id->{
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstatns.ACTIVITY_PRIZE_ERROR);
            }
        });

        //人员数量大于等于奖品数量
        int userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList().stream()
                .mapToLong(CreateActivityParam.CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstatns.USER_PRIZE_AMOUNT_ERROR);
        }

        //活动奖品等级有效性
        param.getActivityPrizeList().forEach(
                prize->{
                    if(null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                        throw new ServiceException(ServiceErrorCodeConstatns.ACTIVITY_PRIZE_TIERS_ERROR);
                    }
                }
        );
    }
    /**
     * 将所获取的详细信息存放至redis缓存
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        // key: ACTIVITY_+
        // value: ActivityDetailDTO(json)
        if(detailDTO == null || detailDTO.getActivityId() == null) {
            log.error("要缓存的活动信息不存在");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e) {
            logger.error("缓存活动异常,ActivityDetailDTO={}",
                    JacksonUtil.writeValueAsString(detailDTO), e);
        }
    }
}

4.1.3:dao层:

        与数据库进行直接接触的层:

mapper层代码实现:

@Mapper
public interface ActivityMapper {

    @Insert("insert into activity (activity_name, description, status)" +
            " values (#{activityName}, #{description}, #{status})")
    @Options(useGeneratedKeys = true, keyProperty ="id", keyColumn ="id")
    Integer insert( ActivityDO activityDO);


    @Select("select count(1) from activity")
    long count();

    @Select("select * from activity order by id desc limit #{offset} , #{pageSize}")
    List<ActivityDO> queryActivitiesByPage(@Param("offset") Long offset,
                                           @Param("pageSize") Long pageSize);
}

4.2:展示活动:

        

前后端交互约定:      

[ 请求 ] /activity/find-list?currentPage=1&pageSize=10 GET
[ 响应 ]
{
"code": 200,
"data": {
"total": 10,
"records": [
{
"activityId": 23,
"activityName": " 抽奖测试 ",
"description": " 年会抽奖活动 ",
"valid": true
},
{
"activityId": 22,
"activityName": " 抽奖测试 ",
"description": " 年会抽奖活动 ",
"valid": true
},
{
"activityId": 21,
"activityName": " 节⽇抽奖 ",
"description": " ⽐特年会抽奖活动 ",
"valid": true
}
]
},
"msg": ""
}

    4.2.1:controller层:

@RequestMapping("/activity/find-list")
     public CommonResult<FindActivityListResult> findActivityList(
             @Valid PageListParam param) {
            logger.info("findActivityList :{}",JacksonUtil.writeValueAsString(param));
            PageListDTO<ActivityDTO> pageListDTO = createActivityService.findActivityList(param);
            return CommonResult.succcess(covertToFindActivityList(pageListDTO));
     }
   private FindActivityListResult covertToFindActivityList(PageListDTO<ActivityDTO> pageListDTO) {
        if(pageListDTO == null) {
            throw new ControllerException(ControllerErrorCodeConstants.FIND_ACITVITY_LIST_ERROR);
        }
        FindActivityListResult activityListResult = new FindActivityListResult();
        activityListResult.setTotal(pageListDTO.getTotal());
        activityListResult.setRecords(
                pageListDTO.getRecords().stream()
                        .map(item->{
                         FindActivityListResult.FindActivityInfo findActivityInfo = new FindActivityListResult.FindActivityInfo();
                         findActivityInfo.setActivityId(item.getActivityId());
                         findActivityInfo.setActivityName(item.getActivityName());
                         findActivityInfo.setDescription(item.getDescription());
                         findActivityInfo.setValid(item.valid());
                         return findActivityInfo;
                        }).collect(Collectors.toList())
        );
        return activityListResult;
    }
}

4.2.2:service层:

        需要注意的是,展示活动列表,获取活动信息还是从数据库中去获取,因为redis中的信息有设置过期时间,所以为了稳定去数据库直接拿!

        当后期设计抽奖后展示详细信息时可以从redis中去快速拿!

@Service
public interface ActivityService {

    PageListDTO<ActivityDTO> findActivityList(PageListParam param);
}

serviceimpl层:

/**
     * 活动列表展示
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageListParam param) {
        //total总数
        long count = activityMapper.count();
        //获取活动列表
        List<ActivityDO> activityDOList = activityMapper.queryActivitiesByPage(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList = new ArrayList<>();
        activityDOList.forEach(activityDO -> {
            ActivityDTO activityDTO = new ActivityDTO();
            activityDTO.setActivityId(activityDO.getId());
            activityDTO.setActivityName(activityDO.getActivityName());
            activityDTO.setDescription(activityDO.getDescription());
            activityDTO.setStatus(ActivityStatusEnum.fromName(activityDO.getStatus()));
            activityDTOList.add(activityDTO);
        });
        return new PageListDTO<>(count,activityDTOList);
    }

4.2.3:dao层:

@Mapper
public interface ActivityMapper {

    @Insert("insert into activity (activity_name, description, status)" +
            " values (#{activityName}, #{description}, #{status})")
    @Options(useGeneratedKeys = true, keyProperty ="id", keyColumn ="id")
    Integer insert( ActivityDO activityDO);


    @Select("select count(1) from activity")
    long count();

    @Select("select * from activity order by id desc limit #{offset} , #{pageSize}")
    List<ActivityDO> queryActivitiesByPage(@Param("offset") Long offset,
                                           @Param("pageSize") Long pageSize);

    @Select("select * from activity where id = #{activityId}")
    ActivityDO selectByActivityId(@Param("activityId") Long activityId);
}

接下来就是重头戏了,对于整个抽奖的设计!!     
                            


http://www.kler.cn/a/568449.html

相关文章:

  • MySQL-基础篇学习总结(2025-03-02)
  • 现代未来派品牌海报设计液体装饰英文字体安装包 Booster – Liquid Font
  • 崩溃(Crash)简记
  • 鸿蒙日期格式工具封装及使用
  • ssm整合项目实现基础查询功能
  • SpringBoot3—核心特性:基础特性
  • 训练营总结篇
  • 【技海登峰】Kafka漫谈系列(三)详解Kafka的数据结构与存储机制
  • 人大金仓国产数据库与PostgreSQL
  • EA SPORTS FC 25 2000+ 大型MOD整合包
  • 使用python实现线性回归
  • 【量化金融自学笔记】--开篇.基本术语及学习路径建议
  • 分布式多卡训练(DDP)踩坑
  • 物联网 水质监测设备 顶级功能 集成小范围内 高度精确GPS
  • 从0搭建Tomcat第二天:深入理解Servlet容器与反射机制
  • 启动你的RocketMQ之旅(三)-Producer启动和发送流程(上)
  • SpringCloud系列教程(十):token验证
  • Hadoop之01:HDFS分布式文件系统
  • 纳米材料简介
  • 【VitePress】vitepress 中 markdown 写作使用