From 25743ef4cfb9da0be8d3e488c93f5429ffd02f8a Mon Sep 17 00:00:00 2001
From: 44323 <443237572@qq.com>
Date: 星期二, 21 五月 2024 14:21:25 +0800
Subject: [PATCH] Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/java/DolphinEnglish

---
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/TGoodsVO.java                                      |  233 ++++
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TIntegralRecordMapper.java                      |   13 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITGoodsService.java                            |   31 
 ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TStudyMapper.xml                                     |   17 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodQueryDTO.java                                  |   33 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITOrderService.java                            |    8 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodExchangeDTO.java                               |   38 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RecipientServiceImpl.java                 |   16 
 ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java                     |   52 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserStudyService.java                        |   17 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserStudyServiceImpl.java                |   40 
 ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java                                           |   65 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TOrderServiceImpl.java                    |    6 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITGameRecordService.java                       |    9 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserServiceImpl.java                     |   61 +
 ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java                         |   15 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TGoodsServiceImpl.java                    |   98 ++
 ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/factory/StudyFallbackFactory.java                  |   11 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/TGoodsController.java                       |  174 +++
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/request/RegisterPhoneRequest.java                      |   22 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserService.java                             |   14 
 ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/feignClient/GoodsClient.java                       |   16 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TGameRecordServiceImpl.java               |   18 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TGoodsMapper.java                               |   11 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/task/StudyRecordTimedTask.java                         |   44 
 ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java                                        |    1 
 ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java |   68 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/domain/Region.java                                     |   21 
 ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java           |   97 +
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRecipientService.java                         |    7 
 ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/factory/TManagementFallbackFactory.java  |    3 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TStudyMapper.java                               |   12 
 ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TIntegralRecordMapper.xml                            |   14 
 ruoyi-common/ruoyi-common-redis/pom.xml                                                                        |    7 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TGameRecord.java                                |   21 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TOrderMapper.java                               |    7 
 ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/feignClient/StudyClient.java                       |   69 +
 ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TOrderMapper.xml                                     |    3 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/utils/SendMsgUtils.java                                |   42 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TStudyServiceImpl.java                    |  120 ++
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITStudyService.java                            |   84 +
 ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java                |   10 
 ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TGoodsMapper.xml                                     |   49 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITIntegralRecordService.java                   |   21 
 ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TUserController.java              |   16 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/StudyWeekDTO.java                                  |   28 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteGameDTO.java                               |   50 +
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRegionService.java                            |    6 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RegionServiceImpl.java                    |   54 +
 ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TProtocolController.java          |   26 
 ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TPageController.java              |   26 
 ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/feignClient/ManagementClient.java        |    1 
 ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/RecipientMapper.xml                                  |    5 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteStudyDTO.java                              |   26 
 ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/RedisConstants.java                |   33 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TIntegralRecordServiceImpl.java           |   21 
 ruoyi-service/ruoyi-goods/pom.xml                                                                              |   12 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java                       |  509 +++++++++-
 ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/factory/GoodsFallbackFactory.java                  |    8 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java                        |  310 +++--
 ruoyi-service/ruoyi-study/pom.xml                                                                              |    6 
 ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TIntegralRecord.java                            |    9 
 ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/RedissonConfig.java                             |   30 
 63 files changed, 2,535 insertions(+), 359 deletions(-)

diff --git a/ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/factory/GoodsFallbackFactory.java b/ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/factory/GoodsFallbackFactory.java
index 6e59292..76f4cfd 100644
--- a/ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/factory/GoodsFallbackFactory.java
+++ b/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());
+            }
         };
     }
 }
diff --git a/ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/feignClient/GoodsClient.java b/ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/feignClient/GoodsClient.java
index 53a5168..e953e27 100644
--- a/ruoyi-api/ruoyi-api-goods/src/main/java/com/ruoyi/goods/api/feignClient/GoodsClient.java
+++ b/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);
+
 }
diff --git a/ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/factory/TManagementFallbackFactory.java b/ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/factory/TManagementFallbackFactory.java
index 82dc9c2..54bb73c 100644
--- a/ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/factory/TManagementFallbackFactory.java
+++ b/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());
             }
+
         };
     }
 }
diff --git a/ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/feignClient/ManagementClient.java b/ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/feignClient/ManagementClient.java
index 16601be..e56667b 100644
--- a/ruoyi-api/ruoyi-api-management/src/main/java/com/ruoyi/management/api/feignClient/ManagementClient.java
+++ b/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();
 
diff --git a/ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/factory/StudyFallbackFactory.java b/ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/factory/StudyFallbackFactory.java
index cffb7e4..7d60d21 100644
--- a/ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/factory/StudyFallbackFactory.java
+++ b/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());
+            }
         };
     }
 }
