Jelajahi Sumber

Merge remote-tracking branch 'origin/main'

classic_blue 4 hari lalu
induk
melakukan
3589b9a3ff
92 mengubah file dengan 2626 tambahan dan 162 penghapusan
  1. 15 0
      edu-travel-adapter/edu-travel-adapter-order/src/main/java/edu/travel/adapter/service/order/OrderAdapter.java
  2. 22 1
      edu-travel-adapter/edu-travel-adapter-upload/src/main/java/edu/travel/adapter/service/upload/UploadAdapter.java
  3. 5 0
      edu-travel-common/edu-travel-common-core/pom.xml
  4. 5 0
      edu-travel-common/edu-travel-common-datasource/pom.xml
  5. 42 4
      edu-travel-common/edu-travel-common-datasource/src/main/java/edu/travel/web/BaseController.java
  6. 26 0
      edu-travel-common/edu-travel-common-excel/pom.xml
  7. 1 0
      edu-travel-common/pom.xml
  8. 8 0
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/commodity/ShopReviewRemoteController.java
  9. 17 0
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/AddFavoriteDto.java
  10. 1 1
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/InsertProductSkuDto.java
  11. 19 0
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/PayShopOrderDto.java
  12. 74 0
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/ShopReviewDto.java
  13. 13 0
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/UpdateShopCarDto.java
  14. 1 1
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ProductImageVo.java
  15. 2 1
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ProductSkuVo.java
  16. 5 0
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ShopCartListVo.java
  17. 4 0
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ShopFavoriteVo.java
  18. 2 1
      edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ShopReviewVo.java
  19. 1 2
      edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/dto/GetOrderItemDto.java
  20. 106 0
      edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/dto/ShopOrderDto.java
  21. 3 3
      edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/order/ShopOrderItemRemoteController.java
  22. 14 1
      edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/order/ShopOrderRemoteController.java
  23. 106 0
      edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/vo/ShopOrderVo.java
  24. 1 1
      edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/vo/ShopSnapshotVo.java
  25. 26 0
      edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/vo/SpecValueVo.java
  26. 35 2
      edu-travel-remote/edu-travel-remote-tenant/src/main/java/edu/travel/remote/feign/mode/dto/tenant/BannerDto.java
  27. 14 2
      edu-travel-remote/edu-travel-remote-tenant/src/main/java/edu/travel/remote/feign/mode/vo/banner/BannerVo.java
  28. 6 0
      edu-travel-remote/edu-travel-remote-upload/pom.xml
  29. 9 1
      edu-travel-remote/edu-travel-remote-upload/src/main/java/edu/travel/remote/upload/UploadRemoteController.java
  30. 56 0
      edu-travel-remote/edu-travel-remote-upload/src/main/java/edu/travel/remote/upload/vo/EduFileBlobVo.java
  31. 50 0
      edu-travel-remote/edu-travel-remote-upload/src/main/java/edu/travel/remote/upload/vo/EduFileVo.java
  32. 15 1
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/constant/BaseConstant.java
  33. 0 6
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/entity/ShopProductImage.java
  34. 4 6
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/enums/OrderStateEnum.java
  35. 1 1
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/exception/GlobalExceptionHandler.java
  36. 2 0
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/ShopFavoriteService.java
  37. 4 0
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/ShopProductSkuService.java
  38. 7 0
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/ShopShoppingCartService.java
  39. 3 3
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopAddressServiceImpl.java
  40. 15 1
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopFavoriteServiceImpl.java
  41. 0 9
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopOpinionServiceImpl.java
  42. 62 11
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopProductSkuServiceImpl.java
  43. 8 0
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopProductSpecServiceImpl.java
  44. 17 2
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopReviewServiceImpl.java
  45. 30 2
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopShoppingCartServiceImpl.java
  46. 42 1
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopFavoriteController.java
  47. 53 0
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopOpinionController.java
  48. 9 5
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopProductController.java
  49. 13 0
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopProductSkuController.java
  50. 83 12
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopReviewController.java
  51. 27 0
      edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopShoppingCartController.java
  52. 1 2
      edu-travel-service/edu-travel-service-commodity/src/main/resources/mapper/ShopProductImageMapper.xml
  53. 60 0
      edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/controller/ShopOrderController.java
  54. 3 2
      edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/controller/ShopOrderItemController.java
  55. 4 6
      edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/enums/OrderStateEnum.java
  56. 4 0
      edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/service/ShopOrderService.java
  57. 52 0
      edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/service/impl/ShopOrderServiceImpl.java
  58. 6 0
      edu-travel-service/edu-travel-service-tenement/pom.xml
  59. 43 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/constant/BaseConstant.java
  60. 19 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/constant/RedisKey.java
  61. 13 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/entity/ShopBanner.java
  62. 8 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/mapper/ShopBannerMapper.java
  63. 7 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/service/ShopBannerService.java
  64. 257 13
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/service/impl/ShopBannerServiceImpl.java
  65. 17 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/FIleUtil.java
  66. 13 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/IdUtils.java
  67. 19 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/ObjectUtils.java
  68. 28 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/PageUtil.java
  69. 36 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/RedisUtil.java
  70. 26 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/StringUtil.java
  71. 16 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/TokenData.java
  72. 161 0
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/TreeUtils.java
  73. 51 12
      edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/web/ShopBannerController.java
  74. 87 28
      edu-travel-service/edu-travel-service-tenement/src/main/resources/mapper/ShopBannerMapper.xml
  75. 4 15
      edu-travel-service/edu-travel-service-tomcat/pom.xml
  76. 17 0
      edu-travel-service/edu-travel-service-tomcat/src/main/java/edu/travel/App.java
  77. 75 1
      edu-travel-service/edu-travel-service-upload/src/main/java/edu/travel/upload/web/UploadController.java
  78. 1 1
      edu-travel-service/edu-travel-service-upload/src/main/resources/bootstrap.yml
  79. 135 0
      edu-travel-service/edu-travel-service-ws/pom.xml
  80. 17 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/WsApplication.java
  81. 32 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/MyAccessDeniedHandler.java
  82. 31 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/MyAuthenticationEntryPoint.java
  83. 39 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/ResourceServerConfig.java
  84. 48 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/TokenConfig.java
  85. 21 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/WebSecurityConfig.java
  86. 23 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/WebServerFactoryConfigs.java
  87. 25 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/WebSocketAutoConfig.java
  88. 61 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/filter/TokenAuthenticationFilter.java
  89. 12 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/handle/TravelMessageHandler.java
  90. 62 0
      edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/interceptor/SystemMessageInterceptor.java
  91. 2 0
      edu-travel-service/pom.xml
  92. 6 1
      pom.xml

+ 15 - 0
edu-travel-adapter/edu-travel-adapter-order/src/main/java/edu/travel/adapter/service/order/OrderAdapter.java

@@ -3,6 +3,8 @@ package edu.travel.adapter.service.order;
 import edu.travel.adapter.annotation.AdapterAnnotation;
 import edu.travel.remote.dto.AddOrderDto;
 import edu.travel.remote.order.ShopOrderRemoteController;
+import edu.travel.remote.vo.ShopOrderVo;
+import edu.travel.remote.vo.ShowOrderInfoVo;
 import edu.travel.rpc.RPCBaseResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -15,4 +17,17 @@ public class OrderAdapter {
     public RPCBaseResponse<Void> addShopOrder(AddOrderDto params) {
        return shopOrderRemoteController.addShopOrder(params);
     }
+
+    @AdapterAnnotation
+    public RPCBaseResponse<ShowOrderInfoVo> orderInfoByOrderNum(String orderNum) {
+        return shopOrderRemoteController.orderInfoByOrderNum(orderNum);
+    }
+    @AdapterAnnotation
+    public RPCBaseResponse<Void> changeOrderState(String orderNum,String state) {
+        return shopOrderRemoteController.changeOrderState(orderNum,state);
+    }
+    @AdapterAnnotation
+    public RPCBaseResponse<ShopOrderVo> getFormId(String orderId) {
+        return shopOrderRemoteController.getFormId(orderId);
+    }
 }

+ 22 - 1
edu-travel-adapter/edu-travel-adapter-upload/src/main/java/edu/travel/adapter/service/upload/UploadAdapter.java

@@ -5,10 +5,11 @@ import edu.travel.adapter.annotation.AdapterAnnotation;
 import edu.travel.remote.upload.UploadRemoteController;
 import edu.travel.remote.upload.dto.EduFileBlobDTO;
 import edu.travel.remote.upload.dto.EduFileDTO;
+import edu.travel.remote.upload.vo.EduFileVo;
 import edu.travel.rpc.RPCBaseResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
-import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
@@ -48,4 +49,24 @@ public class UploadAdapter  {
         RPCBaseResponse rpcBaseResponse = uploadRemoteController.uploadFile(JSON.toJSONString(eduFileDTO),file);
         return rpcBaseResponse;
     }
+    @AdapterAnnotation
+    public RPCBaseResponse<EduFileVo> getFormId(@RequestParam("id") String id) {
+        return uploadRemoteController.getFormId(id);
+    }
+    @AdapterAnnotation
+    public RPCBaseResponse<EduFileVo> updateTargetFormId(EduFileDTO entity) {
+        return uploadRemoteController.updateTargetFormId(entity);
+    }
+    @AdapterAnnotation
+    public RPCBaseResponse<EduFileVo> saveFormTarget(EduFileDTO entity) {
+        return uploadRemoteController.saveFormTarget(entity);
+    }
+    @AdapterAnnotation
+    public RPCBaseResponse<EduFileVo> deleteTargetFormId(List<String> ids) {
+        return uploadRemoteController.deleteTargetFormId(ids);
+    }
+    @AdapterAnnotation
+    public RPCBaseResponse<List<EduFileVo>> getAllForm() {
+        return uploadRemoteController.getAllForm();
+    }
 }

+ 5 - 0
edu-travel-common/edu-travel-common-core/pom.xml

@@ -19,6 +19,11 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
             <scope>provided</scope>

+ 5 - 0
edu-travel-common/edu-travel-common-datasource/pom.xml

@@ -25,6 +25,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-common-excel</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <scope>provided</scope>

+ 42 - 4
edu-travel-common/edu-travel-common-datasource/src/main/java/edu/travel/web/BaseController.java

@@ -1,5 +1,8 @@
 package edu.travel.web;
 
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.read.listener.ReadListener;
 import com.baomidou.mybatisplus.extension.service.IService;
 import edu.travel.interfaces.InsertGroups;
 import edu.travel.interfaces.UpdateGroups;
@@ -7,6 +10,8 @@ import edu.travel.resp.BaseResponse;
 import edu.travel.resp.PageResponse;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.rpc.RPCPageResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.http.HttpStatus;
