主页 > 软件开发  > 

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

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); }

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

标签:

Spring项目-抽奖系统(实操项目-用户管理接口)(THREE)由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Spring项目-抽奖系统(实操项目-用户管理接口)(THREE)