diff --git a/ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/feignClient/StudyClient.java b/ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/feignClient/StudyClient.java
index bfe4919..e73edc1 100644
--- a/ruoyi-api/ruoyi-api-study/src/main/java/com/ruoyi/study/api/feignClient/StudyClient.java
+++ b/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);
+
 }
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java
index d79784a..b9f650b 100644
--- a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java
+++ b/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)
     {
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
index 56f106e..22aa0ef 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
+++ b/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";
+
 }
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/RedisConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/RedisConstants.java
new file mode 100644
index 0000000..c01be78
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/RedisConstants.java
@@ -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";
+
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java
index e0e99f4..47a5658 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java
+++ b/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 身份信息
diff --git a/ruoyi-common/ruoyi-common-redis/pom.xml b/ruoyi-common/ruoyi-common-redis/pom.xml
index 97c986b..32b7f96 100644
--- a/ruoyi-common/ruoyi-common-redis/pom.xml
+++ b/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>
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java
index 6e1ec8a..6a8dc47 100644
--- a/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java
+++ b/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);
 
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java
index a9cb1b7..df981c5 100644
--- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java
+++ b/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());
+    }
+
 }
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
index 294696f..d114fd9 100644
--- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
+++ b/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;
             }
@@ -263,6 +330,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;
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java
index 101de63..8c757e8 100644
--- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java
+++ b/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;
     }
 }
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-goods/pom.xml b/ruoyi-service/ruoyi-goods/pom.xml
index e9a6b02..954e482 100644
--- a/ruoyi-service/ruoyi-goods/pom.xml
+++ b/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>
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/RedissonConfig.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/RedissonConfig.java
new file mode 100644
index 0000000..c9a8d86
--- /dev/null
+++ b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/RedissonConfig.java
@@ -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);
+    }
+
+
+}
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/TGoodsController.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/TGoodsController.java
index ac65535..a69a0d3 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/TGoodsController.java
+++ b/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));
+    }
+
 }
 
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/domain/Region.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/domain/Region.java
index cf79817..bfe3cc4 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/domain/Region.java
+++ b/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<>();
+
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodExchangeDTO.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodExchangeDTO.java
new file mode 100644
index 0000000..93a57ea
--- /dev/null
+++ b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodExchangeDTO.java
@@ -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;
+
+}
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodQueryDTO.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodQueryDTO.java
new file mode 100644
index 0000000..2f9f970
--- /dev/null
+++ b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/dto/GoodQueryDTO.java
@@ -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;
+
+}
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TGoodsMapper.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TGoodsMapper.java
index 7089925..bbc73ae 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TGoodsMapper.java
+++ b/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);
+
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TOrderMapper.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TOrderMapper.java
index c268be2..97c0b26 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/mapper/TOrderMapper.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRecipientService.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRecipientService.java
index bcc273d..08fc851 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRecipientService.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRegionService.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRegionService.java
index 8e32b7d..a0bc735 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/IRegionService.java
+++ b/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();
 
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITGoodsService.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITGoodsService.java
index f7ad029..07d8e3d 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITGoodsService.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITOrderService.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITOrderService.java
index 764bde4..e42ac3b 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/ITOrderService.java
+++ b/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);
+
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RecipientServiceImpl.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RecipientServiceImpl.java
index 1903ed7..7c50131 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RecipientServiceImpl.java
+++ b/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 ? "操作成功!" : "操作失败!";
+    }
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RegionServiceImpl.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RegionServiceImpl.java
index ba857bc..1432bb9 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/RegionServiceImpl.java
+++ b/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;
+    }
 
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TGoodsServiceImpl.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TGoodsServiceImpl.java
index 834cdda..f7658a7 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TGoodsServiceImpl.java
+++ b/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;
+    }
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TOrderServiceImpl.java b/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TOrderServiceImpl.java
index ba918ce..87020ec 100644
--- a/ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/service/impl/TOrderServiceImpl.java
+++ b/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);
+    }
+
 }
diff --git a/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/RecipientMapper.xml b/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/RecipientMapper.xml
index 5a4750d..601f8d8 100644
--- a/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/RecipientMapper.xml
+++ b/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>
diff --git a/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TGoodsMapper.xml b/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TGoodsMapper.xml
index b5874e8..33f079b 100644
--- a/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TGoodsMapper.xml
+++ b/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>
diff --git a/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TOrderMapper.xml b/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TOrderMapper.xml
index 0abf2ba..0abe702 100644
--- a/ruoyi-service/ruoyi-goods/src/main/resources/mapper/goods/TOrderMapper.xml
+++ b/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>
diff --git a/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TPageController.java b/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TPageController.java
index 037fd86..00d199b 100644
--- a/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TPageController.java
+++ b/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());
+    }
+
 }
 