@@ -14,16 +19,49 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.Errors;
 import org.springframework.validation.ObjectError;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-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 org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.List;
 
 public class BaseController<T> {
+    Logger logger = LoggerFactory.getLogger(BaseController.class);
     @Autowired
     private IService<T> service;
+    @GetMapping("/download")
+    public void download(T clazz,HttpServletResponse response) throws IOException {
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        String fileName = URLEncoder.encode("导出数据.xlsx", "UTF-8").replaceAll("\\+", "%20");
+        response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
+        List<T> list = service.list();
+        EasyExcel.write(response.getOutputStream(), clazz.getClass())
+                .sheet("数据")
+                .doWrite(list);
+    }
+    @PostMapping("/upload/excel")
+    public RPCBaseResponse uploadExcel(@RequestBody T clazz ,@RequestPart("file") MultipartFile file) throws IOException {
+        EasyExcel.read(file.getInputStream(),clazz.getClass(),new ReadListener<T>(){
+
+            @Override
+            public void invoke(T t, AnalysisContext analysisContext) {
+                saveTarget(t);
+            }
+
+            @Override
+            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+                logger.debug("读取完成");
+            }
+        })
+                .sheet()
+                .doRead();
+        return RPCBaseResponse.success();
+    }
+
     @GetMapping("/getById")
     public RPCBaseResponse<T> getId(String id) {
         T byId = service.getById(id);

+ 26 - 0
edu-travel-common/edu-travel-common-excel/pom.xml

@@ -0,0 +1,26 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>edu.travel</groupId>
+        <artifactId>edu-travel-common</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>edu-travel-common-excel</artifactId>
+    <packaging>jar</packaging>
+
+    <name>edu-travel-common-excel</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 1 - 0
edu-travel-common/pom.xml

@@ -28,6 +28,7 @@
         <module>edu-travel-common-adapter</module>
         <module>edu-travel-kafka</module>
         <module>edu-travel-common-guava</module>
+        <module>edu-travel-common-excel</module>
     </modules>
 
     <properties>

+ 8 - 0
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/commodity/ShopReviewRemoteController.java

@@ -0,0 +1,8 @@
+package edu.travel.remote.commodity;
+
+import edu.travel.remote.base.RemoteBaseController;
+import edu.travel.remote.dto.ShopReviewDto;
+import edu.travel.remote.vo.ShopReviewVo;
+
+public interface ShopReviewRemoteController extends RemoteBaseController<ShopReviewVo, ShopReviewDto> {
+}

+ 17 - 0
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/AddFavoriteDto.java

@@ -0,0 +1,17 @@
+package edu.travel.remote.dto;
+
+import lombok.Data;
+
+@Data
+public class AddFavoriteDto {
+    /**
+     * 商品id
+     */
+    private String productId;
+
+    /**
+     * 国家
+     */
+    private String countryId;
+
+}

+ 1 - 1
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/InsertProductSkuDto.java

@@ -42,7 +42,7 @@ public class InsertProductSkuDto {
     /**
      * 商品SKU图片
      */
-    private List<String> imageUrl;
+    private List<FIleDto> imageUrl;
     /**
      * 销量
      */

+ 19 - 0
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/PayShopOrderDto.java

@@ -0,0 +1,19 @@
+package edu.travel.remote.dto;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class PayShopOrderDto {
+    /**
+     * 订单号
+     */
+    private String orderNum;
+
+    /**
+     * 支付时间
+     */
+    private Date payTime;
+
+}

+ 74 - 0
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/ShopReviewDto.java

@@ -0,0 +1,74 @@
+package edu.travel.remote.dto;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import edu.travel.entity.BaseEntity;
+import edu.travel.po.PagePO;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 商品评论表
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName(value = "shop_review")
+public class ShopReviewDto extends PagePO {
+    /**
+     * 商品评论表ID
+     */
+    private Long id;
+
+    /**
+     * 父级ID、0为一级评论
+     */
+    private Long parentId;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 商品ID
+     */
+    private Long productId;
+    /**
+     * 订单号
+     */
+    private Long orderId;
+    /**
+     * SKU
+     */
+    private Long skuId;
+
+    /**
+     * 评级
+     */
+    private Integer rating;
+
+    /**
+     * 评论内容
+     */
+    private String content;
+
+    /**
+     * 评论图片
+     */
+    private String image;
+
+    /**
+     * 是否置顶
+     */
+    private String isTop;
+
+    /**
+     * sku详情
+     */
+    private String skuDetails;
+
+}

+ 13 - 0
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/dto/UpdateShopCarDto.java

@@ -0,0 +1,13 @@
+package edu.travel.remote.dto;
+
+import lombok.Data;
+
+@Data
+public class UpdateShopCarDto {
+    //商品ID
+    private String id;
+    //商品SKU
+    private String skuId;
+    //商品数量
+    private Integer query;
+}

+ 1 - 1
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ProductImageVo.java

@@ -22,7 +22,7 @@ public class ProductImageVo {
     /**
      * 判断图片/视频,默认0,0图片,1视频
      */
-    private Integer urlType;
+    private String urlType;
 
     /**是否默认列表图片*/
     private Integer isDefault;

+ 2 - 1
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ProductSkuVo.java

@@ -1,5 +1,6 @@
 package edu.travel.remote.vo;
 
+import edu.travel.remote.dto.FIleDto;
 import lombok.Data;
 
 import java.math.BigDecimal;
@@ -53,7 +54,7 @@ public class ProductSkuVo {
     /**
      * 商品SKU图片
      */
-    private List<ProductImageVo> imageUrl;
+    private List<FIleDto> imageUrls;
     /**
      * 销量
      */

+ 5 - 0
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ShopCartListVo.java

@@ -64,4 +64,9 @@ public class ShopCartListVo {
      */
     private List<SpecValueVo> specValue;
 
+    /**
+     * state  0 上架 1 下架
+     */
+    private String state;
+
 }

+ 4 - 0
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ShopFavoriteVo.java

@@ -6,6 +6,10 @@ import java.math.BigDecimal;
 @Data
 public class ShopFavoriteVo {
     /**
+     * 收藏表ID
+     */
+    private String id;
+    /**
      * 商品ID
      */
     private String productId;

+ 2 - 1
edu-travel-remote/edu-travel-remote-commodity/src/main/java/edu/travel/remote/vo/ShopReviewVo.java

@@ -1,11 +1,12 @@
 package edu.travel.remote.vo;
 
+import edu.travel.entity.BaseEntity;
 import lombok.Data;
 
 import java.util.List;
 
 @Data
-public class ShopReviewVo {
+public class ShopReviewVo extends BaseEntity {
     /**
      * 商品评论表ID
      */

+ 1 - 2
edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/dto/GetOrderItemDto.java

@@ -2,10 +2,9 @@ package edu.travel.remote.dto;
 
 import lombok.Data;
 
-import java.io.Serializable;
 
 @Data
-public class GetOrderItemDto implements Serializable {
+public class GetOrderItemDto{
 
     private String orderId;
 

+ 106 - 0
edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/dto/ShopOrderDto.java

@@ -0,0 +1,106 @@
+package edu.travel.remote.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class ShopOrderDto {
+    /**
+     * 订单表ID
+     */
+    private String id;
+
+    /**
+     * 仓库ID
+     */
+    private Long warehouseId;
+
+    /**
+     * 订单备注
+     */
+    private String orderRemark;
+
+    /**
+     * 国家id
+     */
+    private String countryId;
+
+    /**
+     * 服务费id
+     */
+    private String serviceChargeId;
+
+    /**
+     * 服务费比率
+     */
+    private BigDecimal serviceChargeValue;
+
+    /**
+     * 支付方式id
+     */
+    private String payId;
+
+    /**
+     * 支付方法值
+     */
+    private String payWayValue;
+
+    /**
+     * 用户ID
+     */
+    private String userId;
+
+    /**
+     * 收货地址
+     */
+    private String addressId;
+
+    /**
+     * 收货地址
+     */
+    private String address;
+    /**
+     * 订单号
+     */
+    private String orderNumber;
+
+    /**
+     * 累计金额
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 货币
+     */
+    private String currencyId;
+    /**
+     * 货币
+     */
+    private String currency;
+    /**
+     * 汇率
+     */
+    private BigDecimal exchangeRate;
+
+    /**
+     * 0待付款, 1已下单, 2待发货,3待收货,4待评价,5已完成,6已取消
+     */
+    private Integer status;
+
+    /**
+     * 支付时间
+     */
+    private Date paymentTime;
+
+    /**
+     * 优惠券抵扣金额
+     */
+    private BigDecimal couponDiscount;
+
+    /**
+     * 平台优惠券ID
+     */
+    private String globalCouponId;
+}

+ 3 - 3
edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/order/ShopOrderItemRemoteController.java

@@ -7,7 +7,7 @@ import edu.travel.rpc.RPCBaseResponse;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestBody;
 
 import java.util.List;
 
@@ -19,8 +19,8 @@ public interface ShopOrderItemRemoteController {
      * @param params
      * @return {@link RPCBaseResponse }<{@link OrderItemVo }>
      */
-    @GetMapping("/getOrderItemVo")
-    public RPCBaseResponse<OrderItemVo> getOrderItemVo(@RequestParam("params") GetOrderItemDto params);
+    @PostMapping("/getOrderItemVo")
+    public RPCBaseResponse<OrderItemVo> getOrderItemVo(@RequestBody GetOrderItemDto params);
 
     /**
      *  新增订单商品信息

+ 14 - 1
edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/order/ShopOrderRemoteController.java

@@ -1,12 +1,25 @@
 package edu.travel.remote.order;
 
+import edu.travel.remote.base.RemoteBaseController;
 import edu.travel.remote.dto.AddOrderDto;
+import edu.travel.remote.dto.ShopOrderDto;
+import edu.travel.remote.vo.ShopOrderVo;
+import edu.travel.remote.vo.ShowOrderInfoVo;
 import edu.travel.rpc.RPCBaseResponse;
 import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 
 @FeignClient(name = "order-dev",path = "/shopOrder")
-public interface ShopOrderRemoteController {
+public interface ShopOrderRemoteController extends RemoteBaseController<ShopOrderVo,ShopOrderDto> {
     @PostMapping("/addShopOrder")
     RPCBaseResponse<Void> addShopOrder(AddOrderDto params);
+
+    @GetMapping("/orderInfoByOrderNum")
+    public RPCBaseResponse<ShowOrderInfoVo> orderInfoByOrderNum(@RequestParam("orderNum") String orderNum);
+
+    @GetMapping("/changeOrderState")
+    public RPCBaseResponse<Void> changeOrderState(@RequestParam("orderNum") String orderNum,@RequestParam("state") String state);
+
 }

+ 106 - 0
edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/vo/ShopOrderVo.java

@@ -0,0 +1,106 @@
+package edu.travel.remote.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class ShopOrderVo {
+    /**
+     * 订单表ID
+     */
+    private String id;
+
+    /**
+     * 仓库ID
+     */
+    private String warehouseId;
+
+    /**
+     * 订单备注
+     */
+    private String orderRemark;
+
+    /**
+     * 国家id
+     */
+    private String countryId;
+
+    /**
+     * 服务费id
+     */
+    private String serviceChargeId;
+
+    /**
+     * 服务费比率
+     */
+    private BigDecimal serviceChargeValue;
+
+    /**
+     * 支付方式id
+     */
+    private String payId;
+
+    /**
+     * 支付方法值
+     */
+    private String payWayValue;
+
+    /**
+     * 用户ID
+     */
+    private String userId;
+
+    /**
+     * 收货地址
+     */
+    private String addressId;
+
+    /**
+     * 收货地址
+     */
+    private String address;
+    /**
+     * 订单号
+     */
+    private String orderNumber;
+
+    /**
+     * 累计金额
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 货币
+     */
+    private String currencyId;
+    /**
+     * 货币
+     */
+    private String currency;
+    /**
+     * 汇率
+     */
+    private BigDecimal exchangeRate;
+
+    /**
+     * 0待付款, 1已下单, 2待发货,3待收货,4待评价,5已完成,6已取消
+     */
+    private Integer status;
+
+    /**
+     * 支付时间
+     */
+    private Date paymentTime;
+
+    /**
+     * 优惠券抵扣金额
+     */
+    private BigDecimal couponDiscount;
+
+    /**
+     * 平台优惠券ID
+     */
+    private String globalCouponId;
+}

+ 1 - 1
edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/vo/ShopSnapshotVo.java

@@ -35,6 +35,6 @@ public class ShopSnapshotVo {
     /**
      * 商品快照规格 SpecValueVo
      */
-    private List snapshotSpec;
+    private List<SpecValueVo> snapshotSpec;
 
 }

+ 26 - 0
edu-travel-remote/edu-travel-remote-order/src/main/java/edu/travel/remote/vo/SpecValueVo.java

@@ -0,0 +1,26 @@
+package edu.travel.remote.vo;
+
+import lombok.Data;
+
+/**
+ * 类功能描述:
+ *     购物车商品规格值
+ * @author 大春
+ * @date 2025/02/28
+ */
+
+@Data
+public class SpecValueVo {
+    /**
+     * 规格值id
+     */
+    private String specValueId;
+    /**规格值*/
+    private String specValue;
+    /**
+     * 规格值id
+     */
+    private String specId;
+    /**规格名称*/
+    private String specName;
+}

+ 35 - 2
edu-travel-remote/edu-travel-remote-tenant/src/main/java/edu/travel/remote/feign/mode/dto/tenant/BannerDto.java

@@ -1,9 +1,12 @@
 package edu.travel.remote.feign.mode.dto.tenant;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
+import edu.travel.po.PagePO;
 import lombok.Data;
 
+import java.util.Date;
 @Data
-public class BannerDto {
+public class BannerDto extends PagePO {
     /**
      * 轮播图ID
      */
@@ -13,7 +16,10 @@ public class BannerDto {
      * 图片/视频
      */
     private String url;
-
+    /**
+     * 图片id(上传方法返回)
+     */
+    private String imageId;
     /**
      * 判断图片/视频,默认0,0图片,1视频
      */
@@ -36,5 +42,32 @@ public class BannerDto {
      * mode 0 首页轮播图
      */
     private String mode;
+    /**
+     * 是否默认 0 否 1是
+     */
+    private Integer isDefault;
+    /**
+     * 是否启用
+     */
+    private String enable;
+    /**
+     * 开始时间
+     */
+    private Date startTime;
+    /**
+     * 结束时间
+     */
+    private Date endTime;
+    /**
+     * 创建时间
+     */
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+    /**
+     * 更新时间
+     */
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
 
 }

+ 14 - 2
edu-travel-remote/edu-travel-remote-tenant/src/main/java/edu/travel/remote/feign/mode/vo/banner/BannerVo.java

@@ -2,6 +2,8 @@ package edu.travel.remote.feign.mode.vo.banner;
 
 import lombok.Data;
 
+import java.util.Map;
+
 @Data
 public class BannerVo {
     /**
@@ -27,10 +29,20 @@ public class BannerVo {
      * 国家
      */
     private String countryId;
-
+    /**
+     * 模块
+     */
+    private String mode;
     /**
      * 链接地址
      */
     private String linkUrl;
-
+    /**
+     * 是否默认
+     */
+    private Integer isDefault;
+    /**
+     * map
+     */
+    private Map<String, Object> map;
 }

+ 6 - 0
edu-travel-remote/edu-travel-remote-upload/pom.xml

@@ -28,5 +28,11 @@
             <artifactId>edu-travel-common-openfeign</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-remote-base</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>

+ 9 - 1
edu-travel-remote/edu-travel-remote-upload/src/main/java/edu/travel/remote/upload/UploadRemoteController.java

@@ -1,7 +1,9 @@
 package edu.travel.remote.upload;
 
+import edu.travel.remote.base.RemoteBaseController;
 import edu.travel.remote.upload.dto.EduFileBlobDTO;
 import edu.travel.remote.upload.dto.EduFileDTO;
+import edu.travel.remote.upload.vo.EduFileVo;
 import edu.travel.rpc.RPCBaseResponse;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.http.MediaType;
@@ -13,7 +15,7 @@ import java.io.Serializable;
 import java.util.List;
 
 @FeignClient(name = "upload-dev",path = "/upload")
-public interface UploadRemoteController {
+public interface UploadRemoteController extends RemoteBaseController<EduFileVo, EduFileDTO> {
     @PostMapping("/init")
     public RPCBaseResponse<String> initializeUpload();
     /**
@@ -55,4 +57,10 @@ public interface UploadRemoteController {
      */
     @PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
     RPCBaseResponse uploadFile(@RequestParam("eduFileDTO") String dto,@RequestPart("file") MultipartFile file) throws IOException;
+
+    /**
+     * 通过id获取文件
+     */
+    @GetMapping("/getFormId")
+    RPCBaseResponse<EduFileVo> getFormId(@RequestParam("id")String id);
 }

+ 56 - 0
edu-travel-remote/edu-travel-remote-upload/src/main/java/edu/travel/remote/upload/vo/EduFileBlobVo.java

@@ -0,0 +1,56 @@
+package edu.travel.remote.upload.vo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import edu.travel.entity.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName(value = "edu.edu_file_blob")
+public class EduFileBlobVo extends BaseEntity {
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 分片名字
+     */
+    @TableField(value = "blob_name")
+    private String blobName;
+
+    /**
+     * 传了第几片
+     */
+    @TableField(value = "blob_num")
+    private Integer blobNum;
+
+    /**
+     * 分片地址
+     */
+    @TableField(value = "blob_path")
+    private String blobPath;
+
+    /**
+     * 分片md5
+     */
+    @TableField(value = "blob_md5")
+    private String blobMd5;
+
+    /**
+     * 文件md5
+     */
+    @TableField(value = "file_md5")
+    private String fileMd5;
+
+    /**
+     * 总共多少片
+     */
+    @TableField(value = "total_count")
+    private Integer totalCount;
+
+}

+ 50 - 0
edu-travel-remote/edu-travel-remote-upload/src/main/java/edu/travel/remote/upload/vo/EduFileVo.java

@@ -0,0 +1,50 @@
+package edu.travel.remote.upload.vo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import edu.travel.entity.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName(value = "edu.edu_file")
+public class EduFileVo extends BaseEntity {
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 文件md5
+     */
+    @TableField(value = "file_md5")
+    private String fileMd5;
+
+    /**
+     * 文件名
+     */
+    @TableField(value = "file_name")
+    private String fileName;
+
+    /**
+     * 文件系统名
+     */
+    @TableField(value = "file_sys_name")
+    private String fileSysName;
+
+    /**
+     * 文件路径
+     */
+    @TableField(value = "file_path")
+    private String filePath;
+
+    /**
+     * 文件类型
+     */
+    @TableField(value = "file_type")
+    private String fileType;
+
+}

+ 15 - 1
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/constant/BaseConstant.java

@@ -30,8 +30,22 @@ public class BaseConstant {
     private static final Set<String> PRODUCT_IMAGE_TYPES = new HashSet<>(
             Arrays.asList("jpg", "jpeg", "png")
     );
-
+    /**
+     * 意见文件类型
+     */
+    private static final Set<String> OPINION_FILE_TYPES = new HashSet<>(
+            Arrays.asList("jpg", "jpeg", "png","mp4")
+    );
     public static Set<String> getProductImageTypes() {
         return PRODUCT_IMAGE_TYPES;
     }
+    /**
+     * 获取banner 图片类型
+     */
+    public static Set<String> getBannerImageTypes() {
+        return PRODUCT_IMAGE_TYPES;
+    }
+    public static Set<String> getOpinionFileTypes() {
+        return OPINION_FILE_TYPES;
+    }
 }

+ 0 - 6
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/entity/ShopProductImage.java

@@ -26,12 +26,6 @@ public class ShopProductImage extends BaseEntity {
     private Long id;
 
     /**
-     * sku
-     */
-    @TableField(value = "sku_id")
-    private Long skuId;
-
-    /**
      * 商品ID
      */
     @TableField(value = "product_id")

+ 4 - 6
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/enums/OrderStateEnum.java

@@ -11,12 +11,10 @@ import lombok.Getter;
 @Getter
 public enum OrderStateEnum {
     WAITING_FOR_PAYMENT(0, "待付款"),
-    ORDERED(1, "已下单"),
-    AWAITING_SHIPMENT(2, "待发货"),
-    AWAITING_RECEIPT(3, "待收货"),
-    AWAITING_EVALUATION(4, "待评价"),
-    COMPLETED(5, "已完成"),
-    CANCELLED(6, "已取消");
+    ORDERED(1, "已下单,待发货"),
+    AWAITING_RECEIPT(2, "待收货"),
+    AWAITING_EVALUATION(3, "已完成"),
+    CANCELLED(4, "已取消");
 
     private final int state;
     private final String name;

+ 1 - 1
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/exception/GlobalExceptionHandler.java

@@ -24,7 +24,7 @@ public class GlobalExceptionHandler {
     @ExceptionHandler(BaseException.class)
     public BaseResponse<Object> handleUserInfoException(BaseException e){
         e.printStackTrace();
-        return  PageResponse.out(e.getCode(),e.getMessage(),null);
+        return new BaseResponse<>(e.getCode(),e.getMessage(),null);
     }
 
     @ExceptionHandler(IllegalArgumentException.class)

+ 2 - 0
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/ShopFavoriteService.java

@@ -3,6 +3,7 @@ package edu.travel.commodity.service;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import edu.travel.commodity.entity.ShopFavorite;
 import com.baomidou.mybatisplus.extension.service.IService;
+import edu.travel.remote.dto.AddFavoriteDto;
 import edu.travel.remote.dto.BaseDto;
 import edu.travel.remote.vo.ShopFavoriteVo;
 
@@ -11,4 +12,5 @@ public interface ShopFavoriteService extends IService<ShopFavorite>{
 
     Page<ShopFavoriteVo> getFavorite(BaseDto baseDto);
 
+    void addFavorite(AddFavoriteDto params);
 }

+ 4 - 0
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/ShopProductSkuService.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import edu.travel.remote.dto.AddShopOrderDto;
 import edu.travel.remote.dto.InsertProductSkuDto;
 import edu.travel.remote.dto.InsertProductSpecDto;
+import edu.travel.remote.dto.PayShopOrderDto;
 import edu.travel.remote.vo.ShopSkuSpecValueVo;
 
 import java.util.List;
@@ -20,4 +21,7 @@ public interface ShopProductSkuService extends IService<ShopProductSku>{
     void shopProductSku(AddShopOrderDto params);
 
     boolean insertProductSku(List<InsertProductSpecDto> specs,List<InsertProductSkuDto> skus, ShopProduct id);
+
+    void payProductSku(PayShopOrderDto payShopOrderDto);
+
 }

+ 7 - 0
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/ShopShoppingCartService.java

@@ -5,12 +5,19 @@ import edu.travel.commodity.entity.ShopShoppingCart;
 import com.baomidou.mybatisplus.extension.service.IService;
 import edu.travel.remote.dto.AddShopCartDto;
 import edu.travel.remote.dto.BaseDto;
+import edu.travel.remote.dto.UpdateShopCarDto;
 import edu.travel.remote.vo.ShopCartListVo;
 
+import java.util.Set;
+
 public interface ShopShoppingCartService extends IService<ShopShoppingCart>{
 
 
     Boolean addShopCart(AddShopCartDto params);
 
     Page<ShopCartListVo> getShopCartList(BaseDto params);
+
+    Boolean updateCart(UpdateShopCarDto params);
+
+    Integer delCart(Set<String> ids);
 }

+ 3 - 3
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopAddressServiceImpl.java

@@ -24,10 +24,10 @@ public class ShopAddressServiceImpl extends ServiceImpl<ShopAddressMapper, ShopA
     @Override
     public Page<ShopAddressVo> getShopAddress(BaseDto dto) {
         //当前登录对象
-        Page<ShopAddress> page = lambdaQuery().eq(ShopAddress::getCountryId, dto.getCountryId())
+        LambdaQueryWrapper<ShopAddress> query = Wrappers.<ShopAddress>lambdaQuery().eq(ShopAddress::getCountryId, dto.getCountryId())
                 .eq(ShopAddress::getUserId, TokenData.getUserId())
-                .orderByAsc(ShopAddress::getIsDefault)
-                .page(new Page<ShopAddress>(dto.getCurrentPage(), dto.getPageSize()));
+                .orderByAsc(ShopAddress::getIsDefault);
+        Page<ShopAddress> page = page(new Page<ShopAddress>(dto.getCurrentPage(), dto.getPageSize()), query);
         //page对象转换.
         return PageUtil.toPageEntity(page, ShopAddressVo.class);
     }

+ 15 - 1
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopFavoriteServiceImpl.java

@@ -11,6 +11,8 @@ import edu.travel.commodity.service.ShopFavoriteService;
 import edu.travel.commodity.service.ShopProductService;
 import edu.travel.commodity.utils.PageUtil;
 import edu.travel.commodity.utils.TokenData;
+import edu.travel.exception.BaseException;
+import edu.travel.remote.dto.AddFavoriteDto;
 import edu.travel.remote.dto.BaseDto;
 import edu.travel.service.SysServiceImpl;
 import edu.travel.remote.vo.ShopFavoriteVo;
@@ -54,7 +56,7 @@ public class ShopFavoriteServiceImpl extends SysServiceImpl<ShopFavoriteMapper,
             if(!ObjectUtil.isEmpty(shopProduct)){
                 shopProductVo.setProductName(shopProduct.getProductName());
                 shopProductVo.setDescription(shopProduct.getDescription());
-                shopProductVo.setMainImageUrl(shopProduct.getDescription());
+                shopProductVo.setMainImageUrl(shopProduct.getMainImageUrl());
                 shopProductVo.setHeatValue(shopProduct.getHeatValue());
                 shopProductVo.setSalesVolume(shopProduct.getSalesVolume());
                 shopProductVo.setDefaultPrice(shopProduct.getDefaultPrice());
@@ -64,4 +66,16 @@ public class ShopFavoriteServiceImpl extends SysServiceImpl<ShopFavoriteMapper,
         }
         return pageEntity;
     }
+
+    @Override
+    public void addFavorite(AddFavoriteDto params) {
+        ShopProduct product = shopProductService.getById(params.getProductId());
+        if(ObjectUtil.isEmpty(product) ||!product.getCountryId().toString().equals(params.getCountryId())){
+            throw new BaseException("商品不存在");
+        }
+        ShopFavorite shopFavorite = new ShopFavorite();
+        shopFavorite.setProductId(product.getId());
+        shopFavorite.setUserId(Long.valueOf(TokenData.getUserId()));
+        save(shopFavorite);
+    }
 }

+ 0 - 9
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopOpinionServiceImpl.java

@@ -41,7 +41,6 @@ public class ShopOpinionServiceImpl extends SysServiceImpl<ShopOpinionMapper, Sh
                 (dto.getCurrentPage() - 1) * dto.getPageSize()
         );
 
-        // 这里直接使用 shopOpinions,无需转换
         List<ShopOpinionVo> shopOpinionVoList = shopOpinions; // 直接赋值
 
         int total = shopOpinionMapper.countAll(dto.getDescription(), dto.getState(),null);
@@ -73,14 +72,6 @@ public class ShopOpinionServiceImpl extends SysServiceImpl<ShopOpinionMapper, Sh
         return new RPCBaseResponse<>(200, "SUCCESS", pageVoLink);
     }
 
-    // 确保 convertToVo 方法处理正确
-    private ShopOpinionVo convertToVo(ShopOpinion shopOpinion) {
-        // 确保从 ShopOpinion 转换到 ShopOpinionVo 的逻辑是正确的
-        ShopOpinionVo vo = new ShopOpinionVo();
-        vo.setId(shopOpinion.getId());
-        vo.setCountryId(shopOpinion.getCountryId());
-        return vo;
-    }
     //查询国家信息
     private Map<String, ShopOpinionVo> fetchCountryData(Set<String> countryServeIds) {
         Map<String, ShopOpinionVo> countryMap = new HashMap<>();

+ 62 - 11
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopProductSkuServiceImpl.java

@@ -23,18 +23,14 @@ import edu.travel.commodity.service.ShopAddressService;
 import edu.travel.commodity.service.ShopProductService;
 import edu.travel.commodity.service.ShopProductSkuService;
 import edu.travel.commodity.service.ShopSkuSpecValueService;
-import edu.travel.commodity.utils.StringUtil;
 import edu.travel.commodity.utils.TokenData;
 import edu.travel.exception.BaseException;
 import edu.travel.remote.dto.*;
-import edu.travel.remote.vo.ShopSkuSpecValueVo;
-import edu.travel.remote.vo.ShopSnapshotVo;
-import edu.travel.remote.vo.SpecValueVo;
+import edu.travel.remote.vo.*;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.vo.BaseCountryServeVo;
 import edu.travel.vo.ShopCurrencyVo;
 import io.seata.spring.annotation.GlobalTransactional;
-import org.checkerframework.checker.units.qual.C;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -46,6 +42,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
 import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -100,7 +97,7 @@ public class ShopProductSkuServiceImpl extends ServiceImpl<ShopProductSkuMapper,
         //商品ID
         Set<String> product = products.stream().map(AddShopProductOrderDto::getProductId).collect(Collectors.toSet());
         List<ShopProduct> list1 = shopProductService.lambdaQuery().in(ShopProduct::getId, product).eq(ShopProduct::getStatus, 0).list();
-        if(list1.size()<products.size()){
+        if(list1.size()!=product.size()){
             throw new BaseException("商品不存在");
         }
         HashMap<String, ShopProduct> productMap = new HashMap<>();
@@ -239,19 +236,17 @@ public class ShopProductSkuServiceImpl extends ServiceImpl<ShopProductSkuMapper,
             for (InsertProductSkuDto insertProductSkuDto : skus) {
                 ShopProductSku bean = BeanUtil.toBean(insertProductSkuDto, ShopProductSku.class);
                 bean.setProductId(product.getId());
-                bean.setImageUrl(StringUtil.toListString(insertProductSkuDto.getImageUrl()));
+                bean.setImageUrl(JSONUtil.toJsonStr(insertProductSkuDto.getImageUrl()));
                 shopProductSkus.add(bean);
                 for (SkuValueDto skuValues : insertProductSkuDto.getSkuValues()) {
                     ShopSkuSpecValue skuValue = new ShopSkuSpecValue();
                     skuValue.setSkuId(bean.getSkuId());
-                    skuValue.setSpecId(Long.valueOf(skuValues.getSpecId()));
-                    List<InsertProductSpecDto> collect = specs.stream().filter(spec -> spec.getSpecId().equals(skuValues.getSpecId()))
-                            .collect(Collectors.toList());
+                    List<InsertProductSpecDto> collect = specs.stream().filter(spec -> spec.getSpecName().equals(skuValues.getSpecName())).collect(Collectors.toList());
                     if(ObjectUtil.isEmpty(collect)){
                        throw new BaseException("规格不存在");
                     }
                     InsertProductSpecDto insertProductSpecDto = collect.get(0);
-
+                    skuValue.setSpecId(Long.valueOf(insertProductSpecDto.getSpecId()));
                     List<SpecValueDto> collect1 = insertProductSpecDto.getSpecValues().stream()
                             .filter(specValue -> specValue.getSpecValueName().equals(skuValues.getSpecValueName())).collect(Collectors.toList());
                     skuValue.setSpecValueId(Long.valueOf(collect1.get(0).getSpecValueId()));
@@ -264,6 +259,62 @@ public class ShopProductSkuServiceImpl extends ServiceImpl<ShopProductSkuMapper,
         return saveBatch(shopProductSkus);
     }
 
+    @Override
+//    @GlobalTransactional
+    public void payProductSku(PayShopOrderDto payShopOrderDto) {
+        //查询订单
+        RPCBaseResponse<ShowOrderInfoVo> showOrderInfoVoRPCBaseResponse = orderAdapter.orderInfoByOrderNum(payShopOrderDto.getOrderNum());
+        if(showOrderInfoVoRPCBaseResponse.getCode()!=200){
+            throw new BaseException(showOrderInfoVoRPCBaseResponse.getMsg());
+        }
+        ShowOrderInfoVo data = showOrderInfoVoRPCBaseResponse.getData();
+        if(!data.getStatus().equals(OrderStateEnum.WAITING_FOR_PAYMENT.getState())){
+            throw new BaseException("订单状态有误");
+        }
+
+        List<OrderItemVo> orderItemVoList = data.getOrderItemVoList();
+        List<AddShopProductOrderDto> products = new ArrayList<AddShopProductOrderDto>();
+        Map<String, OrderItemVo> itemMap = new ConcurrentHashMap<>();
+        for (OrderItemVo orderItemVo : orderItemVoList) {
+            itemMap.put(orderItemVo.getProductId()+":"+orderItemVo.getSkuId(),orderItemVo);
+            AddShopProductOrderDto addShopProductOrderDto = new AddShopProductOrderDto();
+            addShopProductOrderDto.setProductId(orderItemVo.getProductId());
+            addShopProductOrderDto.setSkuId(orderItemVo.getSkuId());
+            products.add(addShopProductOrderDto);
+        }
+        List<String> collect = orderItemVoList.stream().map(item -> item.getProductId() + ":" + item.getSkuId()).sorted()
+                .collect(Collectors.toList());
+        RLock[] locks=new RLock[collect.size()];
+        for (int i = 0; i < locks.length; i++) {
+            String s = collect.get(i);
+            locks[i] = redissonClient.getFairLock(RedisKey.PRODUCT_ORDER+s);
+        }
+        RLock multiLock = redissonClient.getMultiLock(locks);
+        //扣除冻结和库存
+        try {
+            if(multiLock.tryLock(20, TimeUnit.SECONDS)){
+                List<ShopProductSku> ls = shopProductSkuMapper.getByProductSkus(products);
+                for (ShopProductSku shopProductSku : ls) {
+                    OrderItemVo orderItemVo = itemMap.get(shopProductSku.getProductId() + ":" + shopProductSku.getSkuId());
+                    String quantity = orderItemVo.getQuantity();
+                    int i = Integer.parseInt(quantity);
+                    //扣除冻结
+                    shopProductSku.setFreeze(shopProductSku.getFreeze()-i);
+                    //扣除库存
+                    shopProductSku.setInventory(shopProductSku.getInventory()-i);
+                }
+                updateBatchById(ls);
+                // 订单状态扭转
+                orderAdapter.changeOrderState(payShopOrderDto.getOrderNum(),OrderStateEnum.AWAITING_RECEIPT.getState()+"");
+            }
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+            throw new BaseException(e);
+        }finally {
+            multiLock.unlock();//解锁
+        }
+    }
+
 
     //获取商品快照信息
     public Map<String,ShopSnapshotVo> getSnapshot(List<AddShopProductOrderDto> products) {

+ 8 - 0
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopProductSpecServiceImpl.java

@@ -2,6 +2,8 @@ package edu.travel.commodity.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -10,6 +12,8 @@ import edu.travel.commodity.entity.*;
 import edu.travel.commodity.mapper.ShopProductSpecMapper;
 import edu.travel.commodity.service.*;
 import edu.travel.commodity.utils.IdUtils;
+import edu.travel.commodity.utils.StringUtil;
+import edu.travel.remote.dto.FIleDto;
 import edu.travel.remote.dto.InsertProductSpecDto;
 import edu.travel.remote.dto.ProductSpecDto;
 import edu.travel.remote.dto.SpecValueDto;
@@ -85,6 +89,8 @@ public class ShopProductSpecServiceImpl extends ServiceImpl<ShopProductSpecMappe
             bean.setImages(imageMap.get(info.getSkuId().toString()));
             List<ShopSkuSpecValueVo> shopSkuSpecValueVos = map.get(info.getSkuId().toString());
             infoVo.setSpecValue(shopSkuSpecValueVos);
+            JSONArray objects = JSONUtil.parseArray(info.getImageUrl());
+            infoVo.setImageUrls(JSONUtil.toList(objects, FIleDto.class));
             productSkuVos.add(infoVo);
         }
         List<SpecSortVo> specSortVoList= getSpecSortVoList(param.getProductId());
@@ -120,6 +126,8 @@ public class ShopProductSpecServiceImpl extends ServiceImpl<ShopProductSpecMappe
             ShopProductSpec shopProductSpec = new ShopProductSpec();
             long id = IdUtils.getSnowflakeId();
             shopProductSpec.setId(id);
+            //覆盖id后期存sku 和 规格关联
+            spec.setSpecId(id+"");
             shopProductSpec.setProductId(bean.getId());
             shopProductSpec.setSpecName(spec.getSpecName());
             shopProductSpec.setSpecId(Long.valueOf(spec.getSpecId()));

+ 17 - 2
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopReviewServiceImpl.java

@@ -7,9 +7,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import edu.travel.adapter.service.order.OrderAdapter;
 import edu.travel.adapter.service.order.OrderItemAdapter;
 import edu.travel.adapter.service.tenant.TenantAdapter;
 import edu.travel.commodity.entity.ShopReview;
+import edu.travel.commodity.enums.OrderStateEnum;
 import edu.travel.commodity.mapper.ShopReviewMapper;
 import edu.travel.commodity.service.ShopReviewService;
 import edu.travel.commodity.utils.ObjectUtils;
@@ -38,6 +40,8 @@ public class ShopReviewServiceImpl extends ServiceImpl<ShopReviewMapper, ShopRev
     private TenantAdapter tenantAdapter;
     @Autowired
     private OrderItemAdapter orderItemAdapter;
+    @Autowired
+    private OrderAdapter orderAdapter;
     @Override
     public Page<ShopReviewVo> getShopReview(ProductSpecDto params) {
         LambdaQueryWrapper<ShopReview> query = Wrappers.<ShopReview>lambdaQuery()
@@ -78,9 +82,20 @@ public class ShopReviewServiceImpl extends ServiceImpl<ShopReviewMapper, ShopRev
 
     @Override
     public String addReview(AddReviewDto params) {
+        RPCBaseResponse<ShopOrderVo> resultValue = orderAdapter.getFormId(params.getOrderId());
+        if(resultValue.getCode()!=200){
+            throw new BaseException("订单不存在");
+        }
+        ShopOrderVo data1 = resultValue.getData();
+        if(!data1.getStatus().equals(OrderStateEnum.AWAITING_EVALUATION.getState())){
+            throw new BaseException("订单未完结,不能评价");
+        }
+
         String userId = TokenData.getUserId();
-        LambdaQueryWrapper<ShopReview> query = Wrappers.<ShopReview>lambdaQuery().eq(ShopReview::getUserId, userId)
+        LambdaQueryWrapper<ShopReview> query = Wrappers.<ShopReview>lambdaQuery()
+                .eq(ShopReview::getUserId, userId)
                 .eq(ShopReview::getOrderId, params.getOrderId())
+                .eq(ShopReview::getSkuId, params.getSkuId())
                 .eq(ShopReview::getProductId, params.getProductId());
         List<ShopReview> list = list(query);
         if(!ObjectUtil.isEmpty(list)){
@@ -107,7 +122,7 @@ public class ShopReviewServiceImpl extends ServiceImpl<ShopReviewMapper, ShopRev
         if(!ObjectUtil.isEmpty(snapshot)){
             //订单快照对象
             ShopSnapshotVo bean1 = JSONUtil.toBean(snapshot, ShopSnapshotVo.class);
-            List<Object> snapshotSpec = bean1.getSnapshotSpec();
+            List<SpecValueVo> snapshotSpec = bean1.getSnapshotSpec();
             List<SpecValueVo> snapshot1 = ObjectUtils.castList(snapshotSpec, SpecValueVo.class);
             String jsonStr = JSONUtil.toJsonStr(snapshot1);
             bean.setSkuDetails(jsonStr);

+ 30 - 2
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/service/impl/ShopShoppingCartServiceImpl.java

@@ -20,6 +20,7 @@ import edu.travel.commodity.utils.TokenData;
 import edu.travel.remote.dto.AddShopCartDto;
 import edu.travel.remote.dto.BaseDto;
 import edu.travel.exception.BaseException;
+import edu.travel.remote.dto.UpdateShopCarDto;
 import edu.travel.remote.vo.ShopCartListVo;
 import edu.travel.remote.vo.ShopSkuSpecValueVo;
 import edu.travel.remote.vo.SpecValueVo;
@@ -42,6 +43,9 @@ public class ShopShoppingCartServiceImpl extends ServiceImpl<ShopShoppingCartMap
     private ShopProductSpecMapper shopProductSpecMapper;
     @Override
     public Boolean addShopCart(AddShopCartDto params) {
+        if(params.getQuantity()<=0){
+            throw new BaseException("数量有误,请重新选择");
+        }
         // 获取用户id
         String userId = TokenData.getUserId();
         ShopProduct byId = shopProductService.getById(params.getProductId());
@@ -51,11 +55,11 @@ public class ShopShoppingCartServiceImpl extends ServiceImpl<ShopShoppingCartMap
         if(byId==null||byId.getStatus().equals(BaseConstant.BASIC_STATUS_YES_NUM)||one==null){
             throw new BaseException("商品已经下架");
         }
-
         ShopShoppingCart shopShoppingCart = new ShopShoppingCart();
         shopShoppingCart.setProductSkuId(one.getId());
         shopShoppingCart.setUserId(Long.valueOf(userId));
         shopShoppingCart.setQuantity(params.getQuantity());
+        shopShoppingCart.setProductId(one.getProductId());
         return save(shopShoppingCart);
     }
 
@@ -91,7 +95,9 @@ public class ShopShoppingCartServiceImpl extends ServiceImpl<ShopShoppingCartMap
         String project = request.getHeader("project");
         List<ShopSkuSpecValueVo> skuValue = shopProductSpecMapper.getSkuValue(skus, project);
         Map<String, List<ShopSkuSpecValueVo>> sku = skuValue.stream().collect(Collectors.groupingBy(ShopSkuSpecValueVo::getSkuId));
-
+        if(ObjectUtil.isEmpty(collect)){
+            return pageEntity;
+        }
 
         List<ShopProduct> shopProducts = shopProductService.listByIds(collect);
         HashMap<String, ShopProduct> map = new HashMap<>();
@@ -111,6 +117,8 @@ public class ShopShoppingCartServiceImpl extends ServiceImpl<ShopShoppingCartMap
                     record.setPrice(shopProductSku.getPrice());
                     record.setInventory(shopProductSku.getInventory());
                     record.setSkuId(shopProductSku.getSkuId().toString());
+                    //0 上架 1 下架
+                    record.setState(shopProductSku.getStatus().toString());
                 }
                 String skuId = record.getSkuId();
                 if(ObjectUtil.isEmpty(skuId))continue;
@@ -124,5 +132,25 @@ public class ShopShoppingCartServiceImpl extends ServiceImpl<ShopShoppingCartMap
         return pageEntity;
     }
 
+    @Override
+    public Boolean updateCart(UpdateShopCarDto params) {
+        ShopShoppingCart cart = getById(params.getId());
+        if(cart==null){
+            return false;
+        }
+        if(!ObjectUtil.isEmpty(params.getQuery())&&params.getQuery()>0){
+            cart.setQuantity(params.getQuery());
+        }
+        if(!ObjectUtil.isEmpty(params.getSkuId())){
+            cart.setProductSkuId(Long.valueOf(params.getSkuId()));
+        }
+        return updateById(cart);
+    }
+
+    @Override
+    public Integer delCart(Set<String> ids) {
+        return baseMapper.deleteBatchIds(ids);
+    }
+
 
 }

+ 42 - 1
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopFavoriteController.java

@@ -1,7 +1,12 @@
 package edu.travel.commodity.web;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import edu.travel.commodity.entity.ShopFavorite;
 import edu.travel.commodity.service.ShopFavoriteService;
+import edu.travel.commodity.utils.TokenData;
+import edu.travel.remote.dto.AddFavoriteDto;
 import edu.travel.remote.dto.BaseDto;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.remote.vo.ShopFavoriteVo;
@@ -9,6 +14,10 @@ import org.springframework.web.bind.annotation.*;
 
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.util.Set;
+
+import static edu.travel.rpc.RPCBaseResponse.success;
+
 /**
 * 收藏表(shop_favorite)表控制层
 *
@@ -29,6 +38,38 @@ public class ShopFavoriteController {
      */
     @GetMapping("/getFavorite")
     public RPCBaseResponse<Page<ShopFavoriteVo>> getFavorite(BaseDto baseDto){
-        return RPCBaseResponse.success(shopFavoriteService.getFavorite(baseDto));
+        return success(shopFavoriteService.getFavorite(baseDto));
+    }
+
+
+    /**
+     *  添加收藏
+     * @param params
+     * @return {@link RPCBaseResponse }<{@link Void }>
+     */
+
+    @PostMapping("/addFavorite")
+    public RPCBaseResponse<Void> addFavorite(@RequestBody AddFavoriteDto params){
+        shopFavoriteService.addFavorite(params);
+        return success();
+    }
+
+    /**
+     * 移除收藏
+     */
+    @PostMapping("/removeFavorite")
+    public RPCBaseResponse<Void> removeFavorite(@RequestBody Set<String> ids){
+        shopFavoriteService.removeByIds(ids);
+        return success();
+    }
+
+    /**
+     * 全部移除
+     */
+    @PostMapping("/removeAllFavorite")
+    public RPCBaseResponse<Void> removeAllFavorite(){
+        LambdaQueryWrapper<ShopFavorite> query = Wrappers.<ShopFavorite>lambdaQuery().eq(ShopFavorite::getUserId, TokenData.getUserId());
+        shopFavoriteService.remove(query);
+        return success();
     }
 }

+ 53 - 0
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopOpinionController.java

@@ -1,10 +1,18 @@
 package edu.travel.commodity.web;
 
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileTypeUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import edu.travel.adapter.service.upload.UploadAdapter;
+import edu.travel.commodity.constant.BaseConstant;
 import edu.travel.commodity.entity.ShopOpinion;
 import edu.travel.commodity.service.ShopOpinionService;
+import edu.travel.commodity.utils.FIleUtil;
 import edu.travel.remote.dto.ShopOpinionDto;
 import edu.travel.remote.opinion.ShopOpinionRemoteController;
+import edu.travel.remote.upload.dto.EduFileDTO;
 import edu.travel.remote.vo.ShopOpinionVo;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.web.BaseController;
@@ -12,9 +20,14 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.web.bind.annotation.*;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.util.Date;
 import java.util.List;
 
+import static edu.travel.rpc.RPCBaseResponse.error;
+import static edu.travel.rpc.RPCBaseResponse.success;
+
 /**
 * 意见表(shop_opinion)表控制层
 *
@@ -28,6 +41,8 @@ public class ShopOpinionController extends BaseController<ShopOpinion> implement
 */
     @Autowired
     private ShopOpinionService shopOpinionService;
+    @Autowired
+    private UploadAdapter uploadAdapter;
     /**
      * 分页查询(连表)
      */
@@ -99,4 +114,42 @@ public class ShopOpinionController extends BaseController<ShopOpinion> implement
         return baseCountryVoRPCBaseResponse;
     }
 
+    /**
+     * 意见图片上传
+     */
+    @GetMapping("/uploadOpinionImage")
+    public RPCBaseResponse<String> uploadOpinionImage(MultipartFile file) {
+        if (file == null || file.isEmpty()) {
+            return error();
+        }
+        try {
+            // 通过文件的字节流获取真实类型(Hutool自动解析魔数)
+            String fileType = FileTypeUtil.getType(file.getInputStream());
+            // 判断类型是否在支持的图片类型集合中
+            if (fileType != null && BaseConstant.getOpinionFileTypes().contains(fileType.toLowerCase())) {
+                String filename = file.getOriginalFilename();
+                if(ObjectUtil.isEmpty(filename)){
+                    return error();
+                }
+                RPCBaseResponse<String> stringRPCBaseResponse = uploadAdapter.initializeUpload();
+
+                EduFileDTO eduFileDTO = new EduFileDTO();
+                eduFileDTO.setUploadId(stringRPCBaseResponse.getData());
+                eduFileDTO.setFileType(fileType);
+                eduFileDTO.setFileName(filename);
+                eduFileDTO.setFileSysName(IdUtil.fastSimpleUUID()+ DateUtil.format(new Date(),"yyyyMMddHHmmss") +"."+fileType);
+                eduFileDTO.setFileMd5(FIleUtil.calculateFileMd5(file.getBytes()));
+                RPCBaseResponse rpcBaseResponse = uploadAdapter.uploadFile(eduFileDTO, file);
+                return success(rpcBaseResponse.getData().toString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            // 处理流读取异常或类型解析失败
+            return error("文件上传失败");
+        }
+        return error();
+    }
+
+
+
 }

+ 9 - 5
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopProductController.java

@@ -11,6 +11,7 @@ import edu.travel.commodity.constant.BaseConstant;
 import edu.travel.commodity.entity.ShopProduct;
 import edu.travel.commodity.service.ShopProductService;
 import edu.travel.commodity.utils.FIleUtil;
+import edu.travel.commodity.utils.IdUtils;
 import edu.travel.remote.dto.GetProductByTypeDto;
 import edu.travel.remote.dto.InsertProductDto;
 import edu.travel.remote.dto.SearchProductDto;
@@ -68,6 +69,11 @@ public class ShopProductController extends BaseController<ShopProduct> {
     }
 
 
+    /**
+     *  上传商品图片
+     * @param file
+     * @return {@link RPCBaseResponse }<{@link String }>
+     */
 
     @PostMapping("/uploadProductImage")
     public RPCBaseResponse<String> searchProduct(MultipartFile file) throws IOException {
@@ -117,14 +123,12 @@ public class ShopProductController extends BaseController<ShopProduct> {
 
     /**
      *  获取SkuId
-     * @param params
      * @return {@link RPCBaseResponse }<{@link String }>
      */
 
     @GetMapping("/getSkuId")
-    public RPCBaseResponse<String> getSkuId(@RequestBody InsertProductDto params){
-        Snowflake snowflake = IdUtil.createSnowflake(1, 1);
-        long orderNum = snowflake.nextId();
-        return success(orderNum+"");
+    public RPCBaseResponse<String> getSkuId(){
+        long id = IdUtils.getSnowflakeId();
+        return success(id+"");
     }
 }

+ 13 - 0
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopProductSkuController.java

@@ -6,6 +6,7 @@ import edu.travel.commodity.service.ShopProductSkuService;
 import edu.travel.remote.commodity.ShopProductSkuRemoteController;
 import edu.travel.remote.dto.AddShopOrderDto;
 import edu.travel.remote.dto.GetOrderItemInfoByOrderNumDto;
+import edu.travel.remote.dto.PayShopOrderDto;
 import edu.travel.remote.vo.OrderItemVo;
 import edu.travel.remote.vo.ShopSkuSpecValueVo;
 import edu.travel.rpc.RPCBaseResponse;
@@ -56,4 +57,16 @@ public class ShopProductSkuController extends BaseController<ShopProductSku> imp
         return RPCBaseResponse.success();
     }
 
+    /**
+     *  支付商品订单
+     * @param payShopOrderDto
+     * @return {@link RPCBaseResponse }<{@link String }>
+     */
+
+    @PostMapping("/payProductSku")
+    public RPCBaseResponse<Void> payProductSku(@RequestBody PayShopOrderDto payShopOrderDto){
+        shopProductSkuService.payProductSku(payShopOrderDto);
+        return RPCBaseResponse.success();
+    }
+
 }

+ 83 - 12
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopReviewController.java

@@ -1,35 +1,41 @@
 package edu.travel.commodity.web;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import edu.travel.commodity.entity.ShopReview;
 import edu.travel.commodity.service.ShopReviewService;
+import edu.travel.remote.commodity.ShopReviewRemoteController;
 import edu.travel.remote.dto.AddReviewDto;
 import edu.travel.remote.dto.ProductSpecDto;
-import edu.travel.rpc.RPCBaseResponse;
+import edu.travel.remote.dto.ShopReviewDto;
 import edu.travel.remote.vo.ShopReviewVo;
-import org.springframework.web.bind.annotation.*;
-
+import edu.travel.rpc.RPCBaseResponse;
+import edu.travel.web.BaseController;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
 
+import java.util.List;
 
 import static edu.travel.rpc.RPCBaseResponse.success;
 
 
 /**
-* 商品评论表(shop_review)表控制层
-*
-* @author xxxxx
-*/
+ * 商品评论表(shop_review)表控制层
+ *
+ * @author xxxxx
+ */
 @RestController
 @RequestMapping("/shopReview")
-public class ShopReviewController {
-/**
-* 服务对象
-*/
+public class ShopReviewController extends BaseController<ShopReview> implements ShopReviewRemoteController {
+    /**
+     * 服务对象
+     */
     @Autowired
     private ShopReviewService shopReviewService;
 
     /**
-     *  获取商品评论
+     * 获取商品评论
+     *
      * @param params
      * @return {@link RPCBaseResponse }<{@link Page }<{@link ShopReviewVo }>>
      */
@@ -41,6 +47,7 @@ public class ShopReviewController {
 
     /**
      * 发表评论
+     *
      * @param params
      * @return {@link RPCBaseResponse }<{@link String }>
      */
@@ -48,4 +55,68 @@ public class ShopReviewController {
     public RPCBaseResponse<String> addReview(@RequestBody AddReviewDto params) {
         return success(shopReviewService.addReview(params));
     }
+
+    /**
+     * 通过id查找商品评论
+     */
+    @Override
+    @GetMapping("/getFormId")
+    public RPCBaseResponse<ShopReviewVo> getFormId(String id) {
+        RPCBaseResponse<ShopReview> shopReviewRPCBaseResponse = super.getId(id);
+        RPCBaseResponse<ShopReviewVo> shopReviewVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(shopReviewRPCBaseResponse, shopReviewVoRPCBaseResponse);
+        return shopReviewVoRPCBaseResponse;
+    }
+
+    /**
+     * 根据id更新商品评论
+     */
+    @Override
+    @PostMapping("/updateTargetFormId")
+    public RPCBaseResponse<ShopReviewVo> updateTargetFormId(ShopReviewDto entity) {
+        ShopReview shopReview = new ShopReview();
+        BeanUtils.copyProperties(entity, shopReview);
+        RPCBaseResponse<ShopReview> shopReviewRPCBaseResponse = super.updateTargetById(shopReview);
+        RPCBaseResponse<ShopReviewVo> shopReviewVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(shopReviewRPCBaseResponse, shopReviewVoRPCBaseResponse);
+        return shopReviewVoRPCBaseResponse;
+    }
+
+    /**
+     * 新增商品评论
+     */
+    @Override
+    @PostMapping("/saveFormTarget")
+    public RPCBaseResponse<ShopReviewVo> saveFormTarget(ShopReviewDto entity) {
+        ShopReview shopReview = new ShopReview();
+        BeanUtils.copyProperties(entity, shopReview);
+        RPCBaseResponse<ShopReview> shopReviewRPCBaseResponse = super.saveTarget(shopReview);
+        RPCBaseResponse<ShopReviewVo> shopReviewVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(shopReviewRPCBaseResponse, shopReviewVoRPCBaseResponse);
+        return shopReviewVoRPCBaseResponse;
+    }
+
+    /**
+     * 删除商品评论
+     */
+    @Override
+    @PostMapping("/deleteTargetFormId")
+    public RPCBaseResponse<ShopReviewVo> deleteTargetFormId(List<String> ids) {
+        RPCBaseResponse<ShopReview> shopReviewRPCBaseResponse = super.deleteTargetById(ids);
+        RPCBaseResponse<ShopReviewVo> shopReviewVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(shopReviewRPCBaseResponse, shopReviewVoRPCBaseResponse);
+        return shopReviewVoRPCBaseResponse;
+    }
+
+    /**
+     * 获取所有商品评论
+     */
+    @Override
+    @GetMapping("/getAllForm")
+    public RPCBaseResponse<List<ShopReviewVo>> getAllForm() {
+        RPCBaseResponse<List<ShopReview>> shopReviewRPCBaseResponse = super.listAll();
+        RPCBaseResponse<List<ShopReviewVo>> shopReviewVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(shopReviewRPCBaseResponse, shopReviewVoRPCBaseResponse);
+        return shopReviewVoRPCBaseResponse;
+    }
 }

+ 27 - 0
edu-travel-service/edu-travel-service-commodity/src/main/java/edu/travel/commodity/web/ShopShoppingCartController.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import edu.travel.commodity.service.ShopShoppingCartService;
 import edu.travel.remote.dto.AddShopCartDto;
 import edu.travel.remote.dto.BaseDto;
+import edu.travel.remote.dto.UpdateShopCarDto;
 import edu.travel.resp.BaseResponse;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.remote.vo.ShopCartListVo;
@@ -11,6 +12,8 @@ import org.springframework.web.bind.annotation.*;
 
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.util.Set;
+
 import static edu.travel.rpc.RPCBaseResponse.success;
 
 /**
@@ -49,5 +52,29 @@ public class ShopShoppingCartController {
         return success(shopShoppingCartService.getShopCartList(params));
     }
 
+    /**
+     * 修改购物车信息
+     * @param params
+     * @return {@link BaseResponse }<{@link Page }<{@link ShopCartListVo }>>
+     */
+
+    @PostMapping("/updateCart")
+    public RPCBaseResponse<Boolean> updateCart(@RequestBody UpdateShopCarDto params) {
+        return success(shopShoppingCartService.updateCart(params));
+    }
+    /**
+     * 删除购物车
+     * @param ids
+     * @return {@link BaseResponse }<{@link Page }<{@link ShopCartListVo }>>
+     */
+
+    @PostMapping("/delCart")
+    public RPCBaseResponse<Integer> delCart(@RequestBody Set<String> ids) {
+        return success(shopShoppingCartService.delCart(ids));
+    }
+
+
+
+
 
 }

+ 1 - 2
edu-travel-service/edu-travel-service-commodity/src/main/resources/mapper/ShopProductImageMapper.xml

@@ -5,7 +5,6 @@
     <!--@mbg.generated-->
     <!--@Table shop_product_image-->
     <id column="id" jdbcType="BIGINT" property="id" />
-    <result column="sku_id" jdbcType="BIGINT" property="skuId" />
     <result column="product_id" jdbcType="BIGINT" property="productId" />
     <result column="url" jdbcType="VARCHAR" property="url" />
     <result column="url_type" jdbcType="VARCHAR" property="urlType" />
@@ -20,7 +19,7 @@
   </resultMap>
   <sql id="Base_Column_List">
     <!--@mbg.generated-->
-    id, sku_id, product_id, url, url_type, sort_order, project, create_time, create_user_id, 
+    id, product_id, url, url_type, sort_order, project, create_time, create_user_id,
     update_time, update_user_id, delete_flag,is_default
   </sql>
 </mapper>

+ 60 - 0
edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/controller/ShopOrderController.java

@@ -1,19 +1,26 @@
 package edu.travel.order.controller;
 
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import edu.travel.order.entity.ShopOrder;
 import edu.travel.order.service.ShopOrderService;
 import edu.travel.remote.dto.AddOrderDto;
 import edu.travel.remote.dto.GetOrderItemInfoByOrderNumDto;
+import edu.travel.remote.dto.ShopOrderDto;
 import edu.travel.remote.dto.ShowOrderInfoDto;
 import edu.travel.remote.order.ShopOrderRemoteController;
 import edu.travel.remote.vo.OrderItemVo;
+import edu.travel.remote.vo.ShopOrderVo;
 import edu.travel.remote.vo.ShowOrderInfoVo;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.web.BaseController;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.List;
+
 import static edu.travel.rpc.RPCBaseResponse.success;
 
 
@@ -53,5 +60,58 @@ public class ShopOrderController extends BaseController<ShopOrder> implements Sh
         return RPCBaseResponse.success(shopOrderService.getOrderItemInfo(params));
     }
 
+    /**
+     *  通过订单号查询订单详情
+     * @param orderNum 订单号
+     * @return {@link RPCBaseResponse }<{@link Page }<{@link ShowOrderInfoVo }>>
+     */
+    @GetMapping("/orderInfoByOrderNum")
+    public RPCBaseResponse<ShowOrderInfoVo> orderInfoByOrderNum(String orderNum){
+        return success(shopOrderService.orderInfoByOrderNum(orderNum));
+    }
+
+    /**
+     *  修改订单状态
+     * @param orderNum 订单号 state 状态 OrderStateEnum
+     * @return {@link RPCBaseResponse }<{@link Page }<{@link ShowOrderInfoVo }>>
+     */
+    @GetMapping("/changeOrderState")
+    public RPCBaseResponse<Void> changeOrderState(String orderNum, String state){
+        shopOrderService.changeOrderState(orderNum,state);
+        return success();
+    }
+
+    /**
+     *  订单号查询订单
+     * @param id
+     * @return {@link RPCBaseResponse }<{@link ShopOrderVo }>
+     */
 
+    @GetMapping("/getFormId")
+    public RPCBaseResponse<ShopOrderVo> getFormId(@RequestParam("id") String id){
+        LambdaQueryWrapper<ShopOrder> query = Wrappers.<ShopOrder>lambdaQuery().eq(ShopOrder::getOrderNumber, id);
+        ShopOrder byId = shopOrderService.getOne(query);
+        ShopOrderVo bean = BeanUtil.toBean(byId, ShopOrderVo.class);
+        return success(bean);
+    }
+
+    @Override
+    public RPCBaseResponse<ShopOrderVo> updateTargetFormId(ShopOrderDto entity) {
+        return null;
+    }
+
+    @Override
+    public RPCBaseResponse<ShopOrderVo> saveFormTarget(ShopOrderDto entity) {
+        return null;
+    }
+
+    @Override
+    public RPCBaseResponse<ShopOrderVo> deleteTargetFormId(List<String> ids) {
+        return null;
+    }
+
+    @Override
+    public RPCBaseResponse<List<ShopOrderVo>> getAllForm() {
+        return null;
+    }
 }

+ 3 - 2
edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/controller/ShopOrderItemController.java

@@ -21,6 +21,7 @@ import edu.travel.remote.vo.SpecValueVo;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.web.BaseController;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.openfeign.SpringQueryMap;
 import org.springframework.security.core.parameters.P;
 import org.springframework.web.bind.annotation.*;
 
@@ -45,8 +46,8 @@ public class ShopOrderItemController extends BaseController<ShopOrderItem> imple
      */
 
     @Override
-    @GetMapping("/getOrderItemVo")
-    public RPCBaseResponse<OrderItemVo> getOrderItemVo(GetOrderItemDto params) {
+    @PostMapping("/getOrderItemVo")
+    public RPCBaseResponse<OrderItemVo> getOrderItemVo(@RequestBody GetOrderItemDto params) {
         LambdaQueryWrapper<ShopOrder> orderQuery = Wrappers.<ShopOrder>lambdaQuery().eq(ShopOrder::getOrderNumber, params.getOrderId())
                 .eq(ShopOrder::getStatus, OrderStateEnum.AWAITING_EVALUATION.getState());
         ShopOrder one = shopOrderService.getOne(orderQuery);

+ 4 - 6
edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/enums/OrderStateEnum.java

@@ -11,12 +11,10 @@ import lombok.Getter;
 @Getter
 public enum OrderStateEnum {
     WAITING_FOR_PAYMENT(0, "待付款"),
-    ORDERED(1, "已下单"),
-    AWAITING_SHIPMENT(2, "待发货"),
-    AWAITING_RECEIPT(3, "待收货"),
-    AWAITING_EVALUATION(4, "待评价"),
-    COMPLETED(5, "已完成"),
-    CANCELLED(6, "已取消");
+    ORDERED(1, "已下单,待发货"),
+    AWAITING_RECEIPT(2, "待收货"),
+    AWAITING_EVALUATION(3, "已完成"),
+    CANCELLED(4, "已取消");
 
     private final int state;
     private final String name;

+ 4 - 0
edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/service/ShopOrderService.java

@@ -17,4 +17,8 @@ public interface ShopOrderService extends IService<ShopOrder> {
     void addShopOrder(AddOrderDto params);
 
     Page<OrderItemVo> getOrderItemInfo(GetOrderItemInfoByOrderNumDto params);
+
+    ShowOrderInfoVo orderInfoByOrderNum(String orderNum);
+
+    void changeOrderState(String orderNum, String state);
 }

+ 52 - 0
edu-travel-service/edu-travel-service-order/src/main/java/edu/travel/order/service/impl/ShopOrderServiceImpl.java

@@ -8,11 +8,15 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import edu.travel.exception.BaseException;
 import edu.travel.order.entity.ShopOrder;
 import edu.travel.order.entity.ShopOrderItem;
+import edu.travel.order.entity.ShopOrderTypeChange;
+import edu.travel.order.enums.OrderStateEnum;
 import edu.travel.order.mapper.ShopOrderMapper;
 import edu.travel.order.service.ShopOrderItemService;
 import edu.travel.order.service.ShopOrderService;
+import edu.travel.order.service.ShopOrderTypeChangeService;
 import edu.travel.order.utils.PageUtil;
 import edu.travel.order.utils.TokenData;
 import edu.travel.remote.dto.AddOrderDto;
@@ -33,6 +37,8 @@ import java.util.stream.Collectors;
 public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder> implements ShopOrderService {
     @Autowired
     private ShopOrderItemService shopOrderItemService;
+    @Autowired
+    private ShopOrderTypeChangeService shopOrderTypeChangeService;
 
 
     @Override
@@ -97,5 +103,51 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
         return pageEntity;
     }
 
+    @Override
+    public ShowOrderInfoVo orderInfoByOrderNum(String orderNum) {
+
+        LambdaQueryWrapper<ShopOrder> query = Wrappers.<ShopOrder>lambdaQuery();
+        query.eq(ShopOrder::getOrderNumber, orderNum);
+
+
+        ShopOrder one = getOne(query);
+        if(ObjectUtil.isEmpty(one)){
+            throw new BaseException("订单不存在");
+        }
+        ShowOrderInfoVo result = BeanUtil.toBean(one, ShowOrderInfoVo.class);
+
+        LambdaQueryWrapper<ShopOrderItem> itemQuery = Wrappers.<ShopOrderItem>lambdaQuery();
+        itemQuery.eq(ShopOrderItem::getOrderId,orderNum);
+        List<ShopOrderItem> list = shopOrderItemService.list();
+        List<OrderItemVo> orderItemVos = BeanUtil.copyToList(list, OrderItemVo.class);
+        for (OrderItemVo orderItemVo : orderItemVos) {
+            ShopSnapshotVo bean1 = JSONUtil.toBean(orderItemVo.getSnapshot(), ShopSnapshotVo.class);
+            orderItemVo.setShopSnapshotVo(bean1);
+        }
+        result.setOrderItemVoList(orderItemVos);
+        return result;
+    }
+
+    @Override
+    public void changeOrderState(String orderNum, String state) {
+        //修改订单状态
+        LambdaQueryWrapper<ShopOrder> query = Wrappers.<ShopOrder>lambdaQuery();
+        query.eq(ShopOrder::getOrderNumber, orderNum);
+        query.eq(ShopOrder::getStatus, OrderStateEnum.WAITING_FOR_PAYMENT.getState());
+
+        ShopOrder one = getOne(query);
+        one.setStatus(Integer.parseInt(state));
+        updateById(one);
+
+        OrderStateEnum orderStateEnum = OrderStateEnum.fromState(Integer.parseInt(state));
+        String userId = TokenData.getUserId();
+        ShopOrderTypeChange shopOrderTypeChange = new ShopOrderTypeChange();
+        shopOrderTypeChange.setStatus(Integer.parseInt(state));
+        shopOrderTypeChange.setOrderId(orderNum);
+        shopOrderTypeChange.setCreateUserId(userId);
+        shopOrderTypeChange.setDescription(orderStateEnum.getName());
+        shopOrderTypeChangeService.save(shopOrderTypeChange);
+    }
+
 
 }

+ 6 - 0
edu-travel-service/edu-travel-service-tenement/pom.xml

@@ -139,6 +139,12 @@
             <version>1.0-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-adapter-country</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
 
     </dependencies>
     <profiles>

+ 43 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/constant/BaseConstant.java

@@ -0,0 +1,43 @@
+package edu.travel.tenant.constant;
+
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class BaseConstant {
+    /**
+     * 删除标记 正常
+     */
+    public static final Integer BASIC_STATUS_NO_NUM = 0;
+    public static final String BASIC_STATUS_NO_STR = "0";
+
+    /**
+     * 删除标记 删除
+     */
+    public static final Integer BASIC_STATUS_YES_NUM = 1;
+    public static final String BASIC_STATUS_YES_STR = "1";
+
+    /**
+     * 删除标记 正常
+     */
+    public static final Integer BASIC_IS_DEFAULT_YES = 0;
+    public static final Integer BASIC_IS_DEFAULT_YES_NO = 1;
+
+    /**
+     * 商品文件上传图片类型
+     */
+    private static final Set<String> PRODUCT_IMAGE_TYPES = new HashSet<>(
+            Arrays.asList("jpg", "jpeg", "png")
+    );
+
+    public static Set<String> getProductImageTypes() {
+        return PRODUCT_IMAGE_TYPES;
+    }
+    /**
+     * 获取banner 图片类型
+     */
+    public static Set<String> getBannerImageTypes() {
+        return PRODUCT_IMAGE_TYPES;
+    }
+}

+ 19 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/constant/RedisKey.java

@@ -0,0 +1,19 @@
+package edu.travel.tenant.constant;
+
+public interface RedisKey {
+    /**商品类型*/
+    String BASH = "shop:";
+    /**商品类型*/
+   String PRODUCT_TYPE = BASH +"product:type";
+   /**热门商品类型*/
+   String PRODUCT_HOT_TYPE = BASH +"product:hot:type";
+    /**热门二级商品类型*/
+   String PRODUCT_TO_HOT_TYPE = BASH +"product:to:hot:type";
+    /**
+     * 加上国家
+     */
+
+
+    String PRODUCT_ORDER = BASH +"product:order";
+
+}

+ 13 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/entity/ShopBanner.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import edu.travel.entity.BaseEntity;
 import lombok.AllArgsConstructor;
 import lombok.Data;
@@ -89,5 +90,17 @@ public class ShopBanner extends BaseEntity {
      */
     @TableField(value = "`enable`")
     private Integer enable;
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+    /**
+     * 更新时间
+     */
+    @TableField(value = "update_time")
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
 
 }

+ 8 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/mapper/ShopBannerMapper.java

@@ -1,7 +1,15 @@
 package edu.travel.tenant.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import edu.travel.remote.feign.mode.dto.tenant.BannerDto;
+import edu.travel.remote.feign.mode.vo.banner.BannerVo;
 import edu.travel.tenant.entity.ShopBanner;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 public interface ShopBannerMapper extends BaseMapper<ShopBanner> {
+    List<BannerVo> selectBannerWithCountry(BannerDto banner);
+
+    int countAll(BannerDto bannerDto);
 }

+ 7 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/service/ShopBannerService.java

@@ -1,5 +1,6 @@
 package edu.travel.tenant.service;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import edu.travel.remote.feign.mode.dto.tenant.BannerDto;
 import edu.travel.remote.feign.mode.vo.banner.BannerVo;
@@ -16,4 +17,10 @@ public interface ShopBannerService extends IService<ShopBanner>{
     List<BannerVo> getBanner(BannerDto mode);
 
     RPCBaseResponse<String> uploadBannerImage(MultipartFile file);
+
+    RPCBaseResponse<BannerVo> saveBannerCountry(BannerDto banner);
+
+    RPCBaseResponse<BannerVo> updateBannerCountry(BannerDto banner);
+
+    RPCBaseResponse<IPage<BannerVo>> getBannerAllPage(BannerDto banner);
 }

+ 257 - 13
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/service/impl/ShopBannerServiceImpl.java

@@ -1,30 +1,49 @@
 package edu.travel.tenant.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.FileTypeUtil;
+import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import edu.travel.adapter.service.country.CountryAdapter;
 import edu.travel.adapter.service.upload.UploadAdapter;
 import edu.travel.remote.feign.mode.dto.tenant.BannerDto;
 import edu.travel.remote.feign.mode.vo.banner.BannerVo;
+import edu.travel.remote.upload.dto.EduFileDTO;
+import edu.travel.remote.upload.vo.EduFileVo;
 import edu.travel.rpc.RPCBaseResponse;
+import edu.travel.tenant.constant.BaseConstant;
 import edu.travel.tenant.entity.ShopBanner;
 import edu.travel.tenant.mapper.ShopBannerMapper;
 import edu.travel.tenant.service.ShopBannerService;
+import edu.travel.tenant.utils.FIleUtil;
+import edu.travel.vo.BaseCountryServeVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static edu.travel.rpc.RPCBaseResponse.error;
+import static edu.travel.rpc.RPCBaseResponse.success;
 
 @Service
 public class ShopBannerServiceImpl extends ServiceImpl<ShopBannerMapper, ShopBanner> implements ShopBannerService {
     @Autowired
     private UploadAdapter uploadAdapter;
+    @Autowired
+    private CountryAdapter countryAdapter;
+    @Autowired
+    private ShopBannerMapper shopBannerMapper;
+    @Autowired
+    private ShopBannerService shopBannerService;
+
     @Override
     public List<BannerVo> getBanner(BannerDto mode) {
        List<BannerVo> list = new ArrayList<>();
@@ -45,17 +64,242 @@ public class ShopBannerServiceImpl extends ServiceImpl<ShopBannerMapper, ShopBan
 
     @Override
     public RPCBaseResponse<String> uploadBannerImage(MultipartFile file) {
-        //判断文件是否为空
+        // 判断上传的文件是否为空或未选择
         if (file == null || file.isEmpty()) {
-            return RPCBaseResponse.error();
-        }
-//        try{
-//            String fileType = FileTypeUtil.getType(file.getInputStream());
-//
-//        }catch (){
-//            return RPCBaseResponse.error();
-//        }
-        return null;
+            return error();
+        }
+        try {
+            // 获取文件类型(MIME 类型)
+            String fileType = FileTypeUtil.getType(file.getInputStream());
+
+            // 检查文件类型是否有效且是否在允许的横幅图片类型列表中
+            if (fileType != null && BaseConstant.getBannerImageTypes().contains(fileType.toLowerCase())) {
+                // 获取用户上传的文件名
+                String filename = file.getOriginalFilename();
+                // 检查文件名是否为空
+                if (ObjectUtil.isEmpty(filename)) {
+                    return error(); // 返回错误响应
+                }
+                // 初始化上传,获取上传 ID
+                RPCBaseResponse<String> stringRPCBaseResponse = uploadAdapter.initializeUpload();
+                // 创建文件 DTO 对象,用于存储文件相关信息
+                EduFileDTO eduFileDTO = new EduFileDTO();
+                eduFileDTO.setUploadId(stringRPCBaseResponse.getData()); // 设置上传 ID
+                eduFileDTO.setFileType(fileType); // 设置文件类型
+                eduFileDTO.setFileName(filename); // 设置文件原始名称
+                // 生成系统文件名,确保唯一性
+                eduFileDTO.setFileSysName(IdUtil.fastSimpleUUID() + DateUtil.format(new Date(), "yyyyMMddHHmmss") + "." + fileType);
+                // 计算文件的 MD5 值以便后续验证
+                eduFileDTO.setFileMd5(FIleUtil.calculateFileMd5(file.getBytes()));
+                // 调用上传适配器进行文件上传
+                RPCBaseResponse rpcBaseResponse = uploadAdapter.uploadFile(eduFileDTO, file);
+                // 返回成功响应,包含上传后的数据
+                return success(rpcBaseResponse.getData().toString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace(); // 打印异常栈信息
+            return error("file upload fail"); // 返回表示文件上传失败的错误响应
+        }
+        return error(); // 如果文件类型不支持,返回错误响应
+    }
+
+    /**
+     * 新增轮播图信息
+     * @param banner
+     * @return
+     */
+    @Override
+    public RPCBaseResponse<BannerVo> saveBannerCountry(BannerDto banner) {
+        // 验证输入参数的有效性
+        if (ObjectUtil.isEmpty(banner)) {
+            return error("Banner data cannot be null");
+        }
+
+        // 检查 isDefault 是否被输入
+        if (banner.getIsDefault() == null) {
+            return error("isDefault must be provided");
+        }
+
+        // 如果 isDefault 为 0,检查 startTime 和 endTime
+        if (banner.getIsDefault() == 0) {
+            if (ObjectUtil.isEmpty(banner.getStartTime()) || ObjectUtil.isEmpty(banner.getEndTime())) {
+                return error("startTime and endTime must be provided when isDefault is false");
+            }
+        }
+
+        try {
+            // 使用 uploadAdapter.getFormId 查找出 file_path 和 fileType
+            RPCBaseResponse<EduFileVo> fileResponse = uploadAdapter.getFormId(banner.getImageId());
+            if (fileResponse.getData() == null) {
+                return error("Failed to retrieve file information");
+            }
+            EduFileVo fileData = fileResponse.getData();
+            String filePath = fileData.getFilePath(); // 假设 EduFileDTO 有 getFilePath 方法
+            String fileType = fileData.getFileType(); // 假设 EduFileDTO 有 getFileType 方法
+
+            // 创建新的 BannerVo 对象
+            BannerVo bannerVo = new BannerVo();
+            bannerVo.setUrl(filePath); // 设置文件路径
+            bannerVo.setUrlType("image".equalsIgnoreCase(fileType) ? 0 : 1); // 假设0为图片,1为视频
+            bannerVo.setContent(banner.getContent()); // 设置名称
+            bannerVo.setCountryId(banner.getCountryId()); // 设置国家
+            bannerVo.setLinkUrl(banner.getLinkUrl()); // 设置链接地址
+            bannerVo.setMode(banner.getMode());
+
+            // 这里可以进行其他必要的字段设置,比如与 banner 相关的其他信息
+            bannerVo.setId(banner.getId()); // 如果需要设置ID,可以从DTO中获取
+
+            // 保存横幅信息到数据库,假设我们有一个方法 saveBanner
+            ShopBanner shopBanner = new ShopBanner();
+            // 将 bannerVo 转换为 shopBanner (可以使用 BeanUtil.copyProperties 或自定义转换)
+            BeanUtil.copyProperties(bannerVo, shopBanner);
+            // 设置其他必要字段
+            shopBanner.setEnable(0); // 示例:设置为启用状态
+            shopBanner.setCreateTime(new Date()); // 设置当前时间为创建时间
+
+            // 设置 isDefault、startTime 和 endTime 字段
+            shopBanner.setIsDefault(banner.getIsDefault());
+            if (banner.getIsDefault() == 0) {
+                shopBanner.setStartTime(banner.getStartTime());
+                shopBanner.setEndTime(banner.getEndTime());
+            }
+
+            // 调用 mapper 保存到数据库
+            this.save(shopBanner);
+
+            return success(bannerVo); // 返回成功的响应
+        } catch (Exception e) {
+            e.printStackTrace();
+            return error("Failed to save banner country");
+        }
+    }
+
+    /**
+     * 更新横幅信息
+     * @param banner
+     * @return
+     */
+    @Override
+    public RPCBaseResponse<BannerVo> updateBannerCountry(BannerDto banner) {
+        // 验证输入参数的有效性
+        if (ObjectUtil.isEmpty(banner) || ObjectUtil.isEmpty(banner.getId())) {
+            return error("Banner data cannot be null and id must be provided");
+        }
+
+        // 检查 isDefault 是否被输入
+        if (banner.getIsDefault() == null) {
+            return error("isDefault must be provided");
+        }
+
+        // 如果 isDefault 为 0,检查 startTime 和 endTime
+        if (banner.getIsDefault() == 0) {
+            if (ObjectUtil.isEmpty(banner.getStartTime()) || ObjectUtil.isEmpty(banner.getEndTime())) {
+                return error("startTime and endTime must be provided when isDefault is 0");
+            }
+        }
+
+        try {
+            // 使用 uploadAdapter.getFormId 查找出 file_path 和 fileType
+            RPCBaseResponse<EduFileVo> fileResponse = uploadAdapter.getFormId(banner.getImageId());
+            if (fileResponse.getData() == null) {
+                return error("Failed to retrieve file information");
+            }
+            EduFileVo fileData = fileResponse.getData();
+            String filePath = fileData.getFilePath(); // 假设 EduFileDTO 有 getFilePath 方法
+            String fileType = fileData.getFileType(); // 假设 EduFileDTO 有 getFileType 方法
+
+            // 查找现有的 ShopBanner 对象进行更新
+            ShopBanner shopBanner = this.getById(banner.getId());
+            if (shopBanner == null) {
+                return error("Banner not found");
+            }
+
+            // 更新 BannerVo 对象
+            BannerVo bannerVo = new BannerVo();
+            bannerVo.setUrl(filePath); // 设置文件路径
+            bannerVo.setUrlType("image".equalsIgnoreCase(fileType) ? 0 : 1); // 假设0为图片,1为视频
+            bannerVo.setContent(banner.getContent()); // 设置名称
+            bannerVo.setCountryId(banner.getCountryId()); // 设置国家
+            bannerVo.setLinkUrl(banner.getLinkUrl()); // 设置链接地址
+            bannerVo.setMode(banner.getMode());
+            bannerVo.setId(banner.getId()); // 保持ID一致
+
+            // 将 bannerVo 转换为 shopBanner,并设置其他字段
+            BeanUtil.copyProperties(bannerVo, shopBanner);
+            shopBanner.setEnable(0); // 示例:设置为启用状态
+            shopBanner.setUpdateTime(new Date()); // 设置当前时间为更新时间
+
+            // 设置 isDefault、startTime 和 endTime 字段
+            shopBanner.setIsDefault(banner.getIsDefault());
+            if (banner.getIsDefault() == 0) {
+                shopBanner.setStartTime(banner.getStartTime());
+                shopBanner.setEndTime(banner.getEndTime());
+            }
+
+            // 调用 mapper 更新到数据库
+            this.updateById(shopBanner);
+
+            return success(bannerVo); // 返回成功的响应
+        } catch (Exception e) {
+            e.printStackTrace();
+            return error("Failed to update banner country");
+        }
+    }
+
+    /**
+     * 获取所有信息(分页-连表)
+     * @param bannerDto
+     * @return
+     */
+    @Override
+    public RPCBaseResponse<IPage<BannerVo>> getBannerAllPage(BannerDto bannerDto) {
+        //查询轮播图数据,确保返回类型正确
+        List<BannerVo> bannerVos = shopBannerMapper.selectBannerWithCountry(bannerDto);
+        List<BannerVo> shopBannerVos = bannerVos;
+
+        int total = shopBannerMapper.countAll(bannerDto);
+
+        //提取CountryID
+        Set<String> countryIds = shopBannerVos.stream()
+                .map(BannerVo::getCountryId)
+                .collect(Collectors.toSet());
+        //查询国家信息
+        Map<String, BannerVo> countryMap = fetchCountryData(countryIds);
+
+        //遍历并设置国家信息
+        for (BannerVo shopBannerVo : shopBannerVos) {
+            if (shopBannerVo.getMap() == null) {
+                shopBannerVo.setMap(new HashMap<>());
+            }
+            if (countryMap.containsKey(shopBannerVo.getCountryId())) {
+                shopBannerVo.getMap().put("countryServe", countryMap.get(shopBannerVo.getCountryId()));
+            }
+        }
+        //封装返回结果
+        IPage<BannerVo> pageVoLink = new Page<>();
+        pageVoLink.setRecords(shopBannerVos);
+        pageVoLink.setTotal(total);
+        pageVoLink.setCurrent(bannerDto.getCurrentPage());
+        pageVoLink.setSize(bannerDto.getPageSize());
+        return new RPCBaseResponse<>(200, "SUCCESS", pageVoLink);
     }
+    //查询国家信息
+    private Map<String, BannerVo>fetchCountryData(Set<String> countryIds){
+        Map<String, BannerVo> countryMap = new HashMap<>();
+        for (String countryId : countryIds) {
+            BaseCountryServeVo countryServeVo = countryAdapter.getFormId(countryId).getData();
+            if (countryServeVo != null) {
+                BannerVo bannerVo = new BannerVo();
+                bannerVo.setCountryId(countryId);
+                bannerVo.setMap(new HashMap<>());
+                bannerVo.getMap().put("countryNameZh", countryServeVo.getCountryNameZh());
+               bannerVo.getMap().put("countryNameEn", countryServeVo.getCountryNameEn());
+               bannerVo.getMap().put("countryNameLocal", countryServeVo.getCountryNameLocal());
+               countryMap.put(countryId, bannerVo);
+            }
+        }
+        return countryMap;
+    }
+
 
 }

+ 17 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/FIleUtil.java

@@ -0,0 +1,17 @@
+package edu.travel.tenant.utils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class FIleUtil {
+
+    public static String calculateFileMd5(byte[] data) throws NoSuchAlgorithmException {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        byte[] digest = md.digest(data);
+        StringBuilder sb = new StringBuilder();
+        for (byte b : digest) {
+            sb.append(String.format("%02x", b));
+        }
+        return sb.toString();
+    }
+}

+ 13 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/IdUtils.java

@@ -0,0 +1,13 @@
+package edu.travel.tenant.utils;
+
+import cn.hutool.core.lang.Singleton;
+import cn.hutool.core.lang.Snowflake;
+import lombok.Data;
+
+@Data
+public class IdUtils {
+    public static long getSnowflakeId() {
+        Snowflake snowflake = Singleton.get(Snowflake.class, 1L, 1L, true);
+        return  snowflake.nextId();
+    }
+}

+ 19 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/ObjectUtils.java

@@ -0,0 +1,19 @@
+package edu.travel.tenant.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ObjectUtils {
+
+
+    public static <T> List<T> castList(Object obj, Class<T> clazz) {
+        List<T> result = new ArrayList<T>();
+        if (obj instanceof List<?>) {
+            for (Object o : (List<?>) obj) {
+                result.add(clazz.cast(o));
+            }
+            return result;
+        }
+        return null;
+    }
+}

+ 28 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/PageUtil.java

@@ -0,0 +1,28 @@
+package edu.travel.tenant.utils;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springframework.beans.BeanUtils;
+
+public class PageUtil{
+
+    /**
+     * Page类型entity转换
+     * @param source
+     * @param type
+     * @return
+     */
+    public static <T,E> Page<E> toPageEntity(Page<T> source, Class<E> type) {
+        Page<E> target = new Page<E>();
+        BeanUtils.copyProperties(source, target);
+        target.setRecords(BeanUtil.copyToList(source.getRecords(), type));
+        return target;
+    }
+    public static <T,E> Page<E> toPageEntity(IPage<T> source, Class<E> type) {
+        Page<E> target = new Page<E>();
+        BeanUtils.copyProperties(source, target);
+        target.setRecords(BeanUtil.copyToList(source.getRecords(), type));
+        return target;
+    }
+}

+ 36 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/RedisUtil.java

@@ -0,0 +1,36 @@
+package edu.travel.tenant.utils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class RedisUtil {
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+    /**
+     *通过KEY 获取值
+     */
+    public String getString(String key) {
+        return stringRedisTemplate.opsForValue().get(key);
+    }
+    /**
+     * 设置KEY 和 VALUE
+     */
+    public void setString(String key, String value) {
+        stringRedisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * @param key KEY
+     * @param value 值
+     * @param expire 过期时间
+     * @param timeUnit 时间单位
+     */
+    public void setString(String key, String value,long expire,TimeUnit timeUnit) {
+        stringRedisTemplate.opsForValue().set(key, value,expire, timeUnit);
+    }
+
+}

+ 26 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/StringUtil.java

@@ -0,0 +1,26 @@
+package edu.travel.tenant.utils;
+
+import cn.hutool.core.util.ObjectUtil;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@Data
+public class StringUtil {
+
+    public static List<String> toList(String str){
+        if(!ObjectUtil.isEmpty(str)){
+            return Arrays.asList(str.split(","));
+        }
+        return new ArrayList<>();
+    }
+    public static String toListString(List<String> list){
+        String result="";
+        for (String s : list) {
+            result=s+",";
+        }
+        return result;
+    }
+}

+ 16 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/TokenData.java

@@ -0,0 +1,16 @@
+package edu.travel.tenant.utils;
+
+import edu.travel.entity.EduTenantPO;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+public class TokenData {
+
+    /**
+     *  获取用户ID
+     * @return {@link String }
+     */
+    public static String getUserId(){
+        EduTenantPO principal =(EduTenantPO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        return principal.getId().toString();
+    }
+}

+ 161 - 0
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/utils/TreeUtils.java

@@ -0,0 +1,161 @@
+package edu.travel.tenant.utils;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+/**
+ * 通用树形结构构建工具类
+ *
+ * <p><strong>功能特性:</strong></p>
+ * <ul>
+ *   <li>支持任意 Java 对象(实体类/DTO/VO)</li>
+ *   <li>自动检测循环依赖(死循环)</li>
+ *   <li>零依赖(无需继承接口或父类)</li>
+ *   <li>编译期类型安全检查</li>
+ * </ul>
+ *
+ * <p><strong>使用示例:</strong></p>
+ * <pre>{@code
+ * List<Department> tree = TreeUtils.buildTree(
+ *     departments,
+ *     Department::getId,
+ *     Department::getParentId,
+ *     Department::setChildren
+ * );
+ * }</pre>
+ */
+public class TreeUtils {
+
+    /**
+     * 构建树形结构的核心方法
+     *
+     * @param nodes          待处理的节点列表(必须包含所有节点)
+     * @param idGetter       节点ID的获取方法(方法引用,如:User::getId)
+     * @param pidGetter      父节点ID的获取方法(方法引用,如:User::getParentId)
+     * @param childrenSetter 子节点列表的设置方法(如:(node, children) -> node.setChildren(children))
+     * @param <T>            节点类型(如:User.class)
+     * @param <ID>           ID 的类型(需正确实现 equals 和 hashCode)
+     * @return 树形结构的根节点列表
+     *
+     * @throws IllegalArgumentException 如果存在以下情况:
+     *                                  <ul>
+     *                                    <li>节点列表为null</li>
+     *                                    <li>存在重复ID</li>
+     *                                    <li>ID字段为null</li>
+     *                                  </ul>
+     * @throws IllegalStateException    如果检测到循环依赖
+     */
+    public static <T, ID> List<T> buildTree(List<T> nodes,
+                                            Function<T, ID> idGetter,
+                                            Function<T, ID> pidGetter,
+                                            BiConsumer<T, List<T>> childrenSetter) {
+        // 防御性拷贝,避免修改原始数据
+        List<T> copyNodes = new ArrayList<>(Optional.ofNullable(nodes).orElseGet(ArrayList::new));
+
+        //========== 数据校验 ==========//
+        validateNodes(copyNodes, idGetter);
+
+        //========== 初始化数据结构 ==========//
+        Map<ID, T> nodeMap = new HashMap<>(copyNodes.size());
+        List<T> rootNodes = new ArrayList<>();
+
+        // 构建 id -> node 映射表
+        copyNodes.forEach(node -> nodeMap.put(idGetter.apply(node), node));
+
+        //========== 构建父子关系 ==========//
+        for (T node : copyNodes) {
+            ID currentId = idGetter.apply(node);
+            ID parentId = pidGetter.apply(node);
+
+            // 判断根节点条件(父ID为空/等于自身/不存在于Map中)
+            if (isRootNode(parentId, currentId, nodeMap)) {
+                rootNodes.add(node);
+                continue;
+            }
+
+            // 获取父节点并建立关联
+            T parent = nodeMap.get(parentId);
+            if (parent != null) {
+                linkParentAndChild(parent, node, childrenSetter);
+                checkCircularDependency(parent, currentId, idGetter, pidGetter, nodeMap);
+            }
+        }
+
+        return rootNodes;
+    }
+
+    //========== 私有方法 ==========//
+
+    /**
+     * 数据校验
+     */
+    private static <T, ID> void validateNodes(List<T> nodes, Function<T, ID> idGetter) {
+        if (nodes == null) {
+            throw new IllegalArgumentException("节点列表不能为null");
+        }
+
+        Set<ID> idSet = new HashSet<>();
+        for (T node : nodes) {
+            ID id = idGetter.apply(node);
+            if (id == null) {
+                throw new IllegalArgumentException("节点存在空ID: " + node);
+            }
+            if (idSet.contains(id)) {
+                throw new IllegalArgumentException("存在重复ID: " + id);
+            }
+            idSet.add(id);
+        }
+    }
+
+    /**
+     * 判断是否为根节点
+     */
+    private static <ID> boolean isRootNode(ID parentId, ID currentId, Map<ID, ?> nodeMap) {
+        return parentId == null
+                || parentId.equals(currentId)
+                || !nodeMap.containsKey(parentId);
+    }
+
+    /**
+     * 建立父子关联关系
+     */
+    private static <T> void linkParentAndChild(T parent,
+                                               T child,
+                                               BiConsumer<T, List<T>> childrenSetter) {
+        List<T> children = new ArrayList<>();
+        childrenSetter.accept(parent, children);
+        children.add(child);
+    }
+
+    /**
+     * 循环依赖检测(核心安全机制)
+     */
+    private static <T, ID> void checkCircularDependency(T parent,
+                                                        ID childId,
+                                                        Function<T, ID> idGetter,
+                                                        Function<T, ID> pidGetter,
+                                                        Map<ID, T> nodeMap) {
+        Set<ID> visited = new HashSet<>();
+        T current = parent;
+
+        while (current != null) {
+            ID currentParentId = pidGetter.apply(current);
+            if (currentParentId == null) break;
+
+            // 发现直接循环(A→B→A)
+            if (currentParentId.equals(childId)) {
+                throw new IllegalStateException("检测到循环依赖:节点 "
+                        + idGetter.apply(current) + " → " + childId);
+            }
+
+            // 发现间接循环(A→B→C→A)
+            if (visited.contains(currentParentId)) {
+                throw new IllegalStateException("检测到循环依赖路径:" + visited);
+            }
+
+            visited.add(currentParentId);
+            current = nodeMap.get(currentParentId);
+        }
+    }
+}

+ 51 - 12
edu-travel-service/edu-travel-service-tenement/src/main/java/edu/travel/tenant/web/ShopBannerController.java

@@ -1,5 +1,6 @@
 package edu.travel.tenant.web;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import edu.travel.remote.feign.mode.dto.tenant.BannerDto;
 import edu.travel.remote.feign.mode.romote.ShopBannerRemoteController;
 import edu.travel.remote.feign.mode.vo.banner.BannerVo;
@@ -10,11 +11,7 @@ import edu.travel.tenant.service.ShopBannerService;
 import edu.travel.web.BaseController;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
@@ -45,17 +42,42 @@ public class ShopBannerController extends BaseController<ShopBanner> implements
     public RPCBaseResponse<List<BannerVo>> getBanner(BannerDto banner){
         return success(shopBannerService.getBanner(banner));
     }
-
+    /**
+     * 获取轮播图(连表-分页)
+     */
+    @GetMapping("/getBannerAllPage")
+    public RPCBaseResponse<IPage<BannerVo>> getBannerAllPage(BannerDto banner){
+        return shopBannerService.getBannerAllPage(banner);
+    }
+    /**
+     * 新增轮播图(通过上传返回id自动填充)
+     */
+    @PostMapping("/saveBannerCountry")
+    public RPCBaseResponse<BannerVo> saveBannerCountry (BannerDto banner){
+        return shopBannerService.saveBannerCountry(banner);
+    }
+    /**
+     * 更新轮播图信息(通过上传返回id自动填充)
+     */
+    @PostMapping("/updateBannerCountry")
+    public RPCBaseResponse<BannerVo> updateBannerCountry(BannerDto banner){
+        return shopBannerService.updateBannerCountry(banner);
+    }
     /**
      * 上传banner图片
      * @param
      * @return
      */
-    @GetMapping("/uploadBannerImage")
-    public ResponseEntity<RPCBaseResponse<String>> uploadBannerImage(@RequestBody MultipartFile file){
-        return ResponseEntity.ok(shopBannerService.uploadBannerImage(file));
+    @PostMapping("/uploadBannerImage")
+    public RPCBaseResponse<String> uploadBannerImage(MultipartFile file){
+        return shopBannerService.uploadBannerImage(file);
     }
 
+    /**
+     * 根据id获取banner
+     * @param id
+     * @return
+     */
     @Override
     @GetMapping("/getFormId")
     public RPCBaseResponse<BannerVo> getFormId(String id) {
@@ -65,8 +87,13 @@ public class ShopBannerController extends BaseController<ShopBanner> implements
         return shopBannerVoRPCBaseResponse;
     }
 
+    /**
+     * 更新banner
+     * @param entity
+     * @return
+     */
     @Override
-    @GetMapping("/updateFormTarget")
+    @GetMapping("/updateTargetFormId")
     public RPCBaseResponse<BannerVo> updateTargetFormId(@RequestBody BannerDto entity) {
         ShopBanner shopBanner = new ShopBanner();
         BeanUtils.copyProperties(entity, shopBanner);
@@ -76,6 +103,11 @@ public class ShopBannerController extends BaseController<ShopBanner> implements
         return shopBannerVoRPCBaseResponse;
     }
 
+    /**
+     * 新增banner
+     * @param entity
+     * @return
+     */
     @Override
     @GetMapping("/saveFormTarget")
     public RPCBaseResponse<BannerVo> saveFormTarget(BannerDto entity) {
@@ -87,7 +119,11 @@ public class ShopBannerController extends BaseController<ShopBanner> implements
         return shopBannerVoRPCBaseResponse;
     }
 
-
+    /**
+     * 删除轮播图
+     * @param ids
+     * @return
+     */
     @Override
     @GetMapping("/deleteFormTarget")
     public RPCBaseResponse<BannerVo> deleteTargetFormId(List<String> ids) {
@@ -97,6 +133,10 @@ public class ShopBannerController extends BaseController<ShopBanner> implements
         return shopBannerVoRPCBaseResponse;
     }
 
+    /**
+     * 获取所有轮播图
+     * @return
+     */
     @Override
     @GetMapping("/getAllForm")
     public RPCBaseResponse<List<BannerVo>> getAllForm() {
@@ -105,5 +145,4 @@ public class ShopBannerController extends BaseController<ShopBanner> implements
         BeanUtils.copyProperties(countryRPCBaseResponse, baseCountryVoRPCBaseResponse);
         return baseCountryVoRPCBaseResponse;
     }
-
 }

+ 87 - 28
edu-travel-service/edu-travel-service-tenement/src/main/resources/mapper/ShopBannerMapper.xml

@@ -1,32 +1,91 @@
 <?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="edu.travel.tenant.mapper.ShopBannerMapper">
-  <resultMap id="BaseResultMap" type="edu.travel.tenant.entity.ShopBanner">
-    <!--@mbg.generated-->
-    <!--@Table shop_banner-->
-    <id column="id" jdbcType="BIGINT" property="id" />
-    <result column="url" jdbcType="VARCHAR" property="url" />
-    <result column="url_type" jdbcType="INTEGER" property="urlType" />
-    <result column="content" jdbcType="VARCHAR" property="content" />
-    <result column="link_url" jdbcType="VARCHAR" property="linkUrl" />
-    <result column="sort_order" jdbcType="INTEGER" property="sortOrder" />
-    <result column="start_time" jdbcType="TIMESTAMP" property="startTime" />
-    <result column="end_time" jdbcType="TIMESTAMP" property="endTime" />
-    <result column="country_id" jdbcType="BIGINT" property="countryId" />
-    <result column="is_default" jdbcType="INTEGER" property="isDefault" />
-    <result column="mode" jdbcType="INTEGER" property="mode" />
-    <result column="enable" jdbcType="INTEGER" property="enable" />
-    <result column="project" jdbcType="VARCHAR" property="project" />
-    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
-    <result column="create_user_id" jdbcType="VARCHAR" property="createUserId" />
-    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
-    <result column="update_user_id" jdbcType="VARCHAR" property="updateUserId" />
-    <result column="delete_flag" jdbcType="INTEGER" property="deleteFlag" />
-  </resultMap>
-  <sql id="Base_Column_List">
-    <!--@mbg.generated-->
-    id, url, url_type, content, link_url, sort_order, start_time, end_time, `is_default`, `country_id`
-    `mode`, `enable`, project, create_time, create_user_id, update_time, update_user_id, 
-    delete_flag
-  </sql>
+    <resultMap id="BaseResultMap" type="edu.travel.tenant.entity.ShopBanner">
+        <!--@mbg.generated-->
+        <!--@Table shop_banner-->
+        <id column="id" jdbcType="BIGINT" property="id"/>
+        <result column="url" jdbcType="VARCHAR" property="url"/>
+        <result column="url_type" jdbcType="INTEGER" property="urlType"/>
+        <result column="content" jdbcType="VARCHAR" property="content"/>
+        <result column="link_url" jdbcType="VARCHAR" property="linkUrl"/>
+        <result column="sort_order" jdbcType="INTEGER" property="sortOrder"/>
+        <result column="start_time" jdbcType="TIMESTAMP" property="startTime"/>
+        <result column="end_time" jdbcType="TIMESTAMP" property="endTime"/>
+        <result column="country_id" jdbcType="BIGINT" property="countryId"/>
+        <result column="is_default" jdbcType="INTEGER" property="isDefault"/>
+        <result column="mode" jdbcType="INTEGER" property="mode"/>
+        <result column="enable" jdbcType="INTEGER" property="enable"/>
+        <result column="project" jdbcType="VARCHAR" property="project"/>
+        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
+        <result column="create_user_id" jdbcType="VARCHAR" property="createUserId"/>
+        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
+        <result column="update_user_id" jdbcType="VARCHAR" property="updateUserId"/>
+        <result column="delete_flag" jdbcType="INTEGER" property="deleteFlag"/>
+    </resultMap>
+    <sql id="Base_Column_List">
+        <!--@mbg.generated-->
+        id, url, url_type, content, link_url, sort_order, start_time, end_time, `is_default`, `country_id`
+        `mode`, `enable`, project, create_time, create_user_id, update_time, update_user_id,
+        delete_flag
+    </sql>
+    <select id="selectBannerWithCountry" resultType="edu.travel.remote.feign.mode.vo.banner.BannerVo">
+        SELECT
+        sb.id,
+        sb.url,
+        sb.url_type,
+        sb.content,
+        sb.link_url,
+        sb.mode,
+        sb.country_id,
+        sb.start_time,
+        sb.end_time,
+        sb.sort_order,
+        sb.is_default,
+        sb.enable,
+        sb.project
+        FROM
+        shop_banner sb
+        <where>
+            <if test="url != null and url != ''">
+                AND sb.url LIKE CONCAT('%', #{url}, '%')
+            </if>
+            <if test="urlType != null">
+                AND sb.url_type = #{urlType}
+            </if>
+            <if test="content != null and content != ''">
+                AND sb.content LIKE CONCAT('%', #{content}, '%')
+            </if>
+            <if test="countryId != null and countryId != ''">
+                AND sb.country_id = #{countryId}
+            </if>
+            <if test="isDefault != null">
+                AND sb.is_default = #{isDefault}
+            </if>
+            <if test="linkUrl != null and linkUrl != ''">
+                AND sb.link_url LIKE CONCAT('%', #{linkUrl}, '%')
+            </if>
+            <if test="mode != null and mode !=''">
+                AND sb.mode = #{mode}
+            </if>
+            <if test="enable != null and enable !=''">
+                AND sb.enable = #{enable}
+            </if>
+        </where>
+    </select>
+    <select id="countAll" resultType="int">
+        SELECT COUNT(*)
+        FROM shop_banner sb
+        <where>
+            <if test="url != null and url != ''">
+                AND sb.url LIKE CONCAT('%', #{url}, '%')
+            </if>
+            <if test="urlType != null">
+                AND sb.url_type = #{urlType}
+            </if>
+            <if test="content != null and content != ''">
+                AND sb.content LIKE CONCAT('%', #{content}, '%')
+            </if>
+        </where>
+    </select>
 </mapper>

+ 4 - 15
edu-travel-adapter/edu-travel-adapter-currency/pom.xml → edu-travel-service/edu-travel-service-tomcat/pom.xml

@@ -3,15 +3,14 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>edu.travel</groupId>
-        <artifactId>edu-travel-adapter</artifactId>
+        <artifactId>edu-travel-service</artifactId>
         <version>1.0-SNAPSHOT</version>
     </parent>
 
-
-    <artifactId>edu-travel-adapter-currency</artifactId>
+    <artifactId>edu-travel-service-tomcat</artifactId>
     <packaging>jar</packaging>
 
-    <name>edu-travel-adapter-currency</name>
+    <name>edu-travel-service-tomcat</name>
     <url>http://maven.apache.org</url>
 
     <properties>
@@ -22,20 +21,10 @@
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-aop</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>edu.travel</groupId>
-            <artifactId>edu-travel-remote-country</artifactId>
-            <version>1.0-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>edu.travel</groupId>
-            <artifactId>edu-travel-common-adapter</artifactId>
+            <artifactId>edu-travel-common-core</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
     </dependencies>

+ 17 - 0
edu-travel-service/edu-travel-service-tomcat/src/main/java/edu/travel/App.java

@@ -0,0 +1,17 @@
+package edu.travel;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Hello world!
+ *
+ */
+@SpringBootApplication
+public class App 
+{
+    public static void main( String[] args )
+    {
+       SpringApplication.run(App.class, args);
+    }
+}

+ 75 - 1
edu-travel-service/edu-travel-service-upload/src/main/java/edu/travel/upload/web/UploadController.java

@@ -7,6 +7,7 @@ import com.obs.services.model.*;
 import edu.travel.remote.upload.UploadRemoteController;
 import edu.travel.remote.upload.dto.EduFileBlobDTO;
 import edu.travel.remote.upload.dto.EduFileDTO;
+import edu.travel.remote.upload.vo.EduFileVo;
 import edu.travel.rpc.RPCBaseResponse;
 import edu.travel.upload.config.MD5Calculator;
 import edu.travel.upload.entity.EduFile;
@@ -14,6 +15,7 @@ import edu.travel.upload.entity.EduFileBlob;
 import edu.travel.upload.obs.property.ObsProperties;
 import edu.travel.upload.service.EduFileBlobService;
 import edu.travel.upload.service.EduFileService;
+import edu.travel.web.BaseController;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,7 +37,7 @@ import java.util.concurrent.ThreadPoolExecutor;
 
 @RestController
 @RequestMapping("/upload")
-public class UploadController implements UploadRemoteController {
+public class UploadController extends BaseController<EduFile> implements UploadRemoteController {
     @Autowired
     private EduFileService eduFileService;
     @Autowired
@@ -150,4 +152,76 @@ public class UploadController implements UploadRemoteController {
         }
         return RPCBaseResponse.error();
     }
+
+    /**
+     * 通过id上传文件
+     * @param id
+     * @return
+     */
+    @Override
+    @GetMapping("/getFormId")
+    public RPCBaseResponse<EduFileVo> getFormId(@RequestParam("id")String id) {
+        RPCBaseResponse<EduFile> eduFileRPCBaseResponse = super.getId(id);
+        RPCBaseResponse<EduFileVo> eduFileVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(eduFileRPCBaseResponse, eduFileVoRPCBaseResponse);
+        return eduFileVoRPCBaseResponse;
+    }
+
+    /**
+     * 更新文件
+     * @param entity
+     * @return
+     */
+    @Override
+    @GetMapping("/updateTargetFormId")
+    public RPCBaseResponse<EduFileVo> updateTargetFormId(@RequestBody EduFileDTO entity) {
+        EduFile eduFile = new EduFile();
+        BeanUtils.copyProperties(entity, eduFile);
+        RPCBaseResponse<EduFile> eduFileRPCBaseResponse = super.updateTargetById(eduFile);
+        RPCBaseResponse<EduFileVo> eduFileVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(eduFileRPCBaseResponse, eduFileVoRPCBaseResponse);
+        return eduFileVoRPCBaseResponse;
+    }
+
+    /**
+     * 新增文件
+     * @param entity
+     * @return
+     */
+    @Override
+    @GetMapping("/saveFormTarget")
+    public RPCBaseResponse<EduFileVo> saveFormTarget(@RequestBody EduFileDTO entity) {
+        EduFile eduFile = new EduFile();
+        BeanUtils.copyProperties(entity, eduFile);
+        RPCBaseResponse<EduFile> eduFileRPCBaseResponse = super.saveTarget(eduFile);
+        RPCBaseResponse<EduFileVo> eduFileVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(eduFileRPCBaseResponse, eduFileVoRPCBaseResponse);
+        return eduFileVoRPCBaseResponse;
+    }
+
+    /**
+     * 删除文件
+     * @param ids
+     * @return
+     */
+    @Override
+    @GetMapping("/deleteTargetFormId")
+    public RPCBaseResponse<EduFileVo> deleteTargetFormId(@RequestBody List<String> ids) {
+        RPCBaseResponse<EduFile> eduFileRPCBaseResponse = super.deleteTargetById(ids);
+        RPCBaseResponse<EduFileVo> eduFileVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(eduFileRPCBaseResponse, eduFileVoRPCBaseResponse);
+        return eduFileVoRPCBaseResponse;
+    }
+
+    /**
+     * 获取全部文件
+     * @return
+     */
+    @Override
+    public RPCBaseResponse<List<EduFileVo>> getAllForm() {
+        RPCBaseResponse<List<EduFile>> eduFileRPCBaseResponse = super.listAll();
+        RPCBaseResponse<List<EduFileVo>> eduFileVoRPCBaseResponse = new RPCBaseResponse<>();
+        BeanUtils.copyProperties(eduFileRPCBaseResponse, eduFileVoRPCBaseResponse);
+        return eduFileVoRPCBaseResponse;
+    }
 }

+ 1 - 1
edu-travel-service/edu-travel-service-upload/src/main/resources/bootstrap.yml

@@ -1,5 +1,5 @@
 server:
-  port: 10005
+  port: 10010
 spring:
   application:
     name: upload-@env@

+ 135 - 0
edu-travel-service/edu-travel-service-ws/pom.xml

@@ -0,0 +1,135 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>edu.travel</groupId>
+        <artifactId>edu-travel-service</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>edu-travel-service-ws</artifactId>
+    <packaging>jar</packaging>
+
+    <name>edu-travel-service-ws</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-zipkin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-oauth2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-transport-simple-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-annotation-aspectj</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-datasource-nacos</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-web-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-common-util</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-model-base</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-common-cache</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-common-datasource</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>edu.travel</groupId>
+            <artifactId>edu-travel-common-constant</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
+
+
+
+    </dependencies>
+</project>

+ 17 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/WsApplication.java

@@ -0,0 +1,17 @@
+package edu.travel;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@MapperScan
+public class WsApplication
+{
+    public static void main( String[] args )
+    {
+        System.out.println( "Hello World!" );
+    }
+}

+ 32 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/MyAccessDeniedHandler.java

@@ -0,0 +1,32 @@
+package edu.travel.ws.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MyAccessDeniedHandler  implements AccessDeniedHandler {
+    @Override
+    public void handle(HttpServletRequest req, HttpServletResponse resp, AccessDeniedException e) throws IOException, ServletException {
+        resp.setContentType("application/json;charset=UTF-8");
+        Map map = new HashMap();
+        map.put("code", "450");
+        map.put("message", e.getMessage());
+        map.put("path", req.getServletPath());
+        map.put("timestamp", String.valueOf(System.currentTimeMillis()));
+        resp.setContentType("application/json");
+        resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.writeValue(resp.getOutputStream(), map);
+        } catch (Exception ex) {
+            throw new ServletException();
+        }
+    }
+}

+ 31 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/MyAuthenticationEntryPoint.java

@@ -0,0 +1,31 @@
+package edu.travel.ws.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
+    @Override
+    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
+        Map map = new HashMap();
+        map.put("code", "451");
+        map.put("message", "无权限访问");
+        map.put("path", httpServletRequest.getServletPath());
+        map.put("timestamp", String.valueOf(System.currentTimeMillis()));
+        httpServletResponse.setContentType("application/json");
+        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.writeValue(httpServletResponse.getOutputStream(), map);
+        } catch (Exception ex) {
+            throw new ServletException();
+        }
+    }
+}

+ 39 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/ResourceServerConfig.java

@@ -0,0 +1,39 @@
+package edu.travel.ws.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+
+@Configuration
+@EnableResourceServer
+public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
+    private static  final String RESOURCE_ID = "admin";
+
+    @Autowired
+    private TokenStore tokenStore;
+
+    @Override
+    public void configure(ResourceServerSecurityConfigurer resources) {
+        resources.resourceId(RESOURCE_ID)//资源 id
+                .tokenStore(tokenStore)
+                .authenticationEntryPoint(new MyAuthenticationEntryPoint())
+                .accessDeniedHandler(new MyAccessDeniedHandler())
+//                .tokenServices(tokenService())//验证令牌的服务
+                .stateless(true);
+    }
+
+    @Override
+    public void configure(HttpSecurity http) throws Exception {
+
+        http
+                .authorizeRequests()
+                .antMatchers("/**").permitAll()
+                .and().csrf().disable()
+                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+    }
+}

+ 48 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/TokenConfig.java

@@ -0,0 +1,48 @@
+package edu.travel.ws.config;
+
+import edu.travel.RSAUtill;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
+
+import java.security.PrivateKey;
+
+@Configuration
+public class TokenConfig {
+    @Value("${OAUTH_KEY}")
+    private String key;
+    @Value("${PRIVATE_KEY}")
+    private String privateKey;
+    @Bean
+    public PasswordEncoder passwordEncoder(){
+        return NoOpPasswordEncoder.getInstance();
+    }
+    @Bean
+    public TokenStore tokenStore() {
+        //JWT令牌存储方案
+        return new JwtTokenStore(accessTokenConverter());
+    }
+
+    @Bean
+    public JwtAccessTokenConverter accessTokenConverter() {
+        try {
+            PrivateKey privateKeyFromString = RSAUtill.getPrivateKeyFromString(privateKey);
+            String decrypt = RSAUtill.decrypt(key, privateKeyFromString);
+            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
+            //对称秘钥,资源服务器使用该秘钥来验证
+//        converter.setKeyPair(keyPair());
+            converter.setSigningKey(decrypt);
+            return converter;
+
+        }catch (Exception e){
+            e.printStackTrace();
+            return null;
+        }
+
+    }
+}

+ 21 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/WebSecurityConfig.java

@@ -0,0 +1,21 @@
+package edu.travel.ws.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+    //安全拦截机制(最重要)
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.csrf().disable()
+                .authorizeRequests()
+                .anyRequest().authenticated()
+        ;
+
+
+    }
+}

+ 23 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/WebServerFactoryConfigs.java

@@ -0,0 +1,23 @@
+package edu.travel.ws.config;
+
+import org.apache.catalina.connector.Connector;
+import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class WebServerFactoryConfigs {
+    @Bean
+    public ConfigurableServletWebServerFactory webServerFactory() {
+        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
+        factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
+            @Override
+            public void customize(Connector connector) {
+                connector.setProperty("relaxedQueryChars", "|{}[]");
+            }
+        });
+        return factory;
+    }
+}

+ 25 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/config/WebSocketAutoConfig.java

@@ -0,0 +1,25 @@
+package edu.travel.ws.config;
+
+import edu.travel.ws.handle.TravelMessageHandler;
+import edu.travel.ws.interceptor.SystemMessageInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+@Configuration
+@EnableWebSocket
+public class WebSocketAutoConfig implements WebSocketConfigurer {
+
+    @Autowired
+    private ApplicationContext applicationContext;
+
+    @Override
+    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+        registry.addHandler(new TravelMessageHandler(applicationContext), "/system/message")
+                .addInterceptors(new SystemMessageInterceptor(applicationContext))
+                .setAllowedOrigins("*");
+    }
+}

+ 61 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/filter/TokenAuthenticationFilter.java

@@ -0,0 +1,61 @@
+package edu.travel.ws.filter;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import edu.travel.EncryptUtil;
+import edu.travel.entity.EduTenantPO;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class TokenAuthenticationFilter extends OncePerRequestFilter {
+    @Autowired
+    private RedisTemplate redisTemplate;
+    @Override
+    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
+        String token = httpServletRequest.getHeader("token");
+//token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYWRtaW4iXSwiZXhwIjoxNzQwMjAyNTcyLCJ1c2VyX25hbWUiOiIxNTk5ODk1NzA3NCIsImp0aSI6IjM0M2JjNGUzLTk5ZjMtNGE4Zi1iMmIxLTI1ZjRkMzBmNmJmYyIsImNsaWVudF9pZCI6ImFkbWluIiwic2NvcGUiOlsic2VydmVyIl19.MYoFq8gg832DQMX-wVMLN0JlIaWeuQZvl1z1NUNFspQ";
+        if (StringUtils.isNotBlank(token)){
+            String json = EncryptUtil.decodeUTF8StringBase64(token);
+            //将token转成json对象
+            JSONObject jsonObject = JSON.parseObject(json);
+            //用户身份信息
+            String username  = jsonObject.getString("principal");
+            Object object = redisTemplate.opsForValue().get(username + "_info");
+            if (object == null){
+                JSONObject resultObject = new JSONObject();
+                resultObject.put("code",401);
+                resultObject.put("msg","not found user");
+                resultObject.put("data",null);
+                httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+                httpServletResponse.setContentType("application/json;charset=utf-8");
+                httpServletResponse.getWriter().write(resultObject.toJSONString());
+                return;
+            }
+            EduTenantPO eduTenant = JSON.parseObject(object.toString(), EduTenantPO.class);
+            //用户权限
+            JSONArray authoritiesArray = jsonObject.getJSONArray("authorities");
+            String[] authorities = authoritiesArray.toArray(new String[authoritiesArray.size()]);
+            //将用户信息和权限填充 到用户身份token对象中
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(eduTenant, null, AuthorityUtils.createAuthorityList(authorities));
+            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
+            //将authenticationToken填充到安全上下文
+            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+        }
+        filterChain.doFilter(httpServletRequest,httpServletResponse);
+    }
+}

+ 12 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/handle/TravelMessageHandler.java

@@ -0,0 +1,12 @@
+package edu.travel.ws.handle;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.socket.handler.AbstractWebSocketHandler;
+
+public class TravelMessageHandler extends AbstractWebSocketHandler {
+    private ApplicationContext applicationContext;
+
+    public TravelMessageHandler(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+}

+ 62 - 0
edu-travel-service/edu-travel-service-ws/src/main/java/edu/travel/ws/interceptor/SystemMessageInterceptor.java

@@ -0,0 +1,62 @@
+package edu.travel.ws.interceptor;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import edu.travel.EncryptUtil;
+import edu.travel.entity.EduTenantPO;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.ApplicationContext;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import java.util.Map;
+
+public class SystemMessageInterceptor implements HandshakeInterceptor {
+    private ApplicationContext applicationContext;
+    public SystemMessageInterceptor(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+    @Override
+    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
+        RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
+        if (request instanceof ServletServerHttpRequest){
+            ServletServerHttpRequest httpRequest = (ServletServerHttpRequest) request;
+            String token = httpRequest.getServletRequest().getParameter("token");
+            if (StringUtils.isBlank(token)){
+                return false;
+            }
+            String json = EncryptUtil.decodeUTF8StringBase64(token);
+            //将token转成json对象
+            JSONObject jsonObject = JSON.parseObject(json);
+            //用户身份信息
+            String username  = jsonObject.getString("principal");
+            Object object = redisTemplate.opsForValue().get(username + "_info");
+            EduTenantPO eduTenant = JSON.parseObject(object.toString(), EduTenantPO.class);
+            //用户权限
+            JSONArray authoritiesArray = jsonObject.getJSONArray("authorities");
+            String[] authorities = authoritiesArray.toArray(new String[authoritiesArray.size()]);
+            //将用户信息和权限填充 到用户身份token对象中
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(eduTenant, null, AuthorityUtils.createAuthorityList(authorities));
+            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest.getServletRequest()));
+            //将authenticationToken填充到安全上下文
+            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+            attributes.put("user", eduTenant);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
+
+    }
+}

+ 2 - 0
edu-travel-service/pom.xml

@@ -19,6 +19,8 @@
         <module>edu-travel-service-upload</module>
         <module>edu-travel-service-debezium</module>
         <module>edu-travel-service-education</module>
+        <module>edu-travel-service-tomcat</module>
+        <module>edu-travel-service-ws</module>
     </modules>
 
     <name>edu-travel-service</name>

+ 6 - 1
pom.xml

@@ -15,7 +15,6 @@
     <module>edu-travel-model</module>
       <module>edu-travel-api</module>
       <module>edu-travel-adapter</module>
-    <module>edu-travel-adapter/edu-travel-adapter-warehouse</module>
   </modules>
   <parent>
     <groupId>org.springframework.boot</groupId>
@@ -46,10 +45,16 @@
     <debezium.version>1.9.4.final</debezium.version>
     <beanutils.version>1.9.4</beanutils.version>
     <modelmapper.version>3.2.0</modelmapper.version>
+    <excel.version>3.3.2</excel.version>
   </properties>
   <dependencyManagement>
 
     <dependencies>
+      <dependency>
+        <groupId>com.alibaba</groupId>
+        <artifactId>easyexcel</artifactId>
+        <version>${excel.version}</version> <!-- 检查最新版本 -->
+      </dependency>
       <!-- 对象转换工具-->
       <dependency>
         <groupId>org.modelmapper</groupId>