hjl
2024-05-21 f0d8f535a4c09dee9d786d8fb13c9b9918addaa9
feat: 接口初始化
52个文件已修改
11个文件已添加
2837 ■■■■ 已修改文件
ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/factory/GoodsFallbackFactory.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/feignClient/GoodsClient.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/factory/TManagementFallbackFactory.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/feignClient/ManagementClient.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/factory/StudyFallbackFactory.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/feignClient/StudyClient.java 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/RedisConstants.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java 68 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/pom.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/RedissonConfig.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/TGoodsController.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/domain/Region.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodExchangeDTO.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodQueryDTO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TGoodsMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TOrderMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRecipientService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRegionService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITGoodsService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITOrderService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RecipientServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RegionServiceImpl.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TGoodsServiceImpl.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TOrderServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/RecipientMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TGoodsMapper.xml 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TOrderMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TPageController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TProtocolController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TUserController.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java 515 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TGameRecord.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TIntegralRecord.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteGameDTO.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteStudyDTO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/StudyWeekDTO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/TGoodsVO.java 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TIntegralRecordMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TStudyMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/request/RegisterPhoneRequest.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITGameRecordService.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITIntegralRecordService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITStudyService.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserStudyService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TGameRecordServiceImpl.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TIntegralRecordServiceImpl.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TStudyServiceImpl.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserStudyServiceImpl.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/task/StudyRecordTimedTask.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/utils/SendMsgUtils.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TIntegralRecordMapper.xml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TStudyMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/factory/GoodsFallbackFactory.java
@@ -16,8 +16,7 @@
public class GoodsFallbackFactory implements FallbackFactory<GoodsClient> {
    @Override
    public GoodsClient create(Throwable cause)
    {
    public GoodsClient create(Throwable cause) {
        return new GoodsClient() {
            @Override
@@ -69,6 +68,11 @@
            public R<List<TGoodsType>> getGoodsInfo() {
                return R.fail("获取商品类型列表" + cause.getMessage());
            }
            @Override
            public R<List<TGoodsVO>> goodRecommend(String userId) {
                return R.fail("获取商品推荐列表" + cause.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/feignClient/GoodsClient.java
@@ -10,7 +10,10 @@
import com.ruoyi.goods.api.model.TGoodsVO;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@@ -42,4 +45,15 @@
    @PostMapping("/base/goods/getGoodsTypeList")
     R<List<TGoodsType>> getGoodsInfo();
    /**
     * 可兑换商品推荐
     *
     * @param userId 用户id
     * @return 推荐商品信息
     */
    @GetMapping("/goodRecommend")
    @ApiOperation(value = "可兑换商品推荐", tags = {"可兑换商品推荐"})
    R<List<TGoodsVO>> goodRecommend(String userId);
}
ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/factory/TManagementFallbackFactory.java
@@ -16,7 +16,7 @@
/**
 * 门店服务降级处理
 *
 *
 * @author ruoyi
 */
@Component
@@ -47,6 +47,7 @@
            public R addFeedBack(TFeedback dto) {
                return R.fail("家长端发布意见反馈失败"+cause.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/feignClient/ManagementClient.java
@@ -21,6 +21,7 @@
    @PostMapping(value = "/tSysSet/getPage1")
    R<List<TPage>> getPage1();
    @PostMapping("/tUser/getVipSet1")
    R<List<TVipSet>> getVipSet1();
ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/factory/StudyFallbackFactory.java
@@ -2,6 +2,7 @@
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.study.api.domain.TUser;
import com.ruoyi.study.api.dto.*;
import com.ruoyi.study.api.feignClient.StudyClient;
import com.ruoyi.study.api.model.TStory;
@@ -123,6 +124,16 @@
            public R updateState1(Integer id, Integer state) {
                return R.fail("修改故事状态失败" + cause.getMessage());
            }
            @Override
            public R<TUser> userInfo() {
                return R.fail("获取用户信息失败" + cause.getMessage());
            }
            @Override
            public R<Boolean> addIntegralDetail(String integral, String method) {
                return R.fail("生成积分明细信息失败" + cause.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/feignClient/StudyClient.java
@@ -2,8 +2,8 @@
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.study.api.domain.TUser;
import com.ruoyi.study.api.dto.*;
import com.ruoyi.study.api.factory.StudyFallbackFactory;
import com.ruoyi.study.api.model.TStory;
@@ -12,10 +12,7 @@
import com.ruoyi.study.api.vo.*;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -24,48 +21,64 @@
    @PostMapping("/base/user/vipBack/{id}")
    R vipBack(@PathVariable("id") Integer id);
    @PostMapping("/base/user/userList")
    R<PageInfo<AppUserVO>> couponReceive(@RequestBody AppUserQuery query);
    @PostMapping("/base/user/getUserInfo")
     R<UserInfoVO> getUserInfo(@RequestBody UserInfoQuery dto);
    R<UserInfoVO> getUserInfo(@RequestBody UserInfoQuery dto);
    @PostMapping("/base/user/freeze/{id}")
     R freeze(@PathVariable("id") Integer id);
    R freeze(@PathVariable("id") Integer id);
    @PostMapping("/base/user/vipOrderList")
     R<PageInfo<VipOrderVO>> vipOrderList(@RequestBody AppUserQuery query);
    R<PageInfo<VipOrderVO>> vipOrderList(@RequestBody AppUserQuery query);
    /**
     * 选择故事列表查询
     *
     * @param query
     * @return
     */
    @PostMapping("/base/study/storyList")
    R<PageInfo<TStory>> storyList(@RequestBody ChoiceStory query);
    /**
     * 选择题目列表查询
     *
     * @param query
     * @return
     */
    @PostMapping("/base/study/subjectList")
    R<PageInfo<TSubject>> subjectList(@RequestBody ChoiceSubject query);
    /**
     * 新增/修改学习类型配置
     *
     * @return
     */
    @PostMapping("/base/study/addStudySet")
     R<Object> addStudySet(@RequestBody AddStudySetDTO dto);
    R<Object> addStudySet(@RequestBody AddStudySetDTO dto);
    /**
     * 通过类型、周目、day查询学习配置
     *
     * @return
     */
    @PostMapping("/base/study/getStudySet")
    R<StudyVO> getStudySet(@RequestBody StudyDTO dto);
    /**
     * 学习类型列表查询
     *
     * @return
     */
    @PostMapping("/base/study/getStudyList")
    R<List<StudyListVO>> getStudyList();
    /**
     * 添加周目
     *
     * @return
     */
    @PostMapping("/base/study/addWeek")
@@ -73,19 +86,24 @@
    /**
     * 题目管理列表查询
     *
     * @param query
     * @return
     */
    @PostMapping("/base/tSubject/subjectList")
    R<PageInfo<SubjectVO>> subjectList(@RequestBody SubjectQuery query);
    /**
     * 题目管理添加
     *
     * @return
     */
    @PostMapping("/base/tSubject/add")
    R add(@RequestBody SubjectDTO dto) ;
    R add(@RequestBody SubjectDTO dto);
    /**
     * 题目管理编辑
     *
     * @return
     */
    @PostMapping("/base/tSubject/update")
@@ -93,13 +111,16 @@
    /**
     * 查看详情
     *
     * @param id
     * @return
     */
    @PostMapping("/base/tSubject/getInfo")
    R<SubjectDTO> getInfo(@RequestParam("id") Integer id);
    /**
     * 修改题目状态
     *
     * @param id
     * @return
     */
@@ -108,6 +129,7 @@
    /**
     * 故事管理列表查询
     *
     * @param query
     * @return
     */
@@ -116,20 +138,25 @@
    /**
     * 故事管理添加
     *
     * @param dto
     * @return
     */
    @PostMapping("/base/tStory/add")
    R add(@RequestBody StoryDTO dto);
    /**
     * 故事管理编辑
     *
     * @param dto
     * @return
     */
    @PostMapping("/base/tStory/update")
    R update(@RequestBody StoryDTO dto);
    /**
     * 故事管理查看详情
     *
     * @return
     */
    @PostMapping("/base/tStory/getInfo")
@@ -137,10 +164,30 @@
    /**
     * 故事管理修改状态
     *
     * @param id
     * @param state
     * @return
     */
    @PostMapping("/base/tStory/updateState/{id}/{state}")
    R updateState1(@PathVariable("id") Integer id,@PathVariable("state")Integer state);
    R updateState1(@PathVariable("id") Integer id, @PathVariable("state") Integer state);
    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @GetMapping("/base/user/userInfo")
    @ApiOperation(value = "用户详情", tags = {"用户详情"})
    R<TUser> userInfo();
    /**
     * 生成积分明细-用于远程调用
     *
     * @param integral 积分变动信息
     * @param method   变动源
     */
    @GetMapping("/base/study/addIntegralDetail")
    R<Boolean> addIntegralDetail(@RequestParam("integral") String integral, @RequestParam("method") String method);
}
ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java
@@ -44,6 +44,7 @@
    @Autowired
    private SysUserClient userClient;
    @PostMapping("login")
    public R<?> login(@RequestBody LoginBody form)
    {
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
@@ -2,11 +2,10 @@
/**
 * 通用常量信息
 *
 *
 * @author ruoyi
 */
public class Constants
{
public class Constants {
    /**
     * UTF-8 字符集
     */
@@ -120,7 +119,7 @@
    /**
     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
     */
    public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
    public static final String[] JOB_WHITELIST_STR = {"com.ruoyi"};
    /**
     * 时间格式化
@@ -131,6 +130,47 @@
    /**
     * 定时任务违规的字符
     */
    public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
            "org.springframework", "org.apache", "com.ruoyi.common.core.utils.file" };
    public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
            "org.springframework", "org.apache", "com.ruoyi.common.core.utils.file"};
    /**
     * 60数字
     */
    public static final Integer SIXTY = 60;
    /**
     * 1000数字
     */
    public static final Integer ONE_THOUSAND = 1000;
    /**
     * 积分明细 符号 -
     */
    public static final String BURDEN = "-";
    /**
     * 积分明细来源 商城消费
     */
    public static final String SHOPPING_CONSUME = "商城消费";
    /**
     * 手机号码正则
     */
    public static final String PHONE = ("^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\\d{8}$");
    /**
     * 日
     */
    public static final String DAY = "day";
    /**
     * 周
     */
    public static final String WEEK = "week";
    /**
     * 月
     */
    public static final String MONTH = "month";
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/RedisConstants.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.common.core.constant;
/**
 * redis key常量
 *
 * @author HJL
 * @version 1.0
 * @since 2024-05-17 10:26
 */
public class RedisConstants {
    /**
     * 学习端 验证码登录key
     */
    public final static String PHONE_CODE = "phone_code:";
    /**
     * 省市区三级树
     */
    public final static String ADDRESS_TREE = "regin_tree";
    /**
     * 商品库存key前缀
     */
    public final static String GOOD_STOCK = "good_stock:%s";
    /**
     * 游戏项目
     */
    public final static String HEARING_TREE = "game_subject";
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java
@@ -1,12 +1,13 @@
package com.ruoyi.common.core.utils;
import java.util.Map;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.TokenConstants;
import com.ruoyi.common.core.text.Convert;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Map;
/**
 * Jwt工具类
@@ -64,6 +65,18 @@
    }
    /**
     * 学习端根据令牌获取用户标识
     *
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserKeyStudy(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.USER_STUDY_KEY);
    }
    /**
     * 根据令牌获取用户标识
     * 
     * @param claims 身份信息
ruoyi-common/ruoyi-common-redis/pom.xml
@@ -16,6 +16,13 @@
    </description>
    <dependencies>
        <!--redission依赖-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.6</version>
        </dependency>
        
        <!-- SpringBoot Boot Redis -->
        <dependency>
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java
@@ -12,18 +12,16 @@
/**
 * redis配置
 *
 *
 * @author ruoyi
 */
@Configuration
@EnableCaching
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport
{
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
    @SuppressWarnings(value = {"unchecked", "rawtypes"})
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java
@@ -1,9 +1,14 @@
package com.ruoyi.common.security.handler;
import javax.naming.SizeLimitExceededException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileUploadBase;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.exception.DemoModeException;
import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.core.exception.InnerAuthException;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.exception.auth.NotPermissionException;
import com.ruoyi.common.core.exception.auth.NotRoleException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@@ -12,25 +17,17 @@
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.exception.DemoModeException;
import com.ruoyi.common.core.exception.InnerAuthException;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.exception.auth.NotPermissionException;
import com.ruoyi.common.core.exception.auth.NotRoleException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import javax.servlet.http.HttpServletRequest;
/**
 * 全局异常处理器
 *
 *
 * @author ruoyi
 */
@RestControllerAdvice
public class GlobalExceptionHandler
{
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @Value("${spring.servlet.multipart.max-file-size:4MB}")
@@ -43,8 +40,7 @@
     * 权限码异常
     */
    @ExceptionHandler(NotPermissionException.class)
    public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request)
    {
    public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
        return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权");
@@ -54,8 +50,7 @@
     * 角色权限异常
     */
    @ExceptionHandler(NotRoleException.class)
    public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request)
    {
    public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
        return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权");
@@ -66,8 +61,7 @@
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
            HttpServletRequest request)
    {
                                                          HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return AjaxResult.error(e.getMessage());
@@ -77,8 +71,7 @@
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
    {
    public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
@@ -88,8 +81,7 @@
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
    {
    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
@@ -99,8 +91,7 @@
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e, HttpServletRequest request)
    {
    public AjaxResult handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
@@ -110,8 +101,7 @@
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult handleBindException(BindException e)
    {
    public AjaxResult handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.error(message);
@@ -121,8 +111,7 @@
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
@@ -132,8 +121,7 @@
     * 内部认证异常
     */
    @ExceptionHandler(InnerAuthException.class)
    public AjaxResult handleInnerAuthException(InnerAuthException e)
    {
    public AjaxResult handleInnerAuthException(InnerAuthException e) {
        return AjaxResult.error(e.getMessage());
    }
@@ -141,8 +129,7 @@
     * 演示模式异常
     */
    @ExceptionHandler(DemoModeException.class)
    public AjaxResult handleDemoModeException(DemoModeException e)
    {
    public AjaxResult handleDemoModeException(DemoModeException e) {
        return AjaxResult.error("演示模式,不允许操作");
    }
@@ -151,4 +138,13 @@
        log.error("上传文件异常 => : {}", e.getMessage());
        return AjaxResult.error("文件识别大小超出限制,允许的大小在" + maxFileSize);
    }
    /**
     * 捕获全局自定义异常
     */
    @ExceptionHandler(GlobalException.class)
    public AjaxResult globalExceptionHandler(GlobalException e) {
        return AjaxResult.error(e.getMessage());
    }
}
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
@@ -1,13 +1,5 @@
package com.ruoyi.common.security.service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import com.ruoyi.system.api.model.LoginUserParent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.utils.JwtUtils;
@@ -18,10 +10,18 @@
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.model.LoginUser;
import com.ruoyi.system.api.model.LoginUserParent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * token验证处理
 *
 *
 * @author ruoyi
 */
@Component
@@ -85,6 +85,27 @@
        rspMap.put("expires_in", expireTime);
        return rspMap;
    }
    public Map<String, Object> createTokenStudy(LoginUserParent loginUser)
    {
        String token = IdUtils.fastUUID();
        Integer userId = loginUser.getUserid();
        String name = loginUser.getName();
        loginUser.setToken(token);
        loginUser.setIpaddr(IpUtils.getIpAddr());
        refreshTokenStudy(loginUser);
        // Jwt存储信息
        Map<String, Object> claimsMap = new HashMap<String, Object>(8);
        claimsMap.put(SecurityConstants.USER_STUDY_KEY, token);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, name);
        // 接口返回信息
        Map<String, Object> rspMap = new HashMap<String, Object>();
        rspMap.put("access_token", JwtUtils.createToken(claimsMap));
        rspMap.put("expires_in", expireTime);
        return rspMap;
    }
    /**
     * 获取用户身份信息
     *
@@ -117,6 +138,16 @@
    }
    /**
     * 学习端获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUserParent getLoginUserStudy()
    {
        return getLoginUserStudy(ServletUtils.getRequest());
    }
    /**
     * 获取用户身份信息
     *
     * @return 用户信息
@@ -126,6 +157,18 @@
        // 获取请求携带的令牌
        String token = SecurityUtils.getToken(request);
        return getLoginUser1(token);
    }
    /**
     * 学习端获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUserParent getLoginUserStudy(HttpServletRequest request)
    {
        // 获取请求携带的令牌
        String token = SecurityUtils.getToken(request);
        return getLoginUserStudy(token);
    }
    /**
@@ -163,6 +206,30 @@
            if (StringUtils.isNotEmpty(token))
            {
                String userkey = JwtUtils.getUserKey1(token);
                user = redisService.getCacheObject(getTokenKey(userkey));
                return user;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return user;
    }
    /**
     * 学习端 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUserParent getLoginUserStudy(String token)
    {
        LoginUserParent user = null;
        try
        {
            if (StringUtils.isNotEmpty(token))
            {
                String userkey = JwtUtils.getUserKeyStudy(token);
                user = redisService.getCacheObject(getTokenKey(userkey));
                return user;
            }
@@ -249,6 +316,18 @@
        redisService.setCacheObject(userKey, dto, expireTime, TimeUnit.MINUTES);
    }
    /**
     * 学习端用户登录
     */
    public void refreshTokenStudy(LoginUserParent dto)
    {
        dto.setLoginTime(System.currentTimeMillis());
        dto.setExpireTime(dto.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(dto.getToken());
        redisService.setCacheObject(userKey, dto, expireTime, TimeUnit.MINUTES);
    }
    private String getTokenKey(String token)
    {
        return ACCESS_TOKEN + token;
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java
@@ -1,14 +1,5 @@
package com.ruoyi.gateway.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.constant.SecurityConstants;
@@ -19,16 +10,24 @@
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
 * 网关鉴权
 *
 *
 * @author ruoyi
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered
{
public class AuthFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
    // 排除过滤的 uri 地址,nacos自行添加
@@ -40,37 +39,31 @@
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
    {
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();
        String url = request.getURI().getPath();
        // 跳过不需要验证的路径
        if (StringUtils.matches(url, ignoreWhite.getWhites()))
        {
        if (StringUtils.matches(url, ignoreWhite.getWhites())) {
            return chain.filter(exchange);
        }
        String token = getToken(request);
        if (StringUtils.isEmpty(token))
        {
        if (StringUtils.isEmpty(token)) {
            return unauthorizedResponse(exchange, "令牌不能为空");
        }
        Claims claims = JwtUtils.parseToken(token);
        if (claims == null)
        {
        if (claims == null) {
            return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
        }
        String userkey = JwtUtils.getUserKey(claims);
        boolean islogin = redisService.hasKey(getTokenKey(userkey));
        if (!islogin)
        {
        if (!islogin) {
            return unauthorizedResponse(exchange, "登录状态已过期");
        }
        String userid = JwtUtils.getUserId(claims);
        String username = JwtUtils.getUserName(claims);
        if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))
        {
        if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
            return unauthorizedResponse(exchange, "令牌验证失败");
        }
@@ -83,10 +76,8 @@
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }
    private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
    {
        if (value == null)
        {
    private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
        if (value == null) {
            return;
        }
        String valueStr = value.toString();
@@ -94,13 +85,11 @@
        mutate.header(name, valueEncode);
    }
    private void removeHeader(ServerHttpRequest.Builder mutate, String name)
    {
    private void removeHeader(ServerHttpRequest.Builder mutate, String name) {
        mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
    }
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg)
    {
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
    }
@@ -108,28 +97,24 @@
    /**
     * 获取缓存key
     */
    private String getTokenKey(String token)
    {
    private String getTokenKey(String token) {
        return CacheConstants.LOGIN_TOKEN_KEY + token;
    }
    /**
     * 获取请求token
     */
    private String getToken(ServerHttpRequest request)
    {
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
        // 如果前端设置了令牌前缀,则裁剪掉前缀
        if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
        {
        if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
            token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
        }
        return token;
    }
    @Override
    public int getOrder()
    {
    public int getOrder() {
        return -200;
    }
}
ruoyi-service/ruoyi-goods/pom.xml
@@ -16,6 +16,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-study</artifactId>
            <version>3.6.2</version>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
@@ -98,6 +104,12 @@
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <!--redission依赖-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.6</version>
        </dependency>
        <!--mybatis-plus-->
        <!--<dependency>
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/RedissonConfig.java
New file
@@ -0,0 +1,30 @@
package com.ruoyi.goods.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author HJL
 * @version 1.0
 * @since 2024-05-20 15:27
 */
@Configuration
public class RedissonConfig {
    /**
     * 创建客户端
     *
     * @return 客户端连接
     */
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        return Redisson.create(config);
    }
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/TGoodsController.java
@@ -4,13 +4,12 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.goods.domain.TGoods;
import com.ruoyi.goods.domain.TGoodsType;
import com.ruoyi.goods.domain.TOrder;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.goods.domain.*;
import com.ruoyi.goods.dto.GoodExchangeDTO;
import com.ruoyi.goods.dto.GoodQueryDTO;
import com.ruoyi.goods.dto.GoodsTypeQuery;
import com.ruoyi.goods.service.ITGoodsService;
import com.ruoyi.goods.service.ITGoodsTypeService;
import com.ruoyi.goods.service.ITOrderService;
import com.ruoyi.goods.service.*;
import com.ruoyi.goods.vo.TGoodsVO;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
@@ -18,7 +17,11 @@
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -31,17 +34,22 @@
@RestController
@RequestMapping("/base/goods")
public class TGoodsController {
    @Autowired
    @Resource
    private ITGoodsService goodsService;
    @Autowired
    private ITGoodsTypeService goodsTypeService;
    @Autowired
    private ITOrderService orderService;
    @Resource
    private IRecipientService recipientService;
    @Resource
    private IRegionService regionService;
    @PostMapping("/listType")
    @ApiOperation(value = "列表查询", tags = {"后台-商品类型管理"})
    public R<PageInfo<TGoodsType>> listType(@RequestBody GoodsTypeQuery query){
    public R<PageInfo<TGoodsType>> listType(@RequestBody GoodsTypeQuery query) {
        QueryWrapper<TGoodsType> wrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(query.getName())){
        if (StringUtils.hasLength(query.getName())) {
            wrapper.like("name", query.getName());
        }
//        wrapper.ne("isDelete",1);
@@ -51,31 +59,49 @@
        res.setRecords(list);
        return R.ok(res);
    }
    @PostMapping("/goodList")
    @ApiOperation(value = "商品列表查询", tags = {"学习端-商品列表"})
    public R<PageInfo<TGoods>> listType(@RequestBody GoodQueryDTO goodQuery) {
        List<String> type = goodQuery.getType();
        String keywords = goodQuery.getKeywords();
        // 初始化条件构造器
        QueryWrapper<TGoods> wrapper = new QueryWrapper<>();
        wrapper = keywords != null && "".equals(keywords.trim()) ? wrapper.like("name", keywords) : wrapper;
        // 类型匹配 todo
        wrapper = type.isEmpty() ? wrapper : wrapper.in("");
        wrapper.eq("isDelete", 0);
        return R.ok(goodsService.page(new PageInfo<>(goodQuery.getPageNumber(), goodQuery.getPageSize()), wrapper));
    }
    @PostMapping("/addGoodsType")
    @ApiOperation(value = "添加", tags = {"后台-商品类型管理"})
    public R addGoodsType(@RequestBody TGoodsType dto) {
        goodsTypeService.save(dto);
        return R.ok("添加成功");
    }
    @PostMapping("/updateGoodsType")
    @ApiOperation(value = "修改", tags = {"后台-商品类型管理"})
    public R updateGoodsType(@RequestBody TGoodsType dto) {
        goodsTypeService.updateById(dto);
        return R.ok("修改成功");
    }
    @PostMapping("/deleteGoodsType/{id}")
    @ApiOperation(value = "删除", tags = {"后台-商品类型管理"})
    public R deleteGoodsType(@PathVariable("id")Integer id) {
    public R deleteGoodsType(@PathVariable("id") Integer id) {
        TGoodsType byId = goodsTypeService.getById(id);
        byId.setIsDelete(1);
        goodsTypeService.removeById(byId);
        return R.ok("删除成功");
    }
    @PostMapping("/listAll")
    @ApiOperation(value = "列表查询", tags = {"后台-商品管理"})
    public R<PageInfo<TGoods>> listAll(@RequestBody GoodsTypeQuery query){
    public R<PageInfo<TGoods>> listAll(@RequestBody GoodsTypeQuery query) {
        QueryWrapper<TGoods> wrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(query.getName())){
        if (StringUtils.hasLength(query.getName())) {
            wrapper.like("name", query.getName());
        }
        wrapper.orderByDesc("id");
@@ -88,12 +114,14 @@
        res.setRecords(list);
        return R.ok(res);
    }
    @PostMapping("/addGoods")
    @ApiOperation(value = "添加", tags = {"后台-商品管理"})
    public R addGoods(@RequestBody TGoods dto) {
        goodsService.save(dto);
        return R.ok("添加成功");
    }
    @PostMapping("/deleteGoods/{id}")
    @ApiOperation(value = "删除", tags = {"后台-商品管理"})
    public R deleteGoods(@PathVariable("id") Integer id) {
@@ -101,29 +129,147 @@
        goodsService.removeById(byId);
        return R.ok("删除成功");
    }
    @PostMapping("/updateGoods")
    @ApiOperation(value = "修改", tags = {"后台-商品管理"})
    public R updateGoods(@RequestBody TGoods dto) {
        goodsService.updateById(dto);
        return R.ok("修改成功");
    }
    @PostMapping("/getGoodsInfo/{id}")
    @ApiOperation(value = "查看详情", tags = {"后台-商品管理"})
    public R<TGoodsVO> getGoodsInfo(@PathVariable("id") Integer id) {
        TGoodsVO tGoodsVO = new TGoodsVO();
        TGoods byId = goodsService.getById(id);
        BeanUtils.copyProperties(byId,tGoodsVO);
        BeanUtils.copyProperties(byId, tGoodsVO);
        long goodsId = orderService.count(new QueryWrapper<TOrder>().eq("goodsId", id));
        tGoodsVO.setInventory(goodsId);
        tGoodsVO.setIntegral(byId.getIntegral());
        return R.ok(tGoodsVO);
    }
    @PostMapping("/getGoodsTypeList")
    @ApiOperation(value = "获取商品类型列表", tags = {"后台-商品管理"})
    public R<List<TGoodsType>> getGoodsInfo() {
        List<TGoodsType> res = goodsTypeService.list(new QueryWrapper<TGoodsType>());
        List<TGoodsType> res = goodsTypeService.list(new QueryWrapper<>());
        return R.ok(res);
    }
    /**
     * 兑换记录
     */
    @GetMapping("/exchangeRecord")
    @ApiOperation(value = "兑换记录", tags = {"兑换记录"})
    public R<List<TOrder>> exchangeRecord() {
        return R.ok(orderService.lambdaQuery().eq(TOrder::getUserId, SecurityUtils.getUserId())
                .orderByDesc(TOrder::getCreateTime).list());
    }
    /**
     * 获取用户收货地址
     */
    @GetMapping("/shopAddress")
    @ApiOperation(value = "获取用户收货地址", tags = {"获取用户收货地址"})
    public R<List<Recipient>> shopAddress() {
        return R.ok(recipientService.lambdaQuery().eq(Recipient::getUserId, SecurityUtils.getUserId()).list());
    }
    /**
     * 新增收货地址/修改收货地址
     */
    @PostMapping("/addressSaveOrUpdate")
    @ApiOperation(value = "新增收货地址/修改收货地址", tags = {"新增收货地址/修改收货地址"})
    public R<String> addressSave(@RequestBody Recipient recipient) {
        return R.ok(recipientService.addressSaveOrUpdate(recipient));
    }
    /**
     * 删除收货地址
     */
    @GetMapping("/addressDelete")
    @ApiOperation(value = "删除收货地址", tags = {"删除收货地址"})
    public R<String> addressDelete(@RequestParam String id) {
        return R.ok(recipientService.removeById(id) ? "删除成功!" : "删除失败!");
    }
    /**
     * 修改订单收货地址
     *
     * @param orderId 订单id
     */
    @GetMapping("/updateOrderAddress")
    @ApiOperation(value = "修改订单收货地址", tags = {"修改订单收货地址"})
    public R<Boolean> updateOrderAddress(@RequestParam String orderId, @RequestParam String address) {
        return R.ok(orderService.lambdaUpdate().set(TOrder::getConsigneeAddress, address)
                .eq(TOrder::getId, orderId).eq(TOrder::getState, 1).update());
    }
    /**
     * 收货地址省市区三级联动
     */
    @GetMapping("/addressTree")
    @ApiOperation(value = "收货地址省市区三级联动", tags = {"收货地址省市区三级联动"})
    public R<List<Region>> addressTree() {
        return R.ok(regionService.addressTree());
    }
    /**
     * 可兑换商品推荐
     */
    @GetMapping("/goodRecommend")
    @ApiOperation(value = "可兑换商品推荐", tags = {"可兑换商品推荐"})
    public R<List<TGoodsVO>> goodRecommend(@RequestParam String userId) {
        List<TGoodsVO> res = goodsService.goodRecommend(userId);
        return R.ok(res);
    }
    /**
     * 商品详情
     *
     * @param goodId 商品id
     */
    @GetMapping("/goodDetail")
    @ApiOperation(value = "商品详情", tags = {"商品详情"})
    public R<Map<String, Object>> goodDetail(@RequestParam String goodId) {
        Map<String, Object> result = new HashMap<>(8);
        // 商品详情
        TGoods goods = goodsService.lambdaQuery().eq(TGoods::getId, goodId).one();
        result.put("goodDetail", goods);
        // 商品分类详情
        result.put("goodTypeDetail", goodsTypeService.lambdaQuery().in(TGoodsType::getId, Arrays.asList(goods.getTypeIds().split(","))).list());
        // 已兑换人数
        result.put("number", goods.getBasicCount() + orderService.getGoodBuyNumber(goods.getId()));
        // 用户收货地址
        if (SecurityUtils.getUserId() != null) {
            result.put("address", recipientService.lambdaQuery()
                    .eq(Recipient::getUserId, SecurityUtils.getUserId()).eq(Recipient::getIsDefault, 1).one());
        }
        return R.ok(result);
    }
    /**
     * 商城-立即兑换
     */
    @GetMapping("/redeemNow")
    @ApiOperation(value = "商城-立即兑换", tags = {"立即兑换"})
    public R<Map<String, Object>> redeemNow(@RequestParam String goodId) {
        Recipient recipient = recipientService.lambdaQuery()
                .eq(Recipient::getUserId, SecurityUtils.getUserId()).eq(Recipient::getIsDefault, 1).one();
        return R.ok(goodsService.redeemNow(goodId, recipient));
    }
    /**
     * 商品兑换
     *
     * @param goodExchange 商品信息
     */
    @PostMapping("/goodExchange")
    @ApiOperation(value = "商品兑换-确认", tags = {"商品兑换-确认"})
    public R<Object> goodExchange(@RequestBody GoodExchangeDTO goodExchange) {
        Recipient recipient = recipientService.getById(goodExchange.getRecipientId());
        return R.ok(goodsService.goodExchange(goodExchange, recipient));
    }
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/domain/Region.java
@@ -4,14 +4,19 @@
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 行政区域数据
* @author pzb
* @Date 2022/2/9 10:00
*/
 * 行政区域数据
 *
 * @author pzb
 * @Date 2022/2/9 10:00
 */
@Data
@TableName("t_region")
public class Region {
@@ -38,4 +43,12 @@
     */
    @TableField("parent_id")
    private Integer parentId;
    /**
     * 子集
     */
    @TableField(exist = false)
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<Region> children = new ArrayList<>();
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodExchangeDTO.java
New file
@@ -0,0 +1,38 @@
package com.ruoyi.goods.dto;
import lombok.Data;
/**
 * @author HJL
 * @version 1.0
 * @since 2024-05-17 9:56
 */
@Data
public class GoodExchangeDTO {
    /**
     * 商品id
     */
    private Integer goodId;
    /**
     * 备注
     */
    private String remark;
    /**
     * 兑换数量
     */
    private Integer number;
    /**
     * 订单编号
     */
    private String orderNumber;
    /**
     * 收货地址id
     */
    private String recipientId;
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodQueryDTO.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.goods.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * @author HJL
 * @version 1.0
 * @since 2024-05-16 17:16
 */
@Data
public class GoodQueryDTO {
    /**
     * 商品类型
     */
    @ApiModelProperty("商品类型")
    private List<String> type;
    /**
     * 关键字
     */
    @ApiModelProperty("关键字")
    private String keywords;
    @ApiModelProperty(value = "页码,首页1", required = true)
    private Integer pageNumber;
    @ApiModelProperty(value = "页条数", required = true)
    private Integer pageSize;
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TGoodsMapper.java
@@ -3,6 +3,9 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.goods.domain.TGoods;
import com.ruoyi.goods.vo.TGoodsVO;
import java.util.List;
/**
 * <p>
@@ -14,4 +17,12 @@
 */
public interface TGoodsMapper extends BaseMapper<TGoods> {
    /**
     * 可兑换商品推荐
     *
     * @param userId 用户id
     * @return 推荐商品
     */
    List<TGoodsVO> goodRecommend(String userId);
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TOrderMapper.java
@@ -20,4 +20,11 @@
    List<TOrderVO> listAll(@Param("req") OrderQuery query);
    /**
     * 获取商品购买数量
     *
     * @param id 商品id
     * @return 购买数量
     */
    Integer getGoodBuyNumber(@Param("goodId") Integer id);
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRecipientService.java
@@ -14,4 +14,11 @@
 */
public interface IRecipientService extends IService<Recipient> {
    /**
     * 新增收货地址/修改收货地址
     *
     * @param recipient 收货地址信息
     * @return 操作结果
     */
    String addressSaveOrUpdate(Recipient recipient);
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRegionService.java
@@ -8,5 +8,11 @@
public interface IRegionService extends IService<Region> {
    /**
     * 收货地址省市区三级联动
     *
     * @return 无限级树
     */
    List<Region> addressTree();
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITGoodsService.java
@@ -2,7 +2,13 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.goods.domain.Recipient;
import com.ruoyi.goods.domain.TGoods;
import com.ruoyi.goods.dto.GoodExchangeDTO;
import com.ruoyi.goods.vo.TGoodsVO;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -14,4 +20,29 @@
 */
public interface ITGoodsService extends IService<TGoods> {
    /**
     * 可兑换商品推荐
     *
     * @param userId 用户id
     * @return 推荐商品
     */
    List<TGoodsVO> goodRecommend(String userId);
    /**
     * 立即兑换
     *
     * @param goodId    商品id
     * @param recipient 默认收货地址
     * @return 立即兑换页面数据
     */
    Map<String, Object> redeemNow(String goodId, Recipient recipient);
    /**
     * 商品兑换
     *
     * @param goodExchange 商品信息
     * @param recipient    收货地址
     * @return 兑换结果
     */
    Object goodExchange(GoodExchangeDTO goodExchange, Recipient recipient);
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITOrderService.java
@@ -20,4 +20,12 @@
    List<TOrderVO> listAll(OrderQuery query);
    /**
     * 获取商品购买数量
     *
     * @param id 商品id
     * @return 购买数量
     */
    Integer getGoodBuyNumber(Integer id);
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RecipientServiceImpl.java
@@ -1,10 +1,13 @@
package com.ruoyi.goods.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.goods.domain.Recipient;
import com.ruoyi.goods.mapper.RecipientMapper;
import com.ruoyi.goods.service.IRecipientService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * <p>
@@ -17,4 +20,17 @@
@Service
public class RecipientServiceImpl extends ServiceImpl<RecipientMapper, Recipient> implements IRecipientService {
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addressSaveOrUpdate(Recipient recipient) {
        boolean result;
        // 主键ID为空,新增收货地址
        if (StringUtils.isEmpty(String.valueOf(recipient.getId()))) {
            recipient.setUserId(SecurityUtils.getUserId().intValue());
            result = this.save(recipient);
        } else {
            result = this.updateById(recipient);
        }
        return result ? "操作成功!" : "操作失败!";
    }
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RegionServiceImpl.java
@@ -1,14 +1,68 @@
package com.ruoyi.goods.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.constant.RedisConstants;
import com.ruoyi.goods.domain.Region;
import com.ruoyi.goods.mapper.RegionMapper;
import com.ruoyi.goods.service.IRegionService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> implements IRegionService {
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;
    @Override
    public List<Region> addressTree() {
        // redis缓存
        Map<Object, Object> regionList = redisTemplate.opsForHash().entries(RedisConstants.ADDRESS_TREE);
        // 获取所有地区信息 省市区三级
        List<Region> regions = getReginList(regionList);
        // 所有地区
        Map<String, Region> courseTypeMap = regions.stream().
                collect(Collectors.toMap(region -> region.getId().toString()
                        , region -> region));
        redisTemplate.opsForHash().putAll(RedisConstants.ADDRESS_TREE, courseTypeMap);
        redisTemplate.expire(RedisConstants.ADDRESS_TREE, 30, TimeUnit.MINUTES);
        // 生成map集合
        Map<Integer, Region> map = regions.stream().collect(Collectors.toMap(Region::getId, region -> region));
        // 存放无限级树
        List<Region> treeData = new ArrayList<>();
        // 遍历地区集合
        regions.forEach(e -> {
            if (e.getParentId() == null || e.getParentId().equals(0)) {
                treeData.add(e);
            } else {
                Region region = map.get(e.getParentId());
                region.getChildren().add(e);
            }
        });
        return treeData;
    }
    /**
     * 获取redis数据进行封装
     */
    private List<Region> getReginList(Map<Object, Object> regionMap) {
        List<Region> regions = new ArrayList<>();
        if (!regionMap.isEmpty()) {
            Collection<Object> values = regionMap.values();
            for (Object value : values) {
                regions.add((Region) value);
            }
        } else {
            regions = this.list();
        }
        return regions;
    }
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TGoodsServiceImpl.java
@@ -1,10 +1,30 @@
package com.ruoyi.goods.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.RedisConstants;
import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.goods.domain.Recipient;
import com.ruoyi.goods.domain.TGoods;
import com.ruoyi.goods.domain.TOrder;
import com.ruoyi.goods.dto.GoodExchangeDTO;
import com.ruoyi.goods.mapper.TGoodsMapper;
import com.ruoyi.goods.service.ITGoodsService;
import com.ruoyi.goods.service.ITOrderService;
import com.ruoyi.goods.vo.TGoodsVO;
import com.ruoyi.study.api.domain.TUser;
import com.ruoyi.study.api.feignClient.StudyClient;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -17,4 +37,82 @@
@Service
public class TGoodsServiceImpl extends ServiceImpl<TGoodsMapper, TGoods> implements ITGoodsService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private StudyClient studyClient;
    @Resource
    private ITOrderService orderService;
    @Override
    public List<TGoodsVO> goodRecommend(String userId) {
        return baseMapper.goodRecommend(userId);
    }
    @Override
    public Map<String, Object> redeemNow(String goodId, Recipient recipient) {
        // 商品详情
        TGoods goods = lambdaQuery().eq(TGoods::getId, goodId).one();
        Map<String, Object> result = new HashMap<>(8);
        result.put("goodDetail", goods);
        // 用户收货地址
        result.put("address", recipient);
        // 库存预热,redisson分布式锁
        String key = String.format(RedisConstants.GOOD_STOCK, goods.getId());
        RSemaphore semaphore = redissonClient.getSemaphore(key);
        semaphore.trySetPermits(goods.getSurplus());
        return result;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object goodExchange(GoodExchangeDTO goodExchange, Recipient recipient) {
        Integer number = goodExchange.getNumber();
        Integer goodId = goodExchange.getGoodId();
        TGoods good = this.getById(goodId);
        if (null == good) {
            throw new GlobalException("商品不存在,请稍后重试!");
        }
        // 校验用户积分是否足够兑换
        TUser user = studyClient.userInfo().getData();
        int needIntegral = good.getIntegral() * number;
        if (user.getIntegral() < needIntegral) {
            throw new GlobalException("兑换失败,当前剩余积分不足!");
        }
        // redisson分布式锁,防止超卖
        String key = String.format(RedisConstants.GOOD_STOCK, good.getId());
        RSemaphore semaphore = redissonClient.getSemaphore(key);
        boolean tried = semaphore.tryAcquire(number);
        // 兑换失败,库存不足
        if (!tried) {
            throw new GlobalException("当前商品库存不足!");
        }
        // 兑换成功,生成订单信息、生成积分明细(积分明细需要远程调用rouyi-study服务)
        TOrder order = orderInfo(goodExchange, recipient, number, goodId, needIntegral);
        if (!orderService.save(order)) {
            throw new GlobalException("订单生成失败!");
        }
        // 远程调用,生成积分明细
        Boolean boo = studyClient.addIntegralDetail(Constants.BURDEN + needIntegral, Constants.SHOPPING_CONSUME).getData();
        if (!boo) {
            throw new GlobalException("生成积分明细失败,请稍后重试!");
        }
        return true;
    }
    private TOrder orderInfo(GoodExchangeDTO goodExchange, Recipient recipient, Integer number, Integer goodId, int needIntegral) {
        TOrder order = new TOrder();
        order.setOrderNumber(goodExchange.getOrderNumber());
        order.setUserId(SecurityUtils.getUserId().intValue());
        order.setInsertTime(new Date());
        order.setGoodsId(goodId);
        order.setCount(number);
        order.setState(1);
        order.setIntegral(needIntegral);
        order.setConsigneeName(recipient.getRecipient());
        order.setConsigneePhone(recipient.getRecipientPhone());
        order.setConsigneeAddress(recipient.getAddress());
        order.setDisabled(Boolean.FALSE);
        return order;
    }
}
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TOrderServiceImpl.java
@@ -25,4 +25,10 @@
    public List<TOrderVO> listAll(OrderQuery query) {
        return this.baseMapper.listAll(query);
    }
    @Override
    public Integer getGoodBuyNumber(Integer id) {
        return baseMapper.getGoodBuyNumber(id);
    }
}
ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/RecipientMapper.xml
@@ -16,9 +16,4 @@
        <result column="isDefault" property="isDefault" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, userId, recipient, recipientPhone, province, provinceCode, city, cityCode, address,isDefault
    </sql>
</mapper>
ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TGoodsMapper.xml
@@ -1,28 +1,45 @@
<?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.ruoyi.goods.mapper.TGoodsMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.goods.domain.TGoods">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="price" property="price" />
        <result column="total" property="total" />
        <result column="surplus" property="surplus" />
        <result column="userCount" property="userCount" />
        <result column="typeIds" property="typeIds" />
        <result column="coverImg" property="coverImg" />
        <result column="detailImg" property="detailImg" />
        <result column="detail" property="detail" />
        <result column="isDelete" property="isDelete" />
        <result column="type" property="type" />
        <result column="integral" property="integral" />
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="price" property="price"/>
        <result column="total" property="total"/>
        <result column="surplus" property="surplus"/>
        <result column="userCount" property="userCount"/>
        <result column="typeIds" property="typeIds"/>
        <result column="coverImg" property="coverImg"/>
        <result column="detailImg" property="detailImg"/>
        <result column="detail" property="detail"/>
        <result column="isDelete" property="isDelete"/>
        <result column="type" property="type"/>
        <result column="integral" property="integral"/>
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name,
 price, total, surplus, userCount, typeIds, coverImg, detailImg, detail,  isDelete, type,integral,
        id,
        name,
        price,
        total,
        surplus,
        userCount,
        typeIds,
        coverImg,
        detailImg,
        detail,
        isDelete,
        type,
        integral
    </sql>
    <select id="goodRecommend" resultType="com.ruoyi.goods.vo.TGoodsVO">
        select *
        from t_goods g
        where g.price <![CDATA[ <= ]]> (select integral from t_user u where u.id = #{userId})
        ORDER BY g.price desc
        limit 3
    </select>
</mapper>
ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TOrderMapper.xml
@@ -44,4 +44,7 @@
        order by t1.insertTime desc
    </select>
    <select id="getGoodBuyNumber" resultType="java.lang.Integer">
        SELECT SUM(count) FROM t_order WHERE goodsId = #{goodId}
    </select>
</mapper>
ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TPageController.java
@@ -1,9 +1,16 @@
package com.ruoyi.management.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.management.domain.TPage;
import com.ruoyi.management.service.ITPageService;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
/**
 * <p>
@@ -17,5 +24,20 @@
@RequestMapping("/tPage")
public class TPageController {
    @Resource
    private ITPageService pageService;
    /**
     * 启动页
     *
     * @param type 类型 1学习 2家长手机 3家长平板 4注意事项
     */
    @GetMapping("/home")
    @ApiOperation(value = "启动页", tags = {"启动页"})
    public R<TPage> home(@RequestParam Integer type) {
        return R.ok(pageService.lambdaQuery().eq(TPage::getType, type)
                .eq(TPage::getDisabled, 0).one());
    }
}
ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TProtocolController.java
@@ -1,9 +1,16 @@
package com.ruoyi.management.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.management.domain.TProtocol;
import com.ruoyi.management.service.ITProtocolService;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
/**
 * <p>
@@ -17,5 +24,20 @@
@RequestMapping("/tProtocol")
public class TProtocolController {
    @Resource
    private ITProtocolService protocolService;
    /**
     * 用户协议/隐私协议/注销协议
     *
     * @param type 类型 1用户协议 2隐私协议 3注销协议
     */
    @GetMapping("/home")
    @ApiOperation(value = "用户协议/隐私协议/注销协议", tags = {"用户协议/隐私协议/注销协议"})
    public R<TProtocol> home(@RequestParam Integer type) {
        return R.ok(protocolService.lambdaQuery().eq(TProtocol::getType, type)
                .eq(TProtocol::getDisabled, 0).one());
    }
}
ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TUserController.java
@@ -18,8 +18,10 @@
import com.ruoyi.study.api.vo.VipOrderVO;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@@ -41,6 +43,7 @@
    @Autowired
    private StudyClient studyClient;
    @PostMapping("/userList")
    @ApiOperation(value = "用户列表", tags = {"用户管理"})
    public AjaxResult<PageInfo<AppUserVO>> couponReceive(AppUserQuery query) {
@@ -111,6 +114,7 @@
        return AjaxResult.success(data);
    }
    @PostMapping("/freeze")
    @ApiOperation(value = "冻结/解冻", tags = {"用户管理"})
    public AjaxResult freeze(Integer id) {
@@ -118,23 +122,26 @@
        if (byId.getState() == 1) {
            studyClient.freeze(id);
            return AjaxResult.success("冻结成功");
        }else {
        } else {
            studyClient.freeze(id);
            return AjaxResult.success("解冻成功");
        }
    }
    @PostMapping("/getVipSet")
    @ApiOperation(value = "获取会员设置", tags = {"用户管理"})
    public AjaxResult<List<TVipSet>> getVipSet() {
        List<TVipSet> list = vipSetService.list(new QueryWrapper<TVipSet>().orderByAsc("amount"));
        return AjaxResult.success(list);
    }
    @PostMapping("/getVipSet1")
    @ApiOperation(value = "获取会员设置", tags = {"家长端"})
    public R<List<TVipSet>> getVipSet1() {
        List<TVipSet> list = vipSetService.list(new QueryWrapper<TVipSet>().orderByAsc("amount"));
        return R.ok(list);
    }
    @PostMapping("/setVipSet")
    @ApiOperation(value = "保存会员设置", tags = {"用户管理"})
    public AjaxResult setVipSet(@RequestBody VipSetVO vo) {
@@ -146,6 +153,7 @@
        }
        return AjaxResult.success("保存成功");
    }
    @PostMapping("/vipOrderList")
    @ApiOperation(value = "列表查询", tags = {"会员管理"})
    public AjaxResult<PageInfo<VipOrderVO>> vipOrderList(AppUserQuery query) {
@@ -153,11 +161,13 @@
        PageInfo<VipOrderVO> data = studyClient.vipOrderList(query).getData();
        return AjaxResult.success(data);
    }
    @PostMapping("/vipBack")
    @ApiOperation(value = "会员退款", tags = {"会员管理"})
    public AjaxResult vipOrderList(Integer id) {
        studyClient.vipBack(id);
        return AjaxResult.success();
    }
}
ruoyi-service/ruoyi-study/pom.xml
@@ -43,6 +43,12 @@
            <artifactId>ruoyi-api-management</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-goods</artifactId>
            <version>3.6.2</version>
        </dependency>
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java
@@ -1,19 +1,32 @@
package com.ruoyi.study.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.constant.RedisConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.goods.api.feignClient.GoodsClient;
import com.ruoyi.goods.api.model.TGoodsVO;
import com.ruoyi.study.domain.*;
import com.ruoyi.study.dto.*;
import com.ruoyi.study.service.*;
import com.ruoyi.study.vo.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -25,6 +38,7 @@
 */
@RestController
@RequestMapping("/base/study")
@Api(tags = "学习端-主接口")
public class TStudyController {
    @Autowired
    private ITStudyAnswerService studyAnswerService;
@@ -46,23 +60,37 @@
    private ITStoryService storyService;
    @Autowired
    private ITStudyService studyService;
    @Resource
    private GoodsClient goodsClient;
    @Resource
    private ITGameRecordService gameRecordService;
    @Resource
    private ITUserStudyService userStudyService;
    @Resource
    private ITIntegralRecordService integralRecordService;
    @Resource
    private RedisService redisService;
    @Resource
    private ITUserService userService;
    @Resource
    private TokenService tokenService;
    @PostMapping("/storyList")
    @ApiOperation(value = "配置学习类型选择故事", tags = {"题目管理"})
    public R<PageInfo<TStory>> storyList(@RequestBody ChoiceStory query) {
        PageInfo<TStory> res = new PageInfo<>(query.getPageNumber(), query.getPageSize());
        QueryWrapper<TStory> wrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(query.getName())){
            wrapper.like("name",query.getName());
        if (StringUtils.hasLength(query.getName())) {
            wrapper.like("name", query.getName());
        }
        if (StringUtils.hasLength(query.getEnglish())){
            wrapper.like("english",query.getEnglish());
        if (StringUtils.hasLength(query.getEnglish())) {
            wrapper.like("english", query.getEnglish());
        }
        if (StringUtils.hasLength(query.getType())){
            wrapper.like("type",query.getType());
        if (StringUtils.hasLength(query.getType())) {
            wrapper.like("type", query.getType());
        }
        wrapper.eq("state",1);
        switch (query.getStoryType()){
        wrapper.eq("state", 1);
        switch (query.getStoryType()) {
            case 2:
                List<TStory> list = storyService.list(wrapper);
                res.setRecords(list);
@@ -82,23 +110,24 @@
        res.setTotal(0);
        return R.ok(res);
    }
    @PostMapping("/subjectList")
    @ApiOperation(value = "配置学习类型选择题目", tags = {"题目管理"})
    public R<PageInfo<TSubject>> subjectList(@RequestBody ChoiceSubject query) {
        PageInfo<TSubject> res = new PageInfo<>(query.getPageNumber(), query.getPageSize());
        QueryWrapper<TSubject> wrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(query.getName())){
            wrapper.like("name",query.getName());
        if (StringUtils.hasLength(query.getName())) {
            wrapper.like("name", query.getName());
        }
        if (StringUtils.hasLength(query.getEnglish())){
            wrapper.like("english",query.getEnglish());
        if (StringUtils.hasLength(query.getEnglish())) {
            wrapper.like("english", query.getEnglish());
        }
        if (StringUtils.hasLength(query.getType())){
            wrapper.like("type",query.getType());
        if (StringUtils.hasLength(query.getType())) {
            wrapper.like("type", query.getType());
        }
        wrapper.eq("state",1);
        switch (query.getStudyType()){
        wrapper.eq("state", 1);
        switch (query.getStudyType()) {
            case 1:
                List<TSubject> list = subjectService.list(wrapper);
                res.setRecords(list);
@@ -132,8 +161,10 @@
        res.setTotal(0);
        return R.ok(res);
    }
    /**
     * 添加学习配置
     *
     * @param dto
     * @return
     */
@@ -147,12 +178,12 @@
                .eq("type", type));
        GameDTO game = dto.getGame();
        StoryListenDTO storyListen = dto.getStoryListen();
        if (day == 6){
        if (day == 6) {
            // 先判断有没有配置
            TGame studyId = gameService.getOne(new QueryWrapper<TGame>()
                    .eq("studyId", one.getId())
                    .eq("week",week));
            if (studyId!=null){
                    .eq("week", week));
            if (studyId != null) {
                studyId.setWeek(dto.getWeek());
                studyId.setStudyId(one.getId());
                studyId.setCount(game.getCount());
@@ -162,7 +193,7 @@
                studyId.setAnswerIntegral(game.getAnswerIntegral());
                studyId.setAnswerCount(game.getAnswerCount());
                gameService.updateById(studyId);
            }else{
            } else {
                TGame tGame = new TGame();
                tGame.setWeek(dto.getWeek());
                tGame.setStudyId(one.getId());
@@ -175,12 +206,12 @@
                gameService.save(tGame);
            }
        }else if(day == 7){
        } else if (day == 7) {
            String story = storyListen.getStory();
            TStoryListen studyId = storyListenService.getOne(new QueryWrapper<TStoryListen>()
                    .eq("studyId", one.getId())
                    .eq("week",week));
            if (studyId!=null){
                    .eq("week", week));
            if (studyId != null) {
                String sort = storyListen.getSort();
                String lookStory = storyListen.getLookStory();
                String lookSort = storyListen.getLookSort();
@@ -195,7 +226,7 @@
                studyId.setLookSort(lookSort);
                studyId.setStudyId(one.getId());
                storyListenService.updateById(studyId);
            }else{
            } else {
                String sort = storyListen.getSort();
                String lookStory = storyListen.getLookStory();
                String lookSort = storyListen.getLookSort();
@@ -212,12 +243,12 @@
                tStoryListen.setStudyId(one.getId());
                storyListenService.save(tStoryListen);
            }
        }else{
        } else {
            // 删除原有数据
            studyListenService.remove(new QueryWrapper<TStudyListen>()
                    .eq("studyId", one.getId())
                    .eq("week",week)
                    .eq("day",day));
                    .eq("week", week)
                    .eq("day", day));
            List<StudyListenDTO> studyListen = dto.getStudyListen();
            for (StudyListenDTO studyListenDTO : studyListen) {
                TStudyListen tStudyListen = new TStudyListen();
@@ -231,8 +262,8 @@
            }
            studyLookService.remove(new QueryWrapper<TStudyLook>()
                    .eq("studyId", one.getId())
                    .eq("week",week)
                    .eq("day",day));
                    .eq("week", week)
                    .eq("day", day));
            List<StudyLookDTO> studyLook = dto.getStudyLook();
            for (StudyLookDTO studyLookDTO : studyLook) {
                TStudyLook tStudyLook = new TStudyLook();
@@ -246,8 +277,8 @@
            }
            studyInductionService.remove(new QueryWrapper<TStudyInduction>()
                    .eq("studyId", one.getId())
                    .eq("week",week)
                    .eq("day",day));
                    .eq("week", week)
                    .eq("day", day));
            List<StudyInductionDTO> studyInduction = dto.getStudyInduction();
            for (StudyInductionDTO studyInductionDTO : studyInduction) {
                TStudyInduction tStudyInduction = new TStudyInduction();
@@ -260,8 +291,8 @@
            }
            studyAnswerService.remove(new QueryWrapper<TStudyAnswer>()
                    .eq("studyId", one.getId())
                    .eq("week",week)
                    .eq("day",day));
                    .eq("week", week)
                    .eq("day", day));
            List<StudyAnswerDTO> studyAnswer = dto.getStudyAnswer();
            for (StudyAnswerDTO studyAnswerDTO : studyAnswer) {
                TStudyAnswer tStudyAnswer = new TStudyAnswer();
@@ -277,8 +308,8 @@
            }
            studyPairService.remove(new QueryWrapper<TStudyPair>()
                    .eq("studyId", one.getId())
                    .eq("week",week)
                    .eq("day",day));
                    .eq("week", week)
                    .eq("day", day));
            List<StudyPairDTO> studyPair = dto.getStudyPair();
            for (StudyPairDTO studyPairDTO : studyPair) {
                TStudyPair tStudyPair = new TStudyPair();
@@ -296,6 +327,7 @@
    /**
     * 添加周目
     *
     * @param dto
     * @return
     */
@@ -309,8 +341,10 @@
        studyService.save(tStudy);
        return R.ok();
    }
    /**
     * 学习类型列表查询
     *
     * @return
     */
    @PostMapping("/getStudyList")
@@ -326,12 +360,14 @@
        res.add(studyListVO);
        // 后续类型 不在1.0功能中
        for (int i = 1; i <= 6; i++) {
            res.add(new StudyListVO(0,0));
            res.add(new StudyListVO(0, 0));
        }
        return R.ok(res);
    }
    /**
     * 通过类型、周目、day查询学习配置
     *
     * @return
     */
    @PostMapping("/getStudySet")
@@ -373,7 +409,7 @@
        List<TStudyLook> list1 = studyLookService.list(new QueryWrapper<TStudyLook>()
                .eq("week", week)
                .eq("day", day)
                );
        );
        for (TStudyLook tStudyLook : list1) {
            int index = 0;
            StringBuilder names = new StringBuilder();
@@ -398,16 +434,16 @@
        List<TStudyInduction> list2 = studyInductionService.list(new QueryWrapper<TStudyInduction>()
                .eq("week", week)
                .eq("day", day)
                );
        );
        for (TStudyInduction tStudyInduction : list2) {
            StringBuilder names = new StringBuilder();
            StudyInductionVO studyInductionVO = new StudyInductionVO();
            for (String s : tStudyInduction.getSubject().split(",")) {
                String replace = s.replace("-", "");
                if (s.contains("-")){
                if (s.contains("-")) {
                    TSubject byId = subjectService.getById(replace);
                    names.append("-").append(byId.getName()).append(",");
                }else{
                } else {
                    TSubject byId = subjectService.getById(s);
                    names.append(byId.getName()).append(",");
                }
@@ -424,12 +460,12 @@
        for (TStudyAnswer tStudyAnswer : list3) {
            StringBuilder names = new StringBuilder();
            StudyAnswerVO studyAnswerVO = new StudyAnswerVO();
            if (tStudyAnswer.getIsAnswer() == 1){
            if (tStudyAnswer.getIsAnswer() == 1) {
                TSubject byId = subjectService.getById(tStudyAnswer.getSubject());
                names.append("-").append(byId.getName()).append(",");
                TSubject byId1 = subjectService.getById(tStudyAnswer.getAnswerSubject());
                names.append("-").append(byId1.getName()).append(",");
            }else{
            } else {
                TSubject byId = subjectService.getById(tStudyAnswer.getSubject());
                names.append(byId.getName()).append(",");
                TSubject byId1 = subjectService.getById(tStudyAnswer.getAnswerSubject());
@@ -444,7 +480,7 @@
        List<TStudyPair> list4 = studyPairService.list(new QueryWrapper<TStudyPair>()
                .eq("week", week)
                .eq("day", day)
                );
        );
        for (TStudyPair tStudyPair : list4) {
            StringBuilder names = new StringBuilder();
            StudyPairVO studyPairVO = new StudyPairVO();
@@ -500,30 +536,30 @@
            storyVOS.add(storyVO);
        }
        int temp = 0;
        if (!list.isEmpty()){
        if (!list.isEmpty()) {
            temp = list.get(0).getStudyId();
        }
        if (!list1.isEmpty()){
        if (!list1.isEmpty()) {
            temp = list1.get(0).getStudyId();
        }
        if (!list2.isEmpty()){
        if (!list2.isEmpty()) {
            temp = list2.get(0).getStudyId();
        }
        if (!list3.isEmpty()){
        if (!list3.isEmpty()) {
            temp = list3.get(0).getStudyId();
        }
        if (!list4.isEmpty()){
        if (!list4.isEmpty()) {
            temp = list4.get(0).getStudyId();
        }
        if (!list5.isEmpty()){
        if (!list5.isEmpty()) {
            temp = list5.get(0).getStudyId();
        }
        if (!list6.isEmpty()){
        if (!list6.isEmpty()) {
            temp = list6.get(0).getStudyId();
        }
        if (temp == 0){
        if (temp == 0) {
            res.setTitle("");
        }else{
        } else {
            res.setTitle(studyService.getById(temp).getTitle());
        }
        res.setAnswer(answerVOS);
@@ -535,5 +571,376 @@
        res.setStory(storyVOS);
        return R.ok(res);
    }
    /**
     * 查询周目列表
     *
     * @param type    所属类型
     * @param quarter 季度
     */
    @GetMapping("/weekList")
    @ApiOperation(value = "周目列表", tags = {"周目列表"})
    public R<List<StudyWeekDTO>> weekList(@RequestParam(defaultValue = "1") Integer type, @RequestParam Integer quarter) {
        List<StudyWeekDTO> result = studyService.weekList(type, quarter);
        return R.ok(result);
    }
    /**
     * 首次页面加载时调用,获取学习进度及学习时长等信息
     *
     * @param week 周目
     * @param day  所属day
     */
    @GetMapping("/studySchedule")
    @ApiOperation(value = "获取用户学习进度", tags = {"获取用户学习进度"})
    public R<TUserStudy> studySchedule(Integer week, Integer day) {
        TUserStudy result = studyService.studySchedule(String.valueOf(SecurityUtils.getUserId()), week, day);
        return R.ok(result);
    }
    /**
     * 可兑换商品推荐
     */
    @GetMapping("/goodRecommend")
    @ApiOperation(value = "可兑换商品推荐", tags = {"可兑换商品推荐"})
    public R<List<TGoodsVO>> studySchedule() {
        return goodsClient.goodRecommend(String.valueOf(SecurityUtils.getUserId()));
    }
    /**
     * 退出学习,记录学习进度、当日学习时长...
     */
    @PostMapping("/exitLearning")
    @ApiOperation(value = "退出学习(记录学习进度等信息)", tags = {"退出学习(记录学习进度等信息)"})
    public R<Boolean> exitLearning(@RequestBody TUserStudy userStudy) {
        // 学习时长处理
        return R.ok(userStudyService.updateById(userStudy));
    }
    /**
     * 自主学习1-听音选图
     *
     * @param week 周目
     * @param day  所属day
     */
    @GetMapping("/listenSelectPicture")
    @ApiOperation(value = "自主学习1-听音选图", tags = {"自主学习1-听音选图"})
    public R<Map<String, Object>> listenSelectPicture(@RequestParam Integer week, @RequestParam Integer day) {
        // 判断当前登录用户是否为 会员
        Boolean isVip = userService.isVip();
        List<TStudyListen> studyListens = studyListenService.lambdaQuery().eq(TStudyListen::getWeek, week)
                .eq(TStudyListen::getDay, day).eq(TStudyListen::getDisabled, 0).list();
        return R.ok(studyService.listenSelectPicture(week, day, studyListens));
    }
    /**
     * 自主学习2-看图选音
     *
     * @param week 周目
     * @param day  所属day
     */
    @GetMapping("/pictureSelectVoice")
    @ApiOperation(value = "自主学习2-看图选音", tags = {"自主学习2-看图选音"})
    public R<Map<String, Object>> pictureSelectVoice(@RequestParam Integer week, @RequestParam Integer day) {
        // 判断当前登录用户是否为 会员
        Boolean isVip = userService.isVip();
        LambdaQueryChainWrapper<TStudyLook> wrapper = studyLookService.lambdaQuery().eq(TStudyLook::getWeek, week)
                .eq(TStudyLook::getDay, day).eq(TStudyLook::getDisabled, 0);
        // 非会员只能查看非会员题目,会员可以查看所有题目
        if (!isVip) {
            wrapper.eq(TStudyLook::getIsVip, 0);
        }
        List<TStudyLook> lookList = studyLookService.lambdaQuery().eq(TStudyLook::getWeek, week)
                .eq(TStudyLook::getDay, day).eq(TStudyLook::getDisabled, 0).list();
        return R.ok(studyService.pictureSelectVoice(week, day, lookList));
    }
    /**
     * 自主学习3-归纳排除
     *
     * @param week 周目
     * @param day  所属day
     */
    @GetMapping("/induceExclude")
    @ApiOperation(value = "自主学习3-归纳排除", tags = {"自主学习3-归纳排除"})
    public R<Map<String, Object>> induceExclude(@RequestParam Integer week, @RequestParam Integer day) {
        // 判断当前登录用户是否为 会员
        Boolean isVip = userService.isVip();
        LambdaQueryChainWrapper<TStudyInduction> wrapper = studyInductionService.lambdaQuery().eq(TStudyInduction::getWeek, week)
                .eq(TStudyInduction::getDay, day).eq(TStudyInduction::getDisabled, 0);
        // 非会员只能查看非会员题目,会员可以查看所有题目
        if (!isVip) {
            wrapper.eq(TStudyInduction::getIsVip, 0);
        }
        List<TStudyInduction> inductionList = wrapper.list();
        return R.ok(studyService.induceExclude(week, day, inductionList));
    }
    /**
     * 自主学习4-有问有答
     *
     * @param week 周目
     * @param day  所属day
     */
    @GetMapping("/questionsAndAnswers")
    @ApiOperation(value = "自主学习4-有问有答", tags = {"自主学习4-有问有答"})
    public R<Map<String, Object>> questionsAndAnswers(@RequestParam Integer week, @RequestParam Integer day) {
        // 判断当前登录用户是否为 会员
        Boolean isVip = userService.isVip();
        LambdaQueryChainWrapper<TStudyAnswer> wrapper = studyAnswerService.lambdaQuery().eq(TStudyAnswer::getWeek, week)
                .eq(TStudyAnswer::getDay, day).eq(TStudyAnswer::getDisabled, 0);
        // 非会员只能查看非会员题目,会员可以查看所有题目
        if (!isVip) {
            wrapper.eq(TStudyAnswer::getIsVip, 0);
        }
        List<TStudyAnswer> answerList = wrapper.list();
        return R.ok(studyService.questionsAndAnswers(week, day, answerList));
    }
    /**
     * 自主学习5-音图相配
     *
     * @param week 周目
     * @param day  所属day
     */
    @GetMapping("/pictureMateVoice")
    @ApiOperation(value = "自主学习5-音图相配", tags = {"自主学习5-音图相配"})
    public R<Map<String, Object>> pictureMateVoice(@RequestParam Integer week, @RequestParam Integer day) {
        // 判断当前登录用户是否为 会员 todo
        TStudyPair pair = studyPairService.lambdaQuery().eq(TStudyPair::getWeek, week)
                .eq(TStudyPair::getDay, day).eq(TStudyPair::getDisabled, 0).one();
        return R.ok(studyService.pictureMateVoice(week, day, pair));
    }
    /**
     * 自主游戏1-超级听力
     *
     * @param difficulty 难度(0入门、1中级、2困难)
     * @param week       所属周目
     */
    @GetMapping("/gameHearing")
    @ApiOperation(value = "自主游戏1-超级听力", tags = {"自主游戏1-超级听力(difficulty: 0入门、1中级、2高级)"})
    public R<Map<String, Object>> gameHearing(@RequestParam Integer difficulty, @RequestParam Integer week) {
        Map<String, Object> result = new HashMap<>(8);
        TGame game = gameService.lambdaQuery().eq(TGame::getWeek, week)
                .eq(TGame::getDisabled, 0).one();
        game.setIntegral(game.getIntegral().split(",")[difficulty]);
        game.setTime(game.getTime().split(",")[difficulty]);
        result.put("game", game);
        // 检验是否完成难度
        studyService.checkDifficulty(difficulty, week, game);
        List<String> subjectId = getSubjectId(week);
        // 判断周目下题目是否足够
        if (subjectId.size() < game.getCount()) {
            throw new GlobalException("当前周目下day1 - day5题目不足!");
        }
        // 根据游戏设置数量获取图片及语音
        List<String> subjectData = new ArrayList<>();
        Random random = new Random();
        // 获取列表大小
        int dataSize = subjectId.size();
        // 生成随机索引并获取数据
        for (int i = 0; i < game.getCount(); i++) {
            // 生成随机索引
            int randomIndex = random.nextInt(dataSize);
            // 获取对应的数据并加入结果列表
            subjectData.add(subjectId.get(randomIndex));
        }
        result.put("subject", subjectService.lambdaQuery().in(TSubject::getId, subjectData).eq(TSubject::getState, 1).list());
        return R.ok(result);
    }
    /**
     * 自主游戏2-超级记忆
     *
     * @param difficulty 难度(0入门、1中级、2困难)
     * @param week       所属周目
     */
    @GetMapping("/gameMemory")
    @ApiOperation(value = "自主游戏2-超级记忆", tags = {"自主游戏2-超级记忆(difficulty: 0入门、1中级、2高级)"})
    public R<Map<String, Object>> gameMemory(@RequestParam Integer difficulty, @RequestParam Integer week) {
        Map<String, Object> result = new HashMap<>(8);
        TGame game = gameService.lambdaQuery().eq(TGame::getWeek, week).eq(TGame::getDisabled, 0).one();
        result.put("game", game);
        // 检验是否完成难度
        studyService.checkDifficulty(difficulty, week, game);
        List<String> subjectId = getSubjectId(week);
        // 判断周目下题目是否足够
        if (subjectId.size() < game.getCount()) {
            throw new GlobalException("当前周目下day1 - day5题目不足!");
        }
        // 根据游戏设置数量获取图片及语音
        List<String> subjectData = new ArrayList<>();
        Random random = new Random();
        // 获取列表大小
        int dataSize = subjectId.size();
        // 生成随机索引并获取数据
        for (int i = 0; i < game.getCount(); i++) {
            // 生成随机索引
            int randomIndex = random.nextInt(dataSize);
            // 获取对应的数据并加入结果列表
            subjectData.add(subjectId.get(randomIndex));
        }
        result.put("subject", subjectService.lambdaQuery().in(TSubject::getId, subjectData).eq(TSubject::getState, 1).list());
        return R.ok(result);
    }
    /**
     * 自主游戏完成
     * 记录游戏测试成绩
     *
     * @param completeStudy 学习信息
     */
    @PostMapping("/gameAchievement")
    @ApiOperation(value = "完成游戏-记录游戏测试成绩", tags = {"完成游戏-记录游戏测试成绩"})
    public R<?> gameAchievement(@RequestBody CompleteGameDTO completeStudy) {
        TGame game = gameService.getById(completeStudy.getGameId());
        // 游戏测试记录
        Boolean add = gameRecordService.add(completeStudy);
        // 添加积分明细记录
        add = add && integralRecordService.add(game.getIntegral(), completeStudy.getMethod());
        // 用户账户添加积分
        return R.ok(add);
    }
    private List<String> getSubjectId(Integer week) {
        // 当前week下day1 - day5所有题目
        List<String> subjectId = redisService.getCacheList(RedisConstants.HEARING_TREE);
        if (null == subjectId || subjectId.isEmpty()) {
            List<String> listenSubject = studyListenService.lambdaQuery().eq(TStudyListen::getWeek, week)
                    .eq(TStudyListen::getDisabled, 0).list().stream().map(TStudyListen::getSubject).collect(Collectors.toList());
            List<String> inductionSubject = studyInductionService.lambdaQuery().eq(TStudyInduction::getWeek, week)
                    .eq(TStudyInduction::getDisabled, 0).list().stream().map(TStudyInduction::getSubject).collect(Collectors.toList());
            List<String> lookSubject = studyLookService.lambdaQuery().eq(TStudyLook::getWeek, week)
                    .eq(TStudyLook::getDisabled, 0).list().stream().map(TStudyLook::getSubject).collect(Collectors.toList());
            List<String> pairSubject = studyPairService.lambdaQuery().eq(TStudyPair::getWeek, week)
                    .eq(TStudyPair::getDisabled, 0).list().stream().map(TStudyPair::getSubject).collect(Collectors.toList());
            listenSubject.addAll(inductionSubject);
            listenSubject.addAll(lookSubject);
            listenSubject.addAll(pairSubject);
            // 获取具体subject信息
            subjectId = new ArrayList<>();
            for (String subject : listenSubject) {
                subjectId.addAll(Arrays.asList(subject.split(",")));
            }
            redisService.setCacheList(RedisConstants.HEARING_TREE, subjectId);
        }
        return subjectId;
    }
    /**
     * 自主故事1-看图配音
     *
     * @param week 周目
     */
    @GetMapping("/lookPictureDbu")
    @ApiOperation(value = "自主故事1-看图配音", tags = {"自主故事1-看图配音"})
    public R<Map<String, Object>> lookPictureDbu(@RequestParam Integer week) {
        // 看图配音信息
        TStoryListen listen = storyListenService.lambdaQuery().eq(TStoryListen::getWeek, week).one();
        // 获取对应图片语音
        List<String> list = Arrays.asList(listen.getLookStory().split(","));
        Map<String, Object> result = new HashMap<>(8);
        result.put("listen", listen);
        result.put("info", subjectService.lambdaQuery().in(TSubject::getId, list).list());
        return R.ok(result);
    }
    /**
     * 自主故事2-框架记忆
     *
     * @param week 周目
     */
    @GetMapping("/frameworkMemory")
    @ApiOperation(value = "自主故事2-框架记忆", tags = {"自主故事2-框架记忆"})
    public R<Map<String, Object>> frameworkMemory(@RequestParam Integer week) {
        // 看图配音信息
        TStoryListen listen = storyListenService.lambdaQuery().eq(TStoryListen::getWeek, week).one();
        // 获取对应图片语音
        List<String> list = Arrays.asList(listen.getStory().split(","));
        Map<String, Object> result = new HashMap<>(8);
        result.put("listen", listen);
        result.put("info", subjectService.lambdaQuery().in(TSubject::getId, list).list());
        return R.ok(result);
    }
    /**
     * 学习完成,生成学习记录,积分明细记录
     *
     * @param completeStudy 完成学习信息
     */
    @PostMapping("/completeLearning")
    @ApiOperation(value = "完成学习", tags = {"完成学习"})
    public R<Boolean> completeLearning(@RequestBody CompleteStudyDTO completeStudy) {
        // 登录用户id
        Long userId = SecurityUtils.getUserId();
        // 获取user详细信息,改变积分
        TUser user = userService.getById(userId);
        user.setIntegral(user.getIntegral() + completeStudy.getIntegral());
        boolean update = userService.updateById(user);
        // 生成积分明细记录
        TIntegralRecord integralRecord = new TIntegralRecord();
        integralRecord.setIntegral(String.valueOf(completeStudy.getIntegral()));
        integralRecord.setMethod(completeStudy.getMethod());
        integralRecord.setUserId(SecurityUtils.getUserId().intValue());
        return R.ok(update && integralRecordService.save(integralRecord));
    }
    /**
     * 完成故事类型
     */
    @GetMapping("/completeStory")
    @ApiOperation(value = "完成故事学习", tags = {"完成故事学习"})
    public R<?> completeStory(@RequestParam Integer integral, @RequestParam Integer storyId,
                              @RequestParam @ApiParam("完成答题/完成听故事") String method) {
        // 添加积分明细记录
        Boolean add = integralRecordService.add(String.valueOf(integral), method);
        // 用户信息
        Long userId = SecurityUtils.getUserId();
        TUser user = userService.lambdaQuery().eq(TUser::getId, userId).one();
        // 返回结果
        user.setIntegral(user.getIntegral() + integral);
        return R.ok(add && userService.updateById(user));
    }
    @GetMapping("/studyRecord")
    @ApiOperation(value = "个人中心-学习记录", tags = {"个人中心-学习记录"})
    public R<Map<String, Object>> studyRecord() {
        Long userId = SecurityUtils.getUserId();
        Map<String, Object> result = new HashMap<>(8);
        // 学习记录
        result.put("record", userStudyService.lambdaQuery().eq(TUserStudy::getUserId, userId)
                .eq(TUserStudy::getDisabled, 0).one());
        // 游戏测试成绩
        result.put("gameAchievement", gameRecordService.lambdaQuery().eq(TGameRecord::getUserId, userId)
                .eq(TGameRecord::getDisabled, 0).list());
        return R.ok(result);
    }
    @GetMapping("/integralDetail")
    @ApiOperation(value = "个人中心-积分明细", tags = {"个人中心-积分明细"})
    public R<IPage<TIntegralRecord>> integralDetail(String time,
                                                    @RequestParam("pageNum") Integer pageNum,
                                                    @RequestParam("pageSize") Integer pageSize) {
        return R.ok(integralRecordService.integralDetail(new Page<>(pageNum, pageSize), SecurityUtils.getUserId(), time));
    }
    /**
     * 生成积分明细-用于远程调用
     *
     * @param integral 积分变动信息
     * @param method   变动源
     */
    @GetMapping("/addIntegralDetail")
    @ApiOperation(value = "添加-积分明细", tags = {"添加-积分明细"})
    public R<Boolean> addIntegralDetail(@RequestParam("integral") String integral, @RequestParam("method") String method) {
        TIntegralRecord integralRecord = new TIntegralRecord();
        integralRecord.setIntegral(integral);
        integralRecord.setMethod(method);
        integralRecord.setUserId(SecurityUtils.getUserId().intValue());
        integralRecord.setDisabled(Boolean.FALSE);
        return R.ok(integralRecordService.save(integralRecord));
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java
@@ -3,8 +3,8 @@
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.core.constant.RedisConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.JwtUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.redis.service.RedisService;
@@ -21,25 +21,22 @@
import com.ruoyi.study.domain.TVipOrder;
import com.ruoyi.study.dto.AppUserQuery;
import com.ruoyi.study.dto.UserInfoQuery;
import com.ruoyi.study.request.RegisterPhoneRequest;
import com.ruoyi.study.service.ITUserService;
import com.ruoyi.study.service.IVipOrderService;
import com.ruoyi.study.utils.PayMoneyUtil;
import com.ruoyi.study.utils.UUIDUtil;
import com.ruoyi.study.vo.*;
import com.ruoyi.system.api.domain.SysRole;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser;
import com.ruoyi.system.api.model.LoginUserParent;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
@@ -66,12 +63,14 @@
    private ManagementClient managementClient;
    @Autowired
    private TokenService tokenService;
    @Resource
    private RedisService redisService;
    @PostMapping("/vipInfo")
    @ApiOperation(value = "会员中心-获取会员说明、当前登录用户是否为会员、会员购买规格", tags = {"家长端-个人中心"})
    public AjaxResult<List<VipInfoVO>> vipInfo() {
        if (tokenService.getLoginUser1() == null){
            return AjaxResult.tokenError("登录失效",new Object());
        if (tokenService.getLoginUser1() == null) {
            return AjaxResult.tokenError("登录失效", new Object());
        }
        List<VipInfoVO> vipInfoVOS = new ArrayList<>();
        List<TVipSet> data = managementClient.getVipSet1().getData();
@@ -82,13 +81,13 @@
            Integer userid = tokenService.getLoginUser1().getUserid();
            TUser byId = userService.getById(userid);
            // 先判断vipEndTime
            if (byId.getVipEndTime() == null){
            if (byId.getVipEndTime() == null) {
                vipInfoVO.setIsVip(0);
            }else{
            } else {
                // 判断会员到期时间是否大于当前时间
                if (byId.getVipEndTime().getTime() > new Date().getTime()){
                if (byId.getVipEndTime().getTime() > new Date().getTime()) {
                    vipInfoVO.setIsVip(1);
                }else{
                } else {
                    vipInfoVO.setIsVip(0);
                }
            }
@@ -97,6 +96,7 @@
        }
        return AjaxResult.ok(vipInfoVOS);
    }
    @Autowired
    private PayMoneyUtil payMoneyUtil;
@@ -107,16 +107,16 @@
            @ApiImplicitParam(name = "payType", value = "支付类型 1=微信 2=支付宝", required = true),
            @ApiImplicitParam(name = "id", value = "会员规格id", required = true),
    })
    public AjaxResult order(Integer payType,Integer id) throws Exception {
        if (tokenService.getLoginUser1() == null){
            return AjaxResult.tokenError("登录失效",new Object());
    public AjaxResult order(Integer payType, Integer id) throws Exception {
        if (tokenService.getLoginUser1() == null) {
            return AjaxResult.tokenError("登录失效", new Object());
        }
        Integer userid = tokenService.getLoginUser1().getUserid();
        TVipOrder tVipOrder = new TVipOrder();
        List<TVipSet> data = managementClient.getVipSet1().getData();
        Integer time = 0;
        for (TVipSet datum : data) {
            if (datum.getId() == id){
            if (datum.getId() == id) {
                tVipOrder.setMoney(datum.getAmount());
                time = datum.getTime();
            }
@@ -125,11 +125,11 @@
        tVipOrder.setUserId(userid);
        tVipOrder.setPayType(payType);
        tVipOrder.setCount(time);
        switch (payType){
        switch (payType) {
            case 1:
                return payMoneyUtil.weixinpay
                        ("购买会员", "",
                                id+"_"+tVipOrder.getId() + "_"+
                                id + "_" + tVipOrder.getId() + "_" +
                                        UUIDUtil.getRandomCode(8),
                                tVipOrder.getMoney().toString(),
                                "/base/wxPayBuyVip", "APP", "");
@@ -138,13 +138,14 @@
                        ("购买会员",
                                "购买会员下单支付",
                                "",
                                id+"_"+tVipOrder.getId()+"_"+
                                id + "_" + tVipOrder.getId() + "_" +
                                        UUIDUtil.getRandomCode(8),
                                tVipOrder.getMoney().toString(),
                                "/base/aliPayBuyVip");
        }
        return AjaxResult.success();
    }
    @ResponseBody
    @PostMapping("/base/aliPayBuyVip")
    public void addVipPaymentAliCallback(HttpServletRequest request, HttpServletResponse response) {
@@ -156,9 +157,9 @@
                // 会员规格id
                String s = out_trade_no.split("_")[0];
                Integer integer = Integer.valueOf(s);
                Integer time  = 0;
                Integer time = 0;
                for (TVipSet datum : managementClient.getVipSet1().getData()) {
                    if (datum.getId() == integer){
                    if (datum.getId() == integer) {
                        time = datum.getTime();
                    }
                }
@@ -173,7 +174,7 @@
                TUser byId1 = userService.getById(byId.getUserId());
                if (byId1.getVipPayTime() == null){
                if (byId1.getVipPayTime() == null) {
                    // 是否是首次充值会员
                    byId1.setVipPayTime(new Date());
                    Calendar calendar = Calendar.getInstance();
@@ -182,16 +183,16 @@
                    Date dateAfterOneMonth = calendar.getTime();
                    byId.setTime(dateAfterOneMonth);
                    byId1.setVipEndTime(dateAfterOneMonth);
                }else{
                } else {
                    // 不是首次 判断vipEndTime 是否到期 如果没有 加指定月份时间 如果到期了 将会员到期时间从当前增加指定月份
                    if (byId1.getVipEndTime().getTime() < new Date().getTime()){
                    if (byId1.getVipEndTime().getTime() < new Date().getTime()) {
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(new Date());
                        calendar.add(Calendar.MONTH, time);
                        Date dateAfterOneMonth = calendar.getTime();
                        byId.setTime(dateAfterOneMonth);
                        byId1.setVipEndTime(dateAfterOneMonth);
                    }else{
                    } else {
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(byId1.getVipEndTime());
                        calendar.add(Calendar.MONTH, time);
@@ -215,6 +216,7 @@
            e.printStackTrace();
        }
    }
    @ResponseBody
    @PostMapping("/base/wxPayBuyVip")
    public void wxPayBuyPackage(HttpServletRequest request, HttpServletResponse response) {
@@ -229,9 +231,9 @@
                // 会员规格id
                String s = out_trade_no.split("_")[0];
                Integer integer = Integer.valueOf(s);
                Integer time  = 0;
                Integer time = 0;
                for (TVipSet datum : managementClient.getVipSet1().getData()) {
                    if (datum.getId() == integer){
                    if (datum.getId() == integer) {
                        time = datum.getTime();
                    }
                }
@@ -246,7 +248,7 @@
                TUser byId1 = userService.getById(byId.getUserId());
                if (byId1.getVipPayTime() == null){
                if (byId1.getVipPayTime() == null) {
                    // 是否是首次充值会员
                    byId1.setVipPayTime(new Date());
                    Calendar calendar = Calendar.getInstance();
@@ -255,16 +257,16 @@
                    Date dateAfterOneMonth = calendar.getTime();
                    byId.setTime(dateAfterOneMonth);
                    byId1.setVipEndTime(dateAfterOneMonth);
                }else{
                } else {
                    // 不是首次 判断vipEndTime 是否到期 如果没有 加指定月份时间 如果到期了 将会员到期时间从当前增加指定月份
                    if (byId1.getVipEndTime().getTime() < new Date().getTime()){
                    if (byId1.getVipEndTime().getTime() < new Date().getTime()) {
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(new Date());
                        calendar.add(Calendar.MONTH, time);
                        Date dateAfterOneMonth = calendar.getTime();
                        byId.setTime(dateAfterOneMonth);
                        byId1.setVipEndTime(dateAfterOneMonth);
                    }else{
                    } else {
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(byId1.getVipEndTime());
                        calendar.add(Calendar.MONTH, time);
@@ -286,59 +288,55 @@
            e.printStackTrace();
        }
    }
    @PostMapping("/getPage")
    @ApiOperation(value = "获取注意事项", tags = {"家长端-注意事项"})
    public AjaxResult<String> getPage() {
        List<TPage> data = managementClient.getPage1().getData();
        for (TPage datum : data) {
            if (datum.getType() == 4){
                return AjaxResult.success(datum.getImg());
            if (datum.getType() == 4) {
                return AjaxResult.success(datum);
            }
        }
        return AjaxResult.success();
    }
    @PostMapping("/useGuide")
    @ApiOperation(value = "使用指南", tags = {"家长端-使用指南"})
    public AjaxResult<PageInfo<TUseGuide>> useGuide(@RequestBody UseGuideQuery query) {
        PageInfo<TUseGuide> data = managementClient.useGuide1(query).getData();
        return AjaxResult.success(data);
    }
    @PostMapping("/feedBack")
    @ApiOperation(value = "反馈", tags = {"家长端-意见反馈"})
    public AjaxResult feedBack(@RequestBody TFeedback dto) {
        if (tokenService.getLoginUser1() == null){
            return AjaxResult.tokenError("登录失效",new Object());
        if (tokenService.getLoginUser1() == null) {
            return AjaxResult.tokenError("登录失效", new Object());
        }
        LoginUserParent loginUser1 = tokenService.getLoginUser1();
        dto.setUserId(loginUser1.getUserid());
        managementClient.addFeedBack(dto);
        return AjaxResult.success("反馈成功");
    }
    @PostMapping("/parentLogin")
    @ApiOperation(value = "登录", tags = {"家长端-登录"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "手机号", name = "phone", dataType = "string", required = true),
            @ApiImplicitParam(value = "验证码", name = "phoneCode", dataType = "string", required = true)
    })
    public AjaxResult login(String phone, String phoneCode)
    {
    public AjaxResult login(String phone, String phoneCode) {
        TUser tUser1 = userService.getOne(new QueryWrapper<TUser>()
                .ne("state", 3)
                .eq("phone",phone));
        if (tUser1 != null){
            if (tUser1.getState() == 2){
                .eq("phone", phone));
        if (tUser1 != null) {
            if (tUser1.getState() == 2) {
                return AjaxResult.error("登录失败,您的账号已被冻结!");
            }
        }else{
        } else {
            // todo 验证码校验
            tUser1 = new TUser();
            // 注册
            tUser1.setName(phone);
            tUser1.setAccount(phone);
            tUser1.setState(1);
            tUser1.setInsertTime(new Date());
            tUser1.setCreateTime(new Date());
            tUser1.setPhone(phone);
            tUser1 = getUser(phone);
            userService.save(tUser1);
        }
        LoginUserParent loginUserParent = new LoginUserParent();
@@ -347,11 +345,79 @@
        loginUserParent.setPhone(tUser1.getPhone());
        loginUserParent.setLoginTime(new Date().getTime());
        HashMap<String, Object> map = new HashMap<>();
        map.put("token",tokenService.createToken1(loginUserParent));
        map.put("token", tokenService.createToken1(loginUserParent));
        // 获取登录token
        return AjaxResult.success(map);
    }
    /**
     * 学生端登录
     *
     * @param phoneRequest 手机号及手机验证码
     */
    @PostMapping("/studyLogin")
    @ApiOperation(value = "学习端-登录", tags = {"学习端-登录"})
    public AjaxResult studyLogin(@RequestBody @Validated RegisterPhoneRequest phoneRequest) {
        String phone = phoneRequest.getPhone();
        String phoneCode = phoneRequest.getPhoneCode();
        TUser user = userService.getOne(new QueryWrapper<TUser>()
                .ne("state", 3)
                .eq("phone", phone));
        if (user != null) {
            if (user.getState() == 2) {
                return AjaxResult.error("登录失败,您的账号已被冻结!");
            }
        } else {
            // 手机验证码校验
            Object redisPhoneCode = redisService.getCacheObject(RedisConstants.PHONE_CODE + phone);
            if (null == redisPhoneCode) {
                return AjaxResult.error("登录失败,手机验证码已过期!");
            } else {
                // redis 验证码的value 为 code:时间戳
                String rCodeAndTime = String.valueOf(redisPhoneCode);
                String rCode = rCodeAndTime.split(":")[0];
                if (!rCode.equalsIgnoreCase(phoneCode)) {
                    return AjaxResult.error("登录失败,手机验证码输入有误!");
                } else {
                    user = getUser(phone);
                    userService.save(user);
                }
            }
        }
        // 生成登录用户信息
        LoginUserParent loginUserParent = new LoginUserParent();
        loginUserParent.setName(user.getName());
        loginUserParent.setUserid(user.getId());
        loginUserParent.setPhone(user.getPhone());
        loginUserParent.setLoginTime(System.currentTimeMillis());
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", tokenService.createTokenStudy(loginUserParent));
        // 获取登录token
        return AjaxResult.success(map);
    }
    private TUser getUser(String phone) {
        TUser user = new TUser();
        // 注册
        user.setName(phone);
        user.setAccount(phone);
        user.setState(1);
        user.setInsertTime(new Date());
        user.setCreateTime(new Date());
        user.setPhone(phone);
        return user;
    }
    /**
     * 家长端 学习端都可用
     *
     * @param phone 手机号
     */
    @GetMapping("/sendPhoneCode")
    @ApiOperation(value = "发送手机验证码", tags = {"家长端/学习端-发送手机验证码"})
    public AjaxResult sendPhoneCode(@RequestParam String phone) {
        return userService.phoneCode(phone) ? AjaxResult.success() : AjaxResult.error();
    }
    @PostMapping("/deleteUser")
    @ApiOperation(value = "注销当前帐号", tags = {"家长端-个人中心"})
@@ -359,8 +425,8 @@
            @ApiImplicitParam(name = "Authorization", value = "Bearer eyJhbGciOiJIUzUxMiJ....", required = true, paramType = "header"),
    })
    public AjaxResult deleteUser() {
        if (tokenService.getLoginUser1() == null){
           return AjaxResult.tokenError("登录失效");
        if (tokenService.getLoginUser1() == null) {
            return AjaxResult.tokenError("登录失效");
        }
        Integer userid = tokenService.getLoginUser1().getUserid();
        TUser tUser = userService.getById(userid);
@@ -371,24 +437,25 @@
        userService.removeById(tUser);
        return AjaxResult.success("注销成功");
    }
    @PostMapping("/logout")
    @ApiOperation(value = "退出登录", tags = {"家长端-个人中心"})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "Bearer eyJhbGciOiJIUzUxMiJ....", required = true, paramType = "header"),
    })
    public AjaxResult logout(HttpServletRequest request) {
        if (tokenService.getLoginUser1() == null){
        if (tokenService.getLoginUser1() == null) {
            return AjaxResult.tokenError("登录失效");
        }
        String token = SecurityUtils.getToken(request);
        if (com.ruoyi.common.core.utils.StringUtils.isNotEmpty(token))
        {
        if (com.ruoyi.common.core.utils.StringUtils.isNotEmpty(token)) {
            // 删除用户缓存记录
            AuthUtil.logoutByToken1(token);
        }
        // todo 清除token
        return AjaxResult.success("退出登录成功");
    }
    @PostMapping("/updateUserInfo")
    @ApiOperation(value = "修改个人资料", tags = {"家长端-个人中心"})
    @ApiImplicitParams({
@@ -397,40 +464,38 @@
            @ApiImplicitParam(name = "phone", value = "电话 改什么就只传什么"),
            @ApiImplicitParam(name = "headImg", value = "头像 改什么就只传什么"),
    })
    public AjaxResult updateUserInfo(String name, String phone,String headImg) {
        if (tokenService.getLoginUser1() == null){
            return AjaxResult.tokenError("登录失效",new Object());
        }
    public AjaxResult updateUserInfo(String name, String phone, String headImg) {
        // todo 获取用户id
        Integer userid = tokenService.getLoginUser1().getUserid();
        TUser byId = userService.getById(userid);
        if (StringUtils.hasLength(name)){
        if (StringUtils.hasLength(name)) {
            byId.setName(name);
        }
        if (StringUtils.hasLength(phone)){
        if (StringUtils.hasLength(phone)) {
            // 先判断手机号是否和当前用户手机号一致
//            if (byId == null){
//                return AjaxResult.tokenError("登录失效");
//            }
            if (phone.equals(byId.getPhone())){
            if (phone.equals(byId.getPhone())) {
                return AjaxResult.error("更换的手机号不能和原手机号相同");
            }
            List<TUser> list = userService.list(new QueryWrapper<TUser>()
                    .eq("phone", phone)
                    .ne("state", 3));
            if (list.size() > 0){
                return AjaxResult.error("更换的手机号已被使用",new Object());
            if (list.size() > 0) {
                return AjaxResult.error("更换的手机号已被使用", new Object());
            }
            byId.setPhone(phone);
        }
        if (StringUtils.hasLength(headImg)){
        if (StringUtils.hasLength(headImg)) {
            byId.setHeadImg(headImg);
        }
        userService.updateById(byId);
        return AjaxResult.success("修改成功");
    }
    @PostMapping("/vipBack/{id}")
    @ApiOperation(value = "会员退款", tags = {"管理后台-会员退款"})
    public R vipBack(@PathVariable("id") Integer id) throws AlipayApiException {
@@ -439,11 +504,11 @@
        String transactionId = byId.getTransactionId();
        // 内部订单号
        String outTradeNo = byId.getOutTradeNo();
        switch (byId.getPayType()){
        switch (byId.getPayType()) {
            case 1:
                 // 微信退款
                // 微信退款
                Map<String, String> stringStringMap = payMoneyUtil.wxRefund(transactionId, outTradeNo, byId.getMoney().toString(), byId.getMoney().toString(), "/base/wxRefund");
                if (stringStringMap.get("code").equals("SUCCESS")){
                if (stringStringMap.get("code").equals("SUCCESS")) {
                    byId.setPayState(3);
                    byId.setBackTime(new Date());
                    vipOrderService.updateById(byId);
@@ -460,12 +525,12 @@
                            .eq("payState", 2)
                            .orderByDesc("createTime"));
                    int size = list.size();
                    if (size == 0){
                    if (size == 0) {
                        // 证明这是用户第一次充值会员 将首次充值会员时间和会员到期时间清空
                        byId1.setVipEndTime(null);
                        byId1.setVipPayTime(null);
                        userService.updateById(byId1);
                    }else{
                    } else {
                        // 最近的一次充值会员时间
                        TVipOrder tVipOrder = list.get(0);
                        // 将会员到期时间回退到上一次
@@ -474,13 +539,13 @@
                    }
                    return R.ok();
                }else{
                } else {
                    return R.fail(stringStringMap.get("msg"));
                }
            case 2:
                // 支付宝退款
                Map<String, String> stringStringMap1 = payMoneyUtil.aliRefund(transactionId,byId.getMoney().toString());
                if (stringStringMap1.get("code").equals("10000")){
                Map<String, String> stringStringMap1 = payMoneyUtil.aliRefund(transactionId, byId.getMoney().toString());
                if (stringStringMap1.get("code").equals("10000")) {
                    byId.setPayState(3);
                    byId.setBackTime(new Date());
                    vipOrderService.updateById(byId);
@@ -497,12 +562,12 @@
                            .eq("payState", 2)
                            .orderByDesc("createTime"));
                    int size = list.size();
                    if (size == 0){
                    if (size == 0) {
                        // 证明这是用户第一次充值会员 将首次充值会员时间和会员到期时间清空
                        byId1.setVipEndTime(null);
                        byId1.setVipPayTime(null);
                        userService.updateById(byId1);
                    }else{
                    } else {
                        // 最近的一次充值会员时间
                        TVipOrder tVipOrder = list.get(0);
                        // 将会员到期时间回退到上一次
@@ -510,24 +575,25 @@
                        userService.updateById(byId1);
                    }
                    return R.ok();
                }else{
                } else {
                    return R.fail(stringStringMap1.get("msg"));
                }
        }
        return R.ok();
    }
    @PostMapping("/userList")
    @ApiOperation(value = "用户列表", tags = {"管理后台-用户管理"})
    public R<PageInfo<AppUserVO>> couponReceive(@RequestBody AppUserQuery query) {
        PageInfo<AppUserVO> res = new PageInfo<>(query.getPageNumber(), query.getPageSize());
        List<AppUserVO> list =  userService.listAll(query);
        List<AppUserVO> list = userService.listAll(query);
        for (AppUserVO appUserVO : list) {
            if (appUserVO.getVipEndTime() == null){
            if (appUserVO.getVipEndTime() == null) {
                appUserVO.setIsVip(0);
            }else{
                if (appUserVO.getVipEndTime().getTime() > System.currentTimeMillis()){
            } else {
                if (appUserVO.getVipEndTime().getTime() > System.currentTimeMillis()) {
                    appUserVO.setIsVip(1);
                }else{
                } else {
                    appUserVO.setIsVip(0);
                }
            }
@@ -551,16 +617,16 @@
        Date insertTime = byId.getInsertTime();
        Date vipPayTime = byId.getVipPayTime();
        Date vipEndTime = byId.getVipEndTime();
        if (vipEndTime == null){
        if (vipEndTime == null) {
            res.setIsVip(0);
        }else{
            if (vipEndTime.getTime() > System.currentTimeMillis()){
        } else {
            if (vipEndTime.getTime() > System.currentTimeMillis()) {
                res.setIsVip(1);
            }else{
            } else {
                res.setIsVip(0);
            }
        }
        if (vipPayTime!=null){
        if (vipPayTime != null) {
            res.setVipPayTime(format.format(vipPayTime));
        }
        res.setState(byId.getState());
@@ -582,15 +648,16 @@
        res.setGameRecords(list);
        return R.ok(res);
    }
    @PostMapping("/freeze/{id}")
    @ApiOperation(value = "冻结/解冻", tags = {"管理后台-用户管理"})
    public R freeze(@PathVariable("id")  Integer id) {
    public R freeze(@PathVariable("id") Integer id) {
        TUser byId = userService.getById(id);
        if (byId.getState() == 1) {
            byId.setState(2);
            userService.updateById(byId);
            return R.ok("冻结成功");
        }else {
        } else {
            byId.setState(1);
            userService.updateById(byId);
            return R.ok("解冻成功");
@@ -602,7 +669,7 @@
    @ApiOperation(value = "列表查询", tags = {"管理后台-会员管理"})
    public R<PageInfo<VipOrderVO>> vipOrderList(@RequestBody AppUserQuery query) {
        PageInfo<VipOrderVO> res = new PageInfo<>(query.getPageNumber(), query.getPageSize());
        List<VipOrderVO> list =  vipOrderService.listAll(query);
        List<VipOrderVO> list = vipOrderService.listAll(query);
        res.setRecords(list);
        res.setTotal(list.size());
        return R.ok(res);
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TGameRecord.java
@@ -30,20 +30,35 @@
     * 用户id
     */
    @ApiModelProperty(value = "用户id")
    private Integer userId;/**
    private Integer userId;
    /**
     * /**
     * 正确率
     */
    @ApiModelProperty(value = "正确率")
    private Integer accuracy;/**
    private Integer accuracy;
    /**
     * 游戏名称
     */
    @ApiModelProperty(value = "游戏名称")
    private String gameName;/**
    private String gameName;
    /**
     * 用时时间 秒
     */
    @ApiModelProperty(value = "用时时间 秒")
    private Integer useTime;
    /**
     * 游戏Id
     */
    @ApiModelProperty(value = "游戏Id")
    private Integer gameId;
    /**
     * 游戏难度(0入门、1中级、2高级)
     */
    @ApiModelProperty(value = "游戏难度(0入门、1中级、2高级)")
    private Integer gameDifficulty;
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TIntegralRecord.java
@@ -39,5 +39,12 @@
     */
    private Integer userId;
    /**
     * 前端用,返回积分变动类型
     *
     * @return 变动类型中文字符串
     */
    public String getType() {
        return integral.startsWith("-") ? "减少" : "增加";
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteGameDTO.java
New file
@@ -0,0 +1,50 @@
package com.ruoyi.study.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author HJL
 * @version 1.0
 * @since 2024-05-20 14:04
 */
@Data
public class CompleteGameDTO {
    /**
     * 游戏难度
     */
    @ApiModelProperty("游戏难度")
    private Integer difficulty;
    /**
     * 游戏id
     */
    @ApiModelProperty("游戏id")
    private Integer gameId;
    /**
     * 用时时间 秒
     */
    @ApiModelProperty("用时时间 秒")
    private Integer useTime;
    /**
     * 游戏名称
     */
    @ApiModelProperty("游戏名称")
    private String gameName;
    /**
     * 正确率
     */
    @ApiModelProperty("正确率")
    private Integer accuracy;
    /**
     * 完成听故事、完成游戏、完成答题、完成每日学习、商城消费
     */
    @ApiModelProperty("完成听故事、完成游戏、完成答题、完成每日学习、商城消费")
    private String method;
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteStudyDTO.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.study.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author HJL
 * @version 1.0
 * @since 2024-05-20 14:04
 */
@Data
public class CompleteStudyDTO {
    /**
     * 积分数量
     */
    @ApiModelProperty("积分数量")
    private Integer integral;
    /**
     * 完成听故事、完成游戏、完成答题、完成每日学习、商城消费
     */
    @ApiModelProperty("完成听故事、完成游戏、完成答题、完成每日学习、商城消费")
    private String method;
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/StudyWeekDTO.java
New file
@@ -0,0 +1,28 @@
package com.ruoyi.study.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author HJL
 */
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(value = "选择周目列表DTO")
public class StudyWeekDTO extends StudyDTO {
    @ApiModelProperty("完成后可获积分数")
    private Integer totalIntegral;
    @ApiModelProperty("所属季度")
    private Integer quarter;
    @ApiModelProperty("本周主题")
    private String title;
    @ApiModelProperty("study表Id")
    private String id;
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/TGoodsVO.java
New file
@@ -0,0 +1,233 @@
package com.ruoyi.study.dto;
import com.ruoyi.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
 * <p>
 * 商品表
 * </p>
 *
 * @author 无关风月
 * @since 2024-04-26
 */
@Data
public class TGoodsVO extends BaseModel {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "剩余数量")
    private Long inventory;
    /**
     * 主键id
     */
    private Integer id;
    /**
     * 商品名称
     */
    @ApiModelProperty(value = "商品名称")
    private String name;
    /**
     * 所需积分
     */
    @ApiModelProperty(value = "所需积分")
    private String integral;
    /**
     * 商品原价
     */
    @ApiModelProperty(value = "商品原价")
    private BigDecimal price;
    /**
     * 商品总数
     */
    @ApiModelProperty(value = "商品总数")
    private Integer total;
    /**
     * 剩余数量
     */
    @ApiModelProperty(value = "剩余数量")
    private Integer surplus;
    /**
     * 单个用户可兑换数量
     */
    @ApiModelProperty(value = "单个用户可兑换数量")
    private Integer userCount;
    /**
     * 商品类型id
     */
    @ApiModelProperty(value = "商品类型id")
    private Integer typeId;
    /**
     * 商品封面图
     */
    @ApiModelProperty(value = "商品封面图")
    private String coverImg;
    /**
     * 商品详情图片 多张逗号隔开
     */
    @ApiModelProperty(value = "商品详情图片 多张逗号隔开")
    private String detailImg;
    /**
     * 商品详情
     */
    @ApiModelProperty(value = "商品详情")
    private String detail;
    /**
     * 插入时间
     */
    @ApiModelProperty(value = "插入时间")
    private Date insertTime;
    /**
     * 是否删除 0否1是
     */
    @ApiModelProperty(value = "是否删除 0否1是")
    private Integer isDelete;
    /**
     * 兑换类型 1.0商城只有积分兑换 保留字段
     */
    @ApiModelProperty(value = "前端忽略 兑换类型 1.0商城只有积分兑换 保留字段 ")
    private Integer type;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getintegral() {
        return
integral;
    }
    public void setintegral(String
integral) {
        this.
integral =
integral;
    }
    public BigDecimal getPrice() {
        return price;
    }
    public void setPrice(BigDecimal price) {
        this.price = price;
    }
    public Integer getTotal() {
        return total;
    }
    public void setTotal(Integer total) {
        this.total = total;
    }
    public Integer getSurplus() {
        return surplus;
    }
    public void setSurplus(Integer surplus) {
        this.surplus = surplus;
    }
    public Integer getUserCount() {
        return userCount;
    }
    public void setUserCount(Integer userCount) {
        this.userCount = userCount;
    }
    public Integer getTypeId() {
        return typeId;
    }
    public void setTypeId(Integer typeId) {
        this.typeId = typeId;
    }
    public String getCoverImg() {
        return coverImg;
    }
    public void setCoverImg(String coverImg) {
        this.coverImg = coverImg;
    }
    public String getDetailImg() {
        return detailImg;
    }
    public void setDetailImg(String detailImg) {
        this.detailImg = detailImg;
    }
    public String getDetail() {
        return detail;
    }
    public void setDetail(String detail) {
        this.detail = detail;
    }
    public Date getInsertTime() {
        return insertTime;
    }
    public void setInsertTime(Date insertTime) {
        this.insertTime = insertTime;
    }
    public Integer getIsDelete() {
        return isDelete;
    }
    public void setIsDelete(Integer isDelete) {
        this.isDelete = isDelete;
    }
    public Integer getType() {
        return type;
    }
    public void setType(Integer type) {
        this.type = type;
    }
    @Override
    public String toString() {
        return "TGoods{" +
        ", id=" + id +
        ", name=" + name +
        ", integral=" +
integral +
        ", price=" + price +
        ", total=" + total +
        ", surplus=" + surplus +
        ", userCount=" + userCount +
        ", typeId=" + typeId +
        ", coverImg=" + coverImg +
        ", detailImg=" + detailImg +
        ", detail=" + detail +
        ", insertTime=" + insertTime +
        ", isDelete=" + isDelete +
        ", type=" + type +
        "}";
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TIntegralRecordMapper.java
@@ -1,8 +1,10 @@
package com.ruoyi.study.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.study.domain.TGameRecord;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.study.domain.TIntegralRecord;
import org.apache.ibatis.annotations.Param;
/**
 * <p>
@@ -14,4 +16,13 @@
 */
public interface TIntegralRecordMapper extends BaseMapper<TIntegralRecord> {
    /**
     * 用户积分明细
     *
     * @param page   分页
     * @param userId 用户id
     * @param time   yyyy-mm格式时间
     * @return 分页列表
     */
    IPage<TIntegralRecord> integralDetail(@Param("userId") Long userId, @Param("time") String time, Page<TIntegralRecord> page);
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TStudyMapper.java
@@ -2,6 +2,10 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.study.domain.TStudy;
import com.ruoyi.study.dto.StudyWeekDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
@@ -13,4 +17,12 @@
 */
public interface TStudyMapper extends BaseMapper<TStudy> {
    /**
     * 查询周目列表
     *
     * @param type    所属类型
     * @param quarter 季度
     * @return 列表
     */
    List<StudyWeekDTO> weekList(@Param("type") Integer type,@Param("quarter") Integer quarter);
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/request/RegisterPhoneRequest.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.study.request;
import com.ruoyi.common.core.constant.Constants;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
/**
 * @author HJL
 * @version 1.0
 * @since 2024-05-17 14:57
 */
@Data
public class RegisterPhoneRequest {
    @Pattern(regexp = Constants.PHONE, message = "手机号不合法,请重试!")
    private String phone;
    @NotBlank(message = "验证码不能为空!")
    private String phoneCode;
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITGameRecordService.java
@@ -1,8 +1,8 @@
package com.ruoyi.study.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.study.domain.TGame;
import com.ruoyi.study.domain.TGameRecord;
import com.ruoyi.study.dto.CompleteGameDTO;
/**
 * <p>
@@ -14,4 +14,11 @@
 */
public interface ITGameRecordService extends IService<TGameRecord> {
    /**
     * 生成游戏记录
     *
     * @param gameAchievement 游戏信息
     * @return 操作结果
     */
    Boolean add(CompleteGameDTO gameAchievement);
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITIntegralRecordService.java
@@ -1,7 +1,8 @@
package com.ruoyi.study.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.study.domain.TGameRecord;
import com.ruoyi.study.domain.TIntegralRecord;
/**
@@ -14,4 +15,22 @@
 */
public interface ITIntegralRecordService extends IService<TIntegralRecord> {
    /**
     * 用户积分明细
     *
     * @param page   分页
     * @param userId 用户id
     * @param time   yyyy-mm格式时间
     * @return 分页列表
     */
    IPage<TIntegralRecord> integralDetail(Page<TIntegralRecord> page, Long userId, String time);
    /**
     * 生成积分明细
     *
     * @param integral 积分 (增加积分+ 、减少积分-)
     * @param method   积分来源
     * @return 操作结果
     */
    Boolean add(String integral, String method);
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITStudyService.java
@@ -1,7 +1,11 @@
package com.ruoyi.study.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.study.domain.TStudy;
import com.ruoyi.study.domain.*;
import com.ruoyi.study.dto.StudyWeekDTO;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -13,4 +17,82 @@
 */
public interface ITStudyService extends IService<TStudy> {
    /**
     * 查询周目列表
     *
     * @param type    所属类型
     * @param quarter 季度
     * @return 列表
     */
    List<StudyWeekDTO> weekList(Integer type, Integer quarter);
    /**
     * 获取学习进度及学习时长等信息
     *
     * @param userId 用户id
     * @param week   周目
     * @param day    所属day
     * @return 学习信息
     */
    TUserStudy studySchedule(String userId, Integer week, Integer day);
    /**
     * 自主学习1-听音选图
     *
     * @param week         周目
     * @param day          所属day
     * @param studyListens 数据集合
     * @return 单个题组下所包含的所有图片及语音
     */
    Map<String, Object> listenSelectPicture(Integer week, Integer day, List<TStudyListen> studyListens);
    /**
     * 自主游戏1-超级听力
     *
     * @param difficulty 难度(0入门、1中级、2高级)
     * @param week       所属周目
     * @param game       游戏信息
     */
    void checkDifficulty(Integer difficulty, Integer week, TGame game);
    /**
     * 自主学习2-看图选音
     *
     * @param week     周目
     * @param day      所属day
     * @param lookList 数据集合
     * @return 单个题组下所包含的所有图片及语音
     */
    Map<String, Object> pictureSelectVoice(Integer week, Integer day, List<TStudyLook> lookList);
    /**
     * 自主学习3-归纳排除
     *
     * @param week 周目
     * @param day  所属day
     * @param inductionList 归纳排除
     * @return 题目信息
     */
    Map<String, Object> induceExclude(Integer week, Integer day, List<TStudyInduction> inductionList);
    /**
     * 自主学习4-有问有答
     *
     * @param week 周目
     * @param day  所属day
     * @param answerList 有问有答
     * @return 题目信息
     */
    Map<String, Object> questionsAndAnswers(Integer week, Integer day, List<TStudyAnswer> answerList);
    /**
     * 自主学习5-音图相配
     *
     * @param week 周目
     * @param day  所属day
     * @param pair 音图相配
     * @return 题目信息
     */
    Map<String, Object> pictureMateVoice(Integer week, Integer day, TStudyPair pair);
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserService.java
@@ -20,4 +20,18 @@
    List<AppUserVO> listAll(AppUserQuery query);
    /**
     * 发送手机验证码
     *
     * @param phone 手机号信息
     * @return 验证码发送结果
     */
    Boolean phoneCode(String phone);
    /**
     * 判断当前登录用户是否为vip
     *
     * @return 判断结果
     */
    Boolean isVip();
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserStudyService.java
@@ -1,7 +1,6 @@
package com.ruoyi.study.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.study.domain.TGame;
import com.ruoyi.study.domain.TUserStudy;
/**
@@ -14,4 +13,20 @@
 */
public interface ITUserStudyService extends IService<TUserStudy> {
    /**
     * 获取学习进度及学习时长等信息
     *
     * @param userId 用户id
     * @param week   周目
     * @param day    所属day
     * @return 学习信息
     */
    TUserStudy studySchedule(String userId, Integer week, Integer day);
    /**
     * 根据条件清空用户学习时长
     *
     * @param time 今日/本周/本月
     */
    void resettingStudyRecord(String time);
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TGameRecordServiceImpl.java
@@ -1,13 +1,13 @@
package com.ruoyi.study.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.study.domain.TGame;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.study.domain.TGameRecord;
import com.ruoyi.study.mapper.TGameMapper;
import com.ruoyi.study.dto.CompleteGameDTO;
import com.ruoyi.study.mapper.TGameRecordMapper;
import com.ruoyi.study.service.ITGameRecordService;
import com.ruoyi.study.service.ITGameService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * <p>
@@ -20,4 +20,16 @@
@Service
public class TGameRecordServiceImpl extends ServiceImpl<TGameRecordMapper, TGameRecord> implements ITGameRecordService {
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(CompleteGameDTO gameAchievement) {
        TGameRecord data = new TGameRecord();
        data.setUserId(SecurityUtils.getUserId().intValue());
        data.setAccuracy(gameAchievement.getAccuracy());
        data.setGameName(gameAchievement.getGameName());
        data.setUseTime(gameAchievement.getUseTime());
        data.setGameId(gameAchievement.getGameId());
        data.setGameDifficulty(gameAchievement.getDifficulty());
        return this.save(data);
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TIntegralRecordServiceImpl.java
@@ -1,13 +1,14 @@
package com.ruoyi.study.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.study.domain.TGameRecord;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.study.domain.TIntegralRecord;
import com.ruoyi.study.mapper.TGameRecordMapper;
import com.ruoyi.study.mapper.TIntegralRecordMapper;
import com.ruoyi.study.service.ITGameRecordService;
import com.ruoyi.study.service.ITIntegralRecordService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * <p>
@@ -20,4 +21,18 @@
@Service
public class TIntegralRecordServiceImpl extends ServiceImpl<TIntegralRecordMapper, TIntegralRecord> implements ITIntegralRecordService {
    @Override
    public IPage<TIntegralRecord> integralDetail(Page<TIntegralRecord> page, Long userId, String time) {
        return baseMapper.integralDetail(userId, time, page);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(String integral, String method) {
        TIntegralRecord integralRecord = new TIntegralRecord();
        integralRecord.setIntegral(integral);
        integralRecord.setMethod(method);
        integralRecord.setUserId(SecurityUtils.getUserId().intValue());
        return this.save(integralRecord);
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TStudyServiceImpl.java
@@ -1,10 +1,17 @@
package com.ruoyi.study.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.study.domain.TStudy;
import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.study.domain.*;
import com.ruoyi.study.dto.StudyWeekDTO;
import com.ruoyi.study.mapper.TStudyMapper;
import com.ruoyi.study.service.ITStudyService;
import com.ruoyi.study.service.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -17,4 +24,113 @@
@Service
public class TStudyServiceImpl extends ServiceImpl<TStudyMapper, TStudy> implements ITStudyService {
    @Resource
    private ITUserStudyService userStudyService;
    @Resource
    private ITStudyListenService studyListenService;
    @Resource
    private ITSubjectService subjectService;
    @Resource
    private ITGameRecordService gameRecordService;
    private final static Map<String, Integer> GAME_DIFFICULTY_MAP = new HashMap<>();
    static {
        GAME_DIFFICULTY_MAP.put("1", 0);
        GAME_DIFFICULTY_MAP.put("2", 1);
    }
    @Override
    public List<StudyWeekDTO> weekList(Integer type, Integer quarter) {
        return baseMapper.weekList(type, quarter);
    }
    @Override
    public TUserStudy studySchedule(String userId, Integer week, Integer day) {
        return userStudyService.studySchedule(userId, week, day);
    }
    @Override
    public void checkDifficulty(Integer difficulty, Integer week, TGame game) {
        // 判断用户是否完成上一个等级
        Integer level = GAME_DIFFICULTY_MAP.get(String.valueOf(difficulty));
        if (null == level) {
            throw new GlobalException("游戏等级异常,请重试!");
        }
        // 获取用户游戏进度
        Long userId = SecurityUtils.getUserId();
        List<TGameRecord> list = gameRecordService.lambdaQuery().eq(TGameRecord::getUserId, userId).eq(TGameRecord::getGameId, game.getId()).list();
        boolean contains = list.stream().map(TGameRecord::getGameDifficulty).collect(Collectors.toList()).contains(level);
        if (!contains) {
            throw new GlobalException("请先完成上一难度再挑战");
        }
    }
    @Override
    public Map<String, Object> listenSelectPicture(Integer week, Integer day, List<TStudyListen> studyListens) {
        // 随机获取一组题
        Random rand = new Random();
        TStudyListen data = studyListens.get(rand.nextInt(studyListens.size()));
        List<TSubject> subjectList = getSubjects(data.getSubject().split(","));
        Map<String, Object> result = new HashMap<>(8);
        result.put("data", data);
        result.put("subject", subjectList);
        return result;
    }
    @Override
    public Map<String, Object> pictureSelectVoice(Integer week, Integer day, List<TStudyLook> lookList) {
        // 随机获取一组题
        Random rand = new Random();
        TStudyLook data = lookList.get(rand.nextInt(lookList.size()));
        List<TSubject> subjectList = getSubjects(data.getSubject().split(","));
        Map<String, Object> result = new HashMap<>(8);
        result.put("data", data);
        result.put("subject", subjectList);
        return result;
    }
    @Override
    public Map<String, Object> induceExclude(Integer week, Integer day, List<TStudyInduction> inductionList) {
        // 随机获取一组题
        Random rand = new Random();
        TStudyInduction data = inductionList.get(rand.nextInt(inductionList.size()));
        List<TSubject> subjectList = getSubjects(data.getSubject().split(","));
        Map<String, Object> result = new HashMap<>(8);
        result.put("data", data);
        result.put("subject", subjectList);
        return result;
    }
    @Override
    public Map<String, Object> questionsAndAnswers(Integer week, Integer day, List<TStudyAnswer> answerList) {
        // 随机获取一组题
        Random rand = new Random();
        TStudyAnswer data = answerList.get(rand.nextInt(answerList.size()));
        // 获取问题题目 和 回答题目
        Map<String, Object> result = new HashMap<>(8);
        result.put("data", data);
        // todo 有问有答
        return result;
    }
    @Override
    public Map<String, Object> pictureMateVoice(Integer week, Integer day, TStudyPair pair) {
        List<TSubject> subjectList = getSubjects(pair.getSubject().split(","));
        Map<String, Object> result = new HashMap<>(8);
        result.put("data", pair);
        result.put("subject", subjectList);
        return result;
    }
    /**
     * 根据参数id查询图片及语音
     *
     * @param ids 多个id
     * @return 图片及语音集合
     */
    private List<TSubject> getSubjects(String[] ids) {
        return subjectService.lambdaQuery().in(TSubject::getId, Arrays.asList(ids)).list();
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserServiceImpl.java
@@ -1,14 +1,23 @@
package com.ruoyi.study.service.impl;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.RedisConstants;
import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.study.domain.TUser;
import com.ruoyi.study.dto.AppUserQuery;
import com.ruoyi.study.mapper.TUserMapper;
import com.ruoyi.study.service.ITUserService;
import com.ruoyi.study.vo.AppUserVO;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * <p>
@@ -21,8 +30,60 @@
@Service
public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements ITUserService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private TokenService tokenService;
    @Override
    public List<AppUserVO> listAll(AppUserQuery query) {
        return this.baseMapper.listAll(query);
    }
    @Override
    public Boolean phoneCode(String phone) {
        // 生成随机 6位数字 验证码
        String phoneCode = RandomUtil.randomNumbers(6);
        // todo 手机验证码暂时 123456
        phoneCode = "123456";
        // 判断redis中是否存在手机验证码
        Object phoneCodeRedis = redisTemplate.opsForValue().get(RedisConstants.PHONE_CODE + phone);
        if (phoneCodeRedis == null) {
            // 将手机验证码 key: reg:用户输入的手机号码  value: 随机验证码:时间戳
            phoneCodeRedis = phoneCode;
        } else {
            // redis有验证码,获取redis中value的时间戳,判断是否过期
            long oldTime = Long.parseLong(String.valueOf(phoneCodeRedis).split(":")[1]);
            // 没有超过1分钟的重发时间
            if (System.currentTimeMillis() - oldTime < (long) Constants.SIXTY * Constants.ONE_THOUSAND) {
                throw new GlobalException("操作频繁,稍后重试!!!");
            } else {
                phoneCode = String.valueOf(phoneCodeRedis).split(":")[0];
            }
        }
        /*
         * 保存信息到redis
         * key为 --> phone_code:手机号码 (phone_code表示该业务为 验证码登录)
         * value为 --> 随机验证码:时间戳 (时间戳用于计算是否超过1分钟的重发时间)
         */
        redisTemplate.opsForValue().set(RedisConstants.PHONE_CODE + phone, phoneCode + ":" + System.currentTimeMillis(), 3, TimeUnit.MINUTES);
        String sendMessage = "验证码发送成功,您的验证码为:" + phoneCode + ",该验证码三分钟内有效,请及时完成登陆";
        // todo 发送此消息
        System.out.println(sendMessage);
        return true;
    }
    @Override
    public Boolean isVip() {
        TUser user = lambdaQuery().eq(TUser::getId, tokenService.getLoginUserStudy().getUserid())
                .eq(TUser::getDisabled, 0).one();
        // 是否为vip 逻辑
        if (null == user) {
            return false;
        }
        // vip过期时间,字段为空也表示 当前用户不是vip
        Date vipEndTime = user.getVipEndTime();
        return null != vipEndTime && System.currentTimeMillis() <= vipEndTime.getTime();
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserStudyServiceImpl.java
@@ -1,13 +1,14 @@
package com.ruoyi.study.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.study.domain.TGame;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.study.domain.TUserStudy;
import com.ruoyi.study.mapper.TGameMapper;
import com.ruoyi.study.mapper.TUserStudyMapper;
import com.ruoyi.study.service.ITGameService;
import com.ruoyi.study.service.ITUserStudyService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
 * <p>
@@ -20,4 +21,37 @@
@Service
public class TUserStudyServiceImpl extends ServiceImpl<TUserStudyMapper, TUserStudy> implements ITUserStudyService {
    @Override
    public TUserStudy studySchedule(String userId, Integer week, Integer day) {
        return lambdaQuery().eq(TUserStudy::getUserId, userId)
                .eq(TUserStudy::getDay, day).eq(TUserStudy::getWeek, week)
                .eq(TUserStudy::getDisabled, 0).one();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resettingStudyRecord(String time) {
        List<TUserStudy> list = lambdaQuery().eq(TUserStudy::getDisabled, 0).list();
        switch (time) {
            case Constants.DAY:
                list.forEach(data -> data.setTodayStudy(0));
                break;
            case Constants.WEEK:
                list.forEach(data -> data.setWeekStudy(0));
                break;
            case Constants.MONTH:
                list.forEach(data -> data.setMonthStudy(0));
                break;
            default:
        }
        int number = 0;
        boolean update = this.updateBatchById(list);
        while (!update) {
            if (number >= 3) {
                break;
            }
            update = this.updateBatchById(list);
            number++;
        }
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/task/StudyRecordTimedTask.java
New file
@@ -0,0 +1,44 @@
package com.ruoyi.study.task;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.study.service.ITUserStudyService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
 * @author HJL
 * @version 1.0
 * @since 2024-05-20 17:21
 */
@Component
public class StudyRecordTimedTask {
    @Resource
    private ITUserStudyService userStudyService;
    /**
     * 每日凌晨执行,重置学习记录中 今日学习时长
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void resettingTodayStudyRecord() {
        userStudyService.resettingStudyRecord(Constants.DAY);
    }
    /**
     * 每周周一执行,重置学习记录中 本周学习时长
     */
    @Scheduled(cron = "0 0 0 * * 1")
    public void resettingWeekStudyRecord() {
        userStudyService.resettingStudyRecord(Constants.WEEK);
    }
    /**
     * 每月一号执行,重置学习记录中 本月学习时长
     */
    @Scheduled(cron = "0 0 0 1 * *")
    public void resettingMonthStudyRecord() {
        userStudyService.resettingStudyRecord(Constants.MONTH);
    }
}
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/utils/SendMsgUtils.java
New file
@@ -0,0 +1,42 @@
package com.ruoyi.study.utils;
/**
 * 发送手机验证码工具类
 *
 * @author HJL
 * @version 1.0
 * @since 2024-05-17 14:31
 */
public class SendMsgUtils {
    public static final String UID = "EUhello";
    public static final String KEY = "6D05DE2FF1294C81CEF25E0862448D11";
    public static String sendMessage(String smsMob, String smsText) {
//        PostMethod post = new PostMethod("https://utf8api.smschinese.cn/");
//        try {
//            HttpClient client = new HttpClient();
//            post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");//在头文件中设置转码
//            NameValuePair[] data = {new NameValuePair("Uid", UID), new NameValuePair("Key", KEY), new NameValuePair("smsMob", smsMob), new NameValuePair("smsText", smsText)};
//            post.setRequestBody(data);
//            client.executeMethod(post);
//            Header[] headers = post.getResponseHeaders();
//            int statusCode = post.getStatusCode();
//            System.out.println("statusCode:" + statusCode); //HTTP状态码
//
//            for (Header h : headers) {
//                System.out.println(h.toString());
//            }
//            String result = new String(post.getResponseBodyAsString().getBytes("utf-8"));
//            System.out.println(result);  //打印返回消息状态
//            return result;
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            // 关流
//            post.releaseConnection();
//        }
        return null;
    }
}
ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TIntegralRecordMapper.xml
@@ -2,6 +2,16 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.study.mapper.TIntegralRecordMapper">
    <select id="integralDetail" resultType="com.ruoyi.study.domain.TIntegralRecord">
        SELECT *
        FROM t_integral_record
        <where>
            <if test="time != null and time != ''">
                DATE_FORMAT(createTime, '%Y-%m') = #{time}
            </if>
            <if test="userId != null and userId != ''">
                and userId = #{userId}
            </if>
        </where>
    </select>
</mapper>
ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TStudyMapper.xml
@@ -19,4 +19,21 @@
        id, week, day, listen, type, integral, sort, title
    </sql>
    <select id="weekList" resultType="com.ruoyi.study.dto.StudyWeekDTO">
        SELECT COALESCE(SUM(sa.integral), 0) + COALESCE(SUM(si.integral), 0) + COALESCE(SUM(st.integral), 0) +
               COALESCE(SUM(sk.integral), 0) + COALESCE(SUM(sp.integral), 0) + COALESCE(SUM(sl.integral), 0) +
               COALESCE(SUM(g.integral), 0) AS totalIntegral,
               s.week,
               s.title,
               s.quarter,s.id
        FROM t_study s
                 LEFT JOIN t_study_answer sa ON s.id = sa.studyId
                 LEFT JOIN t_study_induction si ON s.id = si.studyId
                 LEFT JOIN t_study_listen st ON s.id = st.studyId
                 LEFT JOIN t_study_look sk ON s.id = sk.studyId
                 LEFT JOIN t_study_pair sp ON s.id = sp.studyId
                 LEFT JOIN t_story_listen sl ON s.id = sl.studyId
                 LEFT JOIN t_game g ON s.id = g.studyId
        GROUP BY s.week, s.title, s.quarter,s.id;
    </select>
</mapper>