diff --git a/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TProtocolController.java b/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TProtocolController.java
index a612baa..4191dbf 100644
--- a/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TProtocolController.java
+++ b/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());
+    }
+
 }
 
diff --git a/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TUserController.java b/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TUserController.java
index 3b9d9e4..0f1d1d3 100644
--- a/ruoyi-service/ruoyi-management/src/main/java/com/ruoyi/management/controller/TUserController.java
+++ b/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,17 +122,19 @@
         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() {
@@ -136,6 +142,7 @@
                 .orderByAsc("time"));
         return R.ok(list);
     }
+
     @PostMapping("/setVipSet")
     @ApiOperation(value = "保存会员设置", tags = {"用户管理"})
     public AjaxResult setVipSet(@RequestBody VipSetVO vo) {
@@ -147,6 +154,7 @@
         }
         return AjaxResult.success("保存成功");
     }
+
     @PostMapping("/vipOrderList")
     @ApiOperation(value = "列表查询", tags = {"会员管理"})
     public AjaxResult<PageInfo<VipOrderVO>> vipOrderList(AppUserQuery query) {
@@ -154,11 +162,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();
     }
+
 }
 
diff --git a/ruoyi-service/ruoyi-study/pom.xml b/ruoyi-service/ruoyi-study/pom.xml
index d096c5b..c779948 100644
--- a/ruoyi-service/ruoyi-study/pom.xml
+++ b/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>
+
 
 
 
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java
index c82ae08..3a3babb 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java
+++ b/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,6 +110,7 @@
         res.setTotal(0);
         return R.ok(res);
     }
+
     @PostMapping("/subjectList")
     @ApiOperation(value = "配置学习类型选择题目", tags = {"题目管理"})
     public R<PageInfo<TSubject>> subjectList(@RequestBody ChoiceSubject query) {
@@ -90,14 +119,14 @@
         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);
@@ -131,8 +160,10 @@
         res.setTotal(0);
         return R.ok(res);
     }
+
     /**
      * 添加学习配置
+     *
      * @param dto
      * @return
      */
@@ -146,12 +177,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());
@@ -161,7 +192,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());
@@ -174,12 +205,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();
@@ -194,7 +225,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();
@@ -211,12 +242,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();
@@ -230,8 +261,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();
@@ -245,8 +276,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();
@@ -259,8 +290,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();
@@ -276,8 +307,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();
@@ -295,6 +326,7 @@
 
     /**
      * 添加周目
+     *
      * @param dto
      * @return
      */
@@ -308,8 +340,10 @@
         studyService.save(tStudy);
         return R.ok();
     }
+
     /**
      * 学习类型列表查询
+     *
      * @return
      */
     @PostMapping("/getStudyList")
@@ -358,8 +392,10 @@
         res.add(studyListVO6);
         return R.ok(res);
     }
+
     /**
      * 通过类型、周目、day查询学习配置
+     *
      * @return
      */
     @PostMapping("/getStudySet")
@@ -401,7 +437,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();
@@ -426,16 +462,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(",");
                 }
@@ -452,12 +488,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());
@@ -472,7 +508,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();
@@ -528,30 +564,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);
@@ -563,5 +599,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));
+    }
+
 }
 
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java
index 9065f53..0f9bbac 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java
+++ b/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);
                 }
             }
@@ -98,6 +97,7 @@
         }
         return AjaxResult.ok(vipInfoVOS);
     }
+
     @Autowired
     private PayMoneyUtil payMoneyUtil;
 
@@ -108,16 +108,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();
             }
@@ -126,11 +126,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", "");
@@ -139,13 +139,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) {
@@ -157,9 +158,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();
                     }
                 }
@@ -174,7 +175,7 @@
 
 
                 TUser byId1 = userService.getById(byId.getUserId());
-                if (byId1.getVipPayTime() == null){
+                if (byId1.getVipPayTime() == null) {
                     // 是否是首次充值会员
                     byId1.setVipPayTime(new Date());
                     Calendar calendar = Calendar.getInstance();
@@ -183,16 +184,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);
@@ -216,6 +217,7 @@
             e.printStackTrace();
         }
     }
+
     @ResponseBody
     @PostMapping("/base/wxPayBuyVip")
     public void wxPayBuyPackage(HttpServletRequest request, HttpServletResponse response) {
@@ -230,9 +232,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();
                     }
                 }
@@ -247,7 +249,7 @@
 
 
                 TUser byId1 = userService.getById(byId.getUserId());
-                if (byId1.getVipPayTime() == null){
+                if (byId1.getVipPayTime() == null) {
                     // 是否是首次充值会员
                     byId1.setVipPayTime(new Date());
                     Calendar calendar = Calendar.getInstance();
@@ -256,16 +258,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);
@@ -287,59 +289,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();
@@ -348,11 +346,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 = {"家长端-个人中心"})
@@ -360,8 +426,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);
@@ -372,24 +438,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({
@@ -398,83 +465,36 @@
             @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.saveOrUpdate(byId);
         return AjaxResult.success("修改成功");
-    }
-    @ResponseBody
-    @PostMapping("/base/wxRefund")
-    public void cancelMySiteCallback(HttpServletRequest request, HttpServletResponse response) {
-        try {
-            Map<String, String> map = payMoneyUtil.wxRefundCallback(request);
-            if (null != map) {
-                String code = map.get("out_refund_no");
-                String refund_id = map.get("refund_id");
-                String result = map.get("result");
-                TVipOrder one = vipOrderService.getOne(new QueryWrapper<TVipOrder>()
-                        .eq("outTradeNo", code)
-                        .eq("payState", 3));
-                    one.setBackTime(new Date());
-                    vipOrderService.updateById(one);
-                    Integer userId = one.getUserId();
-                    TUser byId1 = userService.getById(userId);
-                    // 判断用户是不是第一次充值
-                    List<TVipOrder> list = vipOrderService.list(new QueryWrapper<TVipOrder>()
-                            .eq("userId", userId)
-                            .eq("payState", 2)
-                            .orderByDesc("createTime"));
-                    int size = list.size();
-                    if (size == 0){
-                        // 证明这是用户第一次充值会员 将首次充值会员时间和会员到期时间清空
-                        byId1.setVipEndTime(null);
-                        byId1.setVipPayTime(null);
-                        userService.updateById(byId1);
-                    }else{
-                        // 最近的一次充值会员时间
-                        TVipOrder tVipOrder = list.get(0);
-                        // 将会员到期时间回退到上一次
-                        byId1.setVipEndTime(tVipOrder.getTime());
-                        userService.updateById(byId1);
-                    }
-
-                PrintWriter out = response.getWriter();
-                out.write(result);
-                out.flush();
-                out.close();
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
     }
     @PostMapping("/vipBack/{id}")
     @ApiOperation(value = "会员退款", tags = {"管理后台-会员退款"})
@@ -488,8 +508,39 @@
             case 1:
                  // 微信退款
                 Map<String, String> stringStringMap = payMoneyUtil.wxRefund(transactionId, outTradeNo, byId.getMoney().toString(), byId.getMoney().toString(), "/base/wxRefund");
-                if (null == stringStringMap) {
-                    return R.fail("取消退款异常");
+                if (stringStringMap.get("code").equals("SUCCESS")){
+                    byId.setPayState(3);
+                    byId.setBackTime(new Date());
+                    vipOrderService.updateById(byId);
+                    // 用户的vip剩余时间减少
+                    Calendar calendar = Calendar.getInstance();
+                    calendar.setTime(new Date());
+                    calendar.add(Calendar.MONTH, -byId.getCount());
+                    Date dateAfterOneMonth = calendar.getTime();
+                    Integer userId = byId.getUserId();
+                    TUser byId1 = userService.getById(userId);
+                    // 判断用户是不是第一次充值
+                    List<TVipOrder> list = vipOrderService.list(new QueryWrapper<TVipOrder>()
+                            .eq("userId", userId)
+                            .eq("payState", 2)
+                            .orderByDesc("createTime"));
+                    int size = list.size();
+                    if (size == 0) {
+                        // 证明这是用户第一次充值会员 将首次充值会员时间和会员到期时间清空
+                        byId1.setVipEndTime(null);
+                        byId1.setVipPayTime(null);
+                        userService.updateById(byId1);
+                    } else {
+                        // 最近的一次充值会员时间
+                        TVipOrder tVipOrder = list.get(0);
+                        // 将会员到期时间回退到上一次
+                        byId1.setVipEndTime(tVipOrder.getTime());
+                        userService.updateById(byId1);
+                    }
+
+                    return R.ok();
+                }else{
+                    return R.fail(stringStringMap.get("msg"));
                 }
                 String result_code = stringStringMap.get("result_code");
                 if (!"SUCCESS".equals(result_code)) {
@@ -523,12 +574,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);
                         // 将会员到期时间回退到上一次
@@ -536,21 +587,25 @@
                         userService.updateById(byId1);
                     }
                     return R.ok();
+                } 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);
                 }
             }
@@ -574,16 +629,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());
@@ -605,15 +660,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("解冻成功");
@@ -625,7 +681,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);
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TGameRecord.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TGameRecord.java
index 36f321a..4b8493d 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TGameRecord.java
+++ b/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;
+
 
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TIntegralRecord.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TIntegralRecord.java
index c1989ef..42db5a1 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/domain/TIntegralRecord.java
+++ b/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("-") ? "减少" : "增加";
+    }
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteGameDTO.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteGameDTO.java
new file mode 100644
index 0000000..a1b554a
--- /dev/null
+++ b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteGameDTO.java
@@ -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;
+
+}
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteStudyDTO.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteStudyDTO.java
new file mode 100644
index 0000000..62dfa50
--- /dev/null
+++ b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/CompleteStudyDTO.java
@@ -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;
+
+}
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/StudyWeekDTO.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/StudyWeekDTO.java
new file mode 100644
index 0000000..3dd7944
--- /dev/null
+++ b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/StudyWeekDTO.java
@@ -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;
+
+}
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/TGoodsVO.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/TGoodsVO.java
new file mode 100644
index 0000000..1098e88
--- /dev/null
+++ b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/dto/TGoodsVO.java
@@ -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 +
+        "}";
+    }
+}
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TIntegralRecordMapper.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TIntegralRecordMapper.java
index ee66b7b..3be585d 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TIntegralRecordMapper.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TStudyMapper.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TStudyMapper.java
index 85d84fc..61a4f8a 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/mapper/TStudyMapper.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/request/RegisterPhoneRequest.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/request/RegisterPhoneRequest.java
new file mode 100644
index 0000000..793cda8
--- /dev/null
+++ b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/request/RegisterPhoneRequest.java
@@ -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;
+
+}
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITGameRecordService.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITGameRecordService.java
index 141ca9e..6935cd7 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITGameRecordService.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITIntegralRecordService.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITIntegralRecordService.java
index b2d4b7b..3b07b34 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITIntegralRecordService.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITStudyService.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITStudyService.java
index 66a8b27..81afdb0 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITStudyService.java
+++ b/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);
+
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserService.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserService.java
index b78562c..d001aef 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserService.java
+++ b/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();
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserStudyService.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserStudyService.java
index bb199e6..edfba55 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/ITUserStudyService.java
+++ b/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);
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TGameRecordServiceImpl.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TGameRecordServiceImpl.java
index adfd048..8d92964 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TGameRecordServiceImpl.java
+++ b/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);
+    }
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TIntegralRecordServiceImpl.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TIntegralRecordServiceImpl.java
index e2dd428..8e04157 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TIntegralRecordServiceImpl.java
+++ b/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);
+    }
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TStudyServiceImpl.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TStudyServiceImpl.java
index 5891770..c7c51cf 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TStudyServiceImpl.java
+++ b/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();
+    }
+
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserServiceImpl.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserServiceImpl.java
index 0fcce24..54ab466 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserServiceImpl.java
+++ b/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();
+    }
+
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserStudyServiceImpl.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserStudyServiceImpl.java
index f6019c3..0abadbb 100644
--- a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/service/impl/TUserStudyServiceImpl.java
+++ b/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++;
+        }
+    }
 }
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/task/StudyRecordTimedTask.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/task/StudyRecordTimedTask.java
new file mode 100644
index 0000000..9d01d99
--- /dev/null
+++ b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/task/StudyRecordTimedTask.java
@@ -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);
+    }
+
+}
diff --git a/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/utils/SendMsgUtils.java b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/utils/SendMsgUtils.java
new file mode 100644
index 0000000..407276e
--- /dev/null
+++ b/ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/utils/SendMsgUtils.java
@@ -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;
+    }
+
+}
diff --git a/ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TIntegralRecordMapper.xml b/ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TIntegralRecordMapper.xml
index c6d7f75..c5d91f4 100644
--- a/ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TIntegralRecordMapper.xml
+++ b/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>
diff --git a/ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TStudyMapper.xml b/ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TStudyMapper.xml
index c58b5c0..8767c40 100644
--- a/ruoyi-service/ruoyi-study/src/main/resources/mapper/sutdy/TStudyMapper.xml
+++ b/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>

--
Gitblit v1.7